使用PDFBox在PDF上绘制矢量图像

15
我希望使用Apache PDFBox在PDF上绘制矢量图像。
这是我用来绘制常规图像的代码。
PDPage page = (PDPage) document.getDocumentCatalog().getAllPages().get(1);
PDPageContentStream contentStream = new PDPageContentStream(document, page, true, true);

BufferedImage _prevImage = ImageIO.read(new FileInputStream("path/to/image.png"));
PDPixelMap prevImage = new PDPixelMap(document, _prevImage);
contentStream.drawXObject(prevImage, prevX, prevY, imageWidth, imageHeight);
如果我使用svgwmf图像而不是PNG,生成的PDF文档会出现损坏问题。我希望图像是矢量图像的主要原因是,使用PNG或JPG时,图像看起来很糟糕,我认为它被压缩了,所以看起来很糟糕。使用矢量图像不应该发生这种情况(嗯,在Inkscape中将svg路径导出为PDF时,矢量路径得到保留)。有没有一种方法可以使用Apache PDFBox绘制svg或wmf(或其他矢量图像)到PDF中?我目前正在使用PDFBox 1.8,如果这很重要。

快速提示,位图(.png,.jpg等)与矢量图像(.svg)完全不同,在PDFBox库中的实现方式可能会有很大不同。 - insidesin
1
“加载矢量图像并绘制它们”的问题在于,就像位图图像一样,您需要解释数据(其中您可能不完全了解 - 显然,PDFBox会方便地将位图图像转换为必要的正确PDF语法)。因此,要添加SVG图像,您需要编写代码将所有SVG命令转换为其PDF等效项(包括将“元”内容(如过滤器、阴影和箭头)扩展为基元)。对于WMF,也是如此。对于EPS,也是如此。对于AI ...对于CorelDraw ...好吧,我想你明白了。 - Jongware
2
话虽如此,一些搜索结果表明使用Batik作为中间步骤先创建一个单独的PDF文件,然后将该PDF文件插入到您的文件中。 - Jongware
最好的解决方案是Jongware提到的。第二好的方法是将它们转换为PNG并使用PDPixelMap。这不会像PDJpeg那样使它们看起来很糟糕。(我怀疑您使用了从PNG创建的BufferedImage的PDJpeg,对于具有锐利边缘的图像来说,这确实是一个坏主意) - Tilman Hausherr
@TilmanHausherr 我不知道它被称为“叠加”,这就是为什么我找不到任何相关信息!这对我帮助很大,谢谢! - BackSlash
显示剩余6条评论
3个回答

5
请参考在这篇 Jira中赞誉的库pdfbox-graphics2d,它与iText的template.createGraphics()相似。您可以将SVG通过BatikSalamander等工具绘制到PdfBoxGraphics2D类上。请参阅GitHub页面以获取样例。请注意保留HTML标记。
PDDocument document = ...;
PDPage page = ...; // page whereon to draw

String svgXML = "<svg>...</svg>";
double leftX = ...;
double bottomY = ...; // PDFBox coordinates are oriented bottom-up!

// I set these to the SVG size, which I calculated via Salamander.
// Maybe it doesn't matter, as long as the SVG fits on the graphic.
float graphicsWidth = ...;
float graphicsHeight = ...;

// Draw the SVG onto temporary graphics.
var graphics = new PdfBoxGraphics2D(document, graphicsWidth, graphicsHeight);
try {
    int x = 0;
    int y = 0;
    drawSVG(svg, graphics, x, y); // with Batik, Salamander, or whatever you like
} finally {
    graphics.dispose();
}

// Graphics are not visible till a PDFormXObject is added.
var xform = graphics.getXFormObject();

try (var contentWriter = new PDPageContentStream(document, page, AppendMode.APPEND, false)) { // false = don't compress
    // XForm objects have to be placed via transform,
    // since they cannot be placed via coordinates like images.
    var transform = AffineTransform.getTranslateInstance(leftX, bottomY);
    xform.setMatrix(transform);

    // Now the graphics become visible.
    contentWriter.drawForm(xform);
}

如果您希望将SVG图形缩小到25%的大小,则可以进行缩放:

// Way 1: Scale the SVG beforehand
svgXML = String.format("<svg transform=\"scale(%f)\">%s</svg>", .25, svgXML);

// Way 2: Scale in the transform (before calling xform.setMatrix())
transform.concatenate(AffineTransform.getScaleInstance(.25, .25));

如果您要添加到现有的PDF中,请在PDPageContentStream构造函数中将第五个参数设置为true。(请参见javadoc原因) - Tilman Hausherr
1
要使用Salamander处理SVG文件,您可以创建一个SVGUniverse,然后创建一个SVGDiagram(请参见此链接https://dev59.com/KEzSa4cB1Zd3GeqPmWqT),最后执行`diagram.render(graphics);`。 - Nirmal
你好。drawSVG(svg, graphics, x, y); 你能更具体一些吗,例如如何使用Batik绘制SVG文件? - coderodde
你前面的评论中有一个链接,介绍了使用Salamander类将其写入图形的一种常见方法。 - Coemgenus

0

我并不是直接这样做的。 首先,使用FOP库和Batik将SVG文档转换为PDF文档。 https://xmlgraphics.apache.org/fop/dev/design/svg.html

其次,您可以使用pdfbox中的LayerUtility将新的pdf文档转换为PDXObjectForm。之后,只需要在最终的pdf文档中包含PDXObjectForm即可。


0

对我来说最终可行的解决方案是加载一个SVG文件并将其覆盖在PDF文件上(这将在PDF文档的左下角坐标(0,0)处的500x500框中呈现SVG):

package com.example.svgadder;

import java.io.*;
import java.nio.*;

import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;

import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D;

import java.awt.geom.AffineTransform;

import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
import com.kitfox.svg.SVGUniverse;

public class App 
{
    public static void main( String[] args ) throws Exception {
        
        App app = new App();

    }

    public App() throws Exception {
        
        // loading PDF and SVG files

        File pdfFile = new File("input.pdf");
        File svgFile = new File("input.svg");

        PDDocument doc = PDDocument.load(pdfFile);
        PDPage page = doc.getPage(0);

        SVGUniverse svgUniverse = new SVGUniverse();
        SVGDiagram diagram = svgUniverse.getDiagram(svgUniverse.loadSVG(f.toURL()));
        
        PdfBoxGraphics2D graphics = new PdfBoxGraphics2D(doc, 500, 500);

        try {
            diagram.render(graphics);
        } finally {
            graphics.dispose();
        }

        PDFormXObject xform = graphics.getXFormObject();

        try (PDPageContentStream contentWriter = new PDPageContentStream(doc, page, AppendMode.APPEND, false)) {

            AffineTransform transform = AffineTransform.getTranslateInstance(0, 0);
            xform.setMatrix(transform);
            contentWriter.drawForm(xform);
        }

        doc.save("res.pdf");
        doc.close();
    }
}

请从这里使用svgSalamander: https://github.com/mgarin/svgSalamander

请使用Coemgenus建议的方法对最终叠加的SVG进行缩放。我尝试了第二个选项,效果很好。

Nirmal


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接