【问题标题】:Convert SVG to PDF with iText, SVG is not fully displayed in PDF使用 iText 将 SVG 转换为 PDF,SVG 未在 PDF 中完全显示
【发布时间】:2021-03-14 10:26:35
【问题描述】:

我正在为 PDF 页面添加 SVG 图像。

首先,我尝试了 SvgConverter.createPDF 来检查 iText 是否适用于 SVG。

有些 svg 很好。 不幸的是,以下 SVG(使用百分比位置)在 PDF 中未正确显示/定位。

我的转换代码

    String svgImage = resourceFile("svg/circle-sRGB-rgb.svg");
    String destination = targetFile("svg-itext_SVG2PDF.pdf");

    try(InputStream svgStream = new FileInputStream(new File(svgImage))) {
        try(OutputStream pdfStream = new FileOutputStream(new File(destination))) {
            SvgConverter.createPdf(svgStream, pdfStream);
        }
    }

SVG 文件

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 80 60">
    <circle cx="50%" cy="50%" r="30" fill="rgb(10, 200, 200)"/>
</svg>

SVG 预览

生成的 PDF 预览

如果我将 SVG 中的位置 (cx, cy) 更改为绝对值,输出似乎很好。

我也尝试将 SVG 转换为 xObject,但也没有帮助。

private void addSvgImageToPdfPage(PdfPage page, String svgContent, float x, float y, float w, float h) {
    // convert svg to xObject
    PdfFormXObject xObject = SvgConverter.convertToXObject(svgContent, page.getDocument());

    // create page canvas
    PdfCanvas pdfCanvas = new PdfCanvas(page);

    // create AT
    AffineTransform at = AffineTransform.getTranslateInstance(x, y);
    at.concatenate(AffineTransform.getScaleInstance(w / xObject.getWidth(), h / xObject.getHeight()));

    float[] matrix = new float[6];
    at.getMatrix(matrix);

    // add svg xObject to canvas
    pdfCanvas.addXObjectWithTransformationMatrix(xObject, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);

    pdfCanvas.release();
}

【问题讨论】:

    标签: java svg itext itext7


    【解决方案1】:

    问题是圆圈在 cx 和 cy 属性中包含百分比。 itextpdf lib 中的 EllipseSvgNodeRenderer 类不支持百分比,仅支持像素。

    https://git.itextsupport.com/projects/I7J/repos/itextcore/browse/svg/src/main/java/com/itextpdf/svg/renderers/impl/EllipseSvgNodeRenderer.java#64

    因此,当您运行代码时,控制台中会出现以下错误:

    ERROR [main] CssUtils - Unknown absolute metric length parsed "%".
    

    “%”符号被忽略,而不是 40 和 30 像素 cx 和 cy 得到 50px 两者都值。

    【讨论】:

    • 您链接到了错误的课程。 CircleSvgNodeRenderer 确实处理 标签。
    • 你可以调试有问题的代码,并看到 EllipseSvgNodeRenderer 做到了。
    • 似乎相关。但是去掉svg中的'%',生成的PDF与cx和cy中带%的文件不同。
    • 当然。因为 % 符号定义了这些值是相对的,并且必须根据父级的大小来计算。如果没有 %,这些都是以像素为单位的绝对值。
    • 确切地说,@A.Alexander 错误是在 setParameter 方法中引发的,该方法在 CircleSvgNodeRenderer 中被覆盖
    【解决方案2】:

    正如@A.Alexander 正确提到的,开箱即用的 iText 不支持圆形元素的相关参数。 但是,您可以为能够处理百分比的圆形标记(而不是 CircleSvgNodeRenderer)注册自己的渲染器。

    AbstractSvgNodeRenderer 中的 parseAbsoluteLength 方法对此很有用。

    自定义渲染器。

    import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
    import com.itextpdf.svg.SvgConstants;
    
    import com.itextpdf.svg.renderers.ISvgNodeRenderer;
    import com.itextpdf.svg.renderers.SvgDrawContext;
    import com.itextpdf.svg.utils.DrawUtils;
    
    /**
     * {@link ISvgNodeRenderer} implementation for the &lt;circle&gt; tag.
     */
    public class CustomCircleSvgNodeRenderer extends AbstractSvgNodeRenderer {
    
        private float cx;
        private float cy;
        float r;
    
        @Override
        protected void doDraw(SvgDrawContext context) {
            PdfCanvas cv = context.getCurrentCanvas();
            cv.writeLiteral("% ellipse\n");
            if (setParameters(context)) {
                // Use double type locally to have better precision of the result after applying arithmetic operations
                cv.moveTo((double) cx + (double) r, cy);
                DrawUtils.arc((double) cx - (double) r, (double) cy - (double) r, (double) cx + (double) r,
                        (double) cy + (double) r, 0, 360, cv);
            }
        }
    
    
        private boolean setParameters(SvgDrawContext context) {
            cx = 0;
            cy = 0;
            if (getAttribute(SvgConstants.Attributes.CX) != null) {
                cx = parseAbsoluteLength(getAttribute(SvgConstants.Attributes.CX), context.getCurrentViewPort().getWidth(), 0.0f, context);
            }
            if (getAttribute(SvgConstants.Attributes.CY) != null) {
                cy = parseAbsoluteLength(getAttribute(SvgConstants.Attributes.CY), context.getCurrentViewPort().getHeight(), 0.0f, context);
            }
            if (getAttribute(SvgConstants.Attributes.R) != null
                    && parseAbsoluteLength(getAttribute(SvgConstants.Attributes.CY), context.getCurrentViewPort().getHeight(), 0.0f, context) > 0) {
                r = parseAbsoluteLength(getAttribute(SvgConstants.Attributes.CY), context.getCurrentViewPort().getHeight(), 0.0f, context);
            } else {
                return false; //No drawing if rx is absent
            }
            return true;
        }
    
        @Override
        public ISvgNodeRenderer createDeepCopy() {
            CustomCircleSvgNodeRenderer copy = new CustomCircleSvgNodeRenderer();
            deepCopyAttributesAndStyles(copy);
            return copy;
        }
    
    }
    

    那你需要自定义DefaultSvgNodeRendererFactory

    每次 SvgConverter 渲染一个圆时,新工厂都必须创建 CustomCircleSvgNodeRenderer 的实例。 例如

    public class CustomRendererFactory extends DefaultSvgNodeRendererFactory {
    
        @Override
        public ISvgNodeRenderer createSvgNodeRendererForTag(IElementNode tag, ISvgNodeRenderer parent) {
            if (SvgConstants.Tags.CIRCLE.equals(tag.name())) {
                return new CustomCircleSvgNodeRenderer();
            }
            return super.createSvgNodeRendererForTag(tag, parent);
        }
    }
    

    强制 SVGConverter 使用 CustomRendererFactory

    您应该将此工厂添加到 ConverterProperties 实例,然后使用 SvgConverter 中具有 ISvgConverterProperties 类型参数的方法。

        SvgConverterProperties properties = new SvgConverterProperties();
        properties.setRendererFactory(new RendererFactory());
        // xObject
        SvgConverter.convertToXObject(svg, pdfDoc, properties);
        // or pdf
        SvgConverter.createPdf(svgStream, pdfStream, properties);
    

    【讨论】:

    • 谢谢帕维尔。它几乎完成了。除了标签比较应该适用于tag.name()
    猜你喜欢
    • 2021-07-06
    • 2013-06-29
    • 2011-05-06
    • 2016-05-23
    • 2016-01-20
    • 2015-10-04
    • 2014-03-22
    • 2020-12-14
    相关资源
    最近更新 更多