使用飞碟将 Svg 集成到 pdf 中

Svg integration in pdf using flying saucer

我遇到了将html转成pdf的情况,还好我可以通过飞碟api实现。但是我的 HTML 在转换时包含 svg 标签,我无法获得 pdf 格式的 svg。可以使用 来实现 和 Tutorial.

replacedElementFactory是什么意思?

ChainingReplacedElementFactory chainingReplacedElementFactory 
        = new ChainingReplacedElementFactory();
chainingReplacedElementFactory.addReplacedElementFactory(replacedElementFactory);
chainingReplacedElementFactory.addReplacedElementFactory(new SVGReplacedElementFactory());
renderer.getSharedContext().setReplacedElementFactory(chainingReplacedElementFactory);

这只是教程中的一个错误,replacedElementFactory这一行不需要。

这是我的工作示例。

Java:

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextRenderer;

public class PdfSvg {
    public static void main(String[] args) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document inputDoc =  builder.parse("svg.html");

        ByteArrayOutputStream output = new ByteArrayOutputStream();

        ITextRenderer renderer = new ITextRenderer();

        ChainingReplacedElementFactory chainingReplacedElementFactory = new ChainingReplacedElementFactory();
        chainingReplacedElementFactory.addReplacedElementFactory(new SVGReplacedElementFactory());
        renderer.getSharedContext().setReplacedElementFactory(chainingReplacedElementFactory);

        renderer.setDocument(inputDoc, "");;
        renderer.layout();
        renderer.createPDF(output);

        OutputStream fos = new FileOutputStream("svg.pdf");
        output.writeTo(fos);
    }
}

HTML:

<html>
<head>
<style type="text/css">
    svg {display: block;width:100mm;height:100mm}
</style>
</head>
<body>
    <div>
        <svg xmlns="http://www.w3.org/2000/svg">
            <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3"
                fill="red" />
        </svg>
    </div>
</body>
</html>

ChainingReplacedElementFactorySVGReplacedElementSVGReplacedElementFactory来自tutorial

如果您想要一个页内解决方案,这里有一个替代方法,使用@cloudformatter,它是一种远程格式化服务。我将他们的 Javascript 添加到您的 fiddle 以及一些文本和您的 Highchart 图表。

http://jsfiddle.net/yk0Lxzg0/1/

var click="return xepOnline.Formatter.Format('printme', {render:'download'})";
jQuery('#buttons').append('<button onclick="'+ click +'">PDF</button>');

上面的代码放在 fiddle 中会将 div with 'id' printme 格式化为 PDF 以供下载。 div 包括您的图表和一些文本。

http://www.cloudformatter.com/CSS2Pdf.APIDoc.Usage 显示使用说明,并有更多 SVG 图表示例,这些图表可以单独格式化为 PDF,也可以作为与文本、表格等相结合的页面的一部分。

@Rajesh 希望您已经找到解决问题的方法。如果没有(或任何人在使用飞碟、蜡染和 svg 标签时遇到问题),那么您可能需要考虑这个- 从 <g> 标签中删除所有 clip-path="url(#highcharts-xxxxxxx-xx)" 对我有用。

我的代码是指缺少的代码部分 "SVGReplacedElementFactory"。

我是这样使用的:

renderer
.getSharedContext()
.setReplacedElementFactory( new B64ImgReplacedElementFactory() );
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.codec.Base64;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.JPEGTranscoder;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.w3c.dom.Element;
import org.xhtmlrenderer.extend.FSImage;
import org.xhtmlrenderer.extend.ReplacedElement;
import org.xhtmlrenderer.extend.ReplacedElementFactory;
import org.xhtmlrenderer.extend.UserAgentCallback;
import org.xhtmlrenderer.layout.LayoutContext;
import org.xhtmlrenderer.pdf.ITextFSImage;
import org.xhtmlrenderer.pdf.ITextImageElement;
import org.xhtmlrenderer.render.BlockBox;
import org.xhtmlrenderer.simple.extend.FormSubmissionListener;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class B64ImgReplacedElementFactory implements ReplacedElementFactory
{

    public ReplacedElement createReplacedElement(LayoutContext c, BlockBox box, UserAgentCallback uac, int cssWidth, int cssHeight)
    {
        Element e = box.getElement();
        if(e == null)
        {
            return null;
        }
        String nodeName = e.getNodeName();
        if(nodeName.equals("img"))
        {
            String attribute = e.getAttribute("src");
            FSImage fsImage;
            try
            {
                fsImage = buildImage(attribute, uac);
            }
            catch(BadElementException e1)
            {
                fsImage = null;
            }
            catch(IOException e1)
            {
                fsImage = null;
            }
            if(fsImage != null)
            {
                if(cssWidth != -1 || cssHeight != -1)
                {
                    fsImage.scale(cssWidth, cssHeight);
                }
                return new ITextImageElement(fsImage);
            }
        }

        return null;
    }

    protected FSImage buildImage(String srcAttr, UserAgentCallback uac) throws IOException, BadElementException
    {
        if(srcAttr.startsWith("data:image/"))
        {
            // BASE64Decoder decoder = new BASE64Decoder();
            // byte[] decodedBytes = decoder.decodeBuffer(b64encoded);
            // byte[] decodedBytes = B64Decoder.decode(b64encoded);
            byte[] decodedBytes = Base64.decode(srcAttr.substring(srcAttr.indexOf("base64,") + "base64,".length(), srcAttr.length()));
            return new ITextFSImage(Image.getInstance(decodedBytes));
        }
        FSImage fsImage = uac.getImageResource(srcAttr).getImage();
        if(fsImage == null)
        {
            return convertToPNG(srcAttr);
        }
        return null;
    }

    private FSImage convertToPNG(String srcAttr) throws IOException, BadElementException
    {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        PNGTranscoder t = new PNGTranscoder();
//        t.addTranscodingHint(JPEGTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER, (25.4f / 72f));
        t.addTranscodingHint(JPEGTranscoder.KEY_WIDTH, 4000.0F);
        t.addTranscodingHint(JPEGTranscoder.KEY_HEIGHT, 4000.0F);
        try
        {
            t.transcode(
                    new TranscoderInput(srcAttr),
                    new TranscoderOutput(byteArrayOutputStream)
            );
        }
        catch(TranscoderException e)
        {
            e.printStackTrace();
        }
        byteArrayOutputStream.flush();
        byteArrayOutputStream.close();
        return new ITextFSImage(Image.getInstance(byteArrayOutputStream.toByteArray()));
    }

    public void remove(Element e)
    {
    }

    @Override
    public void setFormSubmissionListener(FormSubmissionListener formSubmissionListener)
    {

    }

    public void reset()
    {
    }
}