【问题标题】:Vector graphics in iText PDFiText PDF 中的矢量图形
【发布时间】:2010-09-29 07:30:22
【问题描述】:

我们使用 iText 从 Java 生成 PDF(部分基于此站点上的建议)。但是,以 GIF 之类的图像格式嵌入我们的徽标副本会导致它在人们放大和缩小时看起来有点奇怪。

理想情况下,我们希望以矢量格式嵌入图像,例如 EPS、SVG 或只是 PDF 模板。该网站声称已放弃对 EPS 的支持,在 PDF 中嵌入 PDF 或 PS 会导致错误,甚至没有提及 SVG。

我们的代码使用 Graphics2D API 而不是直接使用 iText,但如果能达到结果,我们愿意跳出 AWT 模式并使用 iText 本身。如何做到这一点?

【问题讨论】:

    标签: java image pdf vector itext


    【解决方案1】:

    我最近了解到,您可以将 Graphics2D 对象直接发送到 iText,生成的 PDF 文件与可缩放矢量图形一样好。从您的帖子来看,这听起来可能会解决您的问题。

    Document document = new Document(PageSize.LETTER);
    PdfWriter writer = null;
    try {
        writer = PdfWriter.getInstance(document, new FileOutputStream(your file name));
    } catch (Exception e) {
        // do something with exception
    }
    
    document.open();
    
    PdfContentByte cb = writer.getDirectContent();
    PdfTemplate tp = cb.createTemplate(width, height);
    Graphics2D g2 = tp.createGraphics(width, height, new DefaultFontMapper());
    
    // Create your graphics here - draw on the g2 Graphics object
    
    g2.dispose();
    cb.addTemplate(tp, 0, 100); // 0, 100 = x,y positioning of graphics in PDF page
    document.close();
    

    【讨论】:

    • 这就是我们已经在做的 - 使用 Graphics2D 绘制到页面。我们需要的是添加一个矢量格式的图像。
    【解决方案2】:

    根据documentation,iText 支持以下图像格式:JPEG、GIF、PNG、TIFF、BMP、WMF 和 EPS。我不知道这是否有任何帮助,但我已成功使用 iTextSharp 将矢量 WMF 图像嵌入到 pdf 文件中:

    C#:

    using System;
    using System.IO;
    using iTextSharp.text;
    using iTextSharp.text.pdf;
    
    public class Program 
    {
    
        public static void Main() 
        {
            Document document = new Document();
            using (Stream outputPdfStream = new FileStream("output.pdf", FileMode.Create, FileAccess.Write, FileShare.None))
            using (Stream imageStream = new FileStream("test.wmf", FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                PdfWriter.GetInstance(document, outputPdfStream);
                Image wmf = Image.GetInstance(imageStream);
                document.Open();
                document.Add(wmf);
                document.Close();
            }
        }
    }
    

    【讨论】:

    • 我在尝试您的代码时遇到异常,这对我不起作用,我最近读到不支持 eps,您必须将其转换为 WMF
    【解决方案3】:

    我发现 iText 作者的几个示例使用 Graphics2D API 和 Apache Batik 库在 PDF 中绘制 SVG。

    http://itextpdf.com/examples/iia.php?id=269

    http://itextpdf.com/examples/iia.php?id=263

    出于我的目的,我需要获取一串 SVG 并在 PDF 中以特定大小和位置绘制它,同时保持图像的矢量性质(无光栅化)。

    我想绕过 SAXSVGDocumentFactory.createSVGDocument() 函数中流行的 SVG 文件。我发现以下帖子有助于使用 SVG 文本字符串而不是平面文件。

    http://batik.2283329.n4.nabble.com/Parse-SVG-from-String-td3539080.html

    您必须从您的 String 创建一个 StringReader 并将其传递给 SAXSVGDocumentFactory#createDocument(String, Reader) 方法。作为字符串的第一个参数传递的 URI 将是 SVG 文档的基本文档 URI。仅当您的 SVG 引用任何外部文件时,这才重要。

    最好的问候,

    丹尼尔

    来自 iText 示例的 Java 源代码:

    // SVG as a text string.
    String svg = "<svg>...</svg>";
    
    // Create the PDF document.
    // rootPath is the present working directory path.
    Document document = new Document();
    PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(new File(rootPath + "svg.pdf")));
    document.open();
    
    // Add paragraphs to the document...
    document.add(new Paragraph("Paragraph 1"));
    document.add(new Paragraph(" "));
    
    // Boilerplate for drawing the SVG to the PDF.
    String parser = XMLResourceDescriptor.getXMLParserClassName();
    SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(parser);
    UserAgent userAgent = new UserAgentAdapter();
    DocumentLoader loader = new DocumentLoader(userAgent);
    BridgeContext ctx = new BridgeContext(userAgent, loader);
    ctx.setDynamicState(BridgeContext.DYNAMIC);
    GVTBuilder builder = new GVTBuilder();
    PdfContentByte cb = writer.getDirectContent();
    
    // Parse the SVG and draw it to the PDF.
    Graphics2D g2d = new PdfGraphics2D(cb, 725, 400);
    SVGDocument chart = factory.createSVGDocument(rootPath, new StringReader(svg));
    GraphicsNode chartGfx = builder.build(ctx, chart);
    chartGfx.paint(g2d);
    g2d.dispose();
    
    // Add paragraphs to the document...
    document.add(new Paragraph("Paragraph 2"));
    document.add(new Paragraph(" "));
    
    document.close();
    

    请注意,这会将 SVG 绘制到您正在处理的 PDF 中。 SVG 显示为文本上方的浮动层。我仍在努力移动/缩放它并使其与文本内联,但希望这超出了问题的直接范围。

    希望这能有所帮助。

    干杯

    编辑:我能够使用以下方法将我的 svg 实现为内联对象。注释行用于添加快速边框以检查定位。

    SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());
    UserAgent userAgent = new UserAgentAdapter();
    DocumentLoader loader = new DocumentLoader(userAgent);
    BridgeContext ctx = new BridgeContext(userAgent, loader);
    ctx.setDynamicState(BridgeContext.DYNAMIC);
    GVTBuilder builder = new GVTBuilder();
    SVGDocument svgDoc = factory.createSVGDocument(rootPath, new StringReader(svg));
    PdfTemplate svgTempl = PdfTemplate.createTemplate(writer, Float.parseFloat(svgDoc.getDocumentElement().getAttribute("width")), Float.parseFloat(svgDoc.getDocumentElement().getAttribute("height")));
    Graphics2D g2d = new PdfGraphics2D(svgTempl, svgTempl.getWidth(), svgTempl.getHeight());
    GraphicsNode chartGfx = builder.build(ctx, svgDoc);
    chartGfx.paint(g2d);
    g2d.dispose();
    Image svgImg = new ImgTemplate(svgTempl);
    svgImg.setAlignment(Image.ALIGN_CENTER);
    //svgImg.setBorder(Image.BOX);
    //svgImg.setBorderColor(new BaseColor(0xff, 0x00, 0x00));
    //svgImg.setBorderWidth(1);
    document.add(svgImg);
    

    【讨论】:

    • 您好,我正在尝试寻找一种方法来做类似的事情,将 SVG 文件呈现为 PDF 文档。您使用的是哪个版本的 Itext?我注意到,com.lowagie.text.pdf.PdfGraphics2D 类的构造函数是私有的,至少在 iText 2.1.2 中是这样,我在 IText 5.5.4 中找不到这样的类。谢谢! – Jose Tepedino 6 分钟前
    • @JoseTepedino 很抱歉,但我已经好几年没碰过这个了(因为我写了答案)。我想我使用的是 2012 年 9 月左右发布的版本,所以根据他们的 GitHub 版本 - github.com/itext/itextpdf/releases/tag/5.3.2,它可能是 2012 年 8 月发布的 iText 5.3.2。希望对您有所帮助。
    • 感谢您的留言!通过找到一个 API 页面,我取得了一些进展,该页面说明了 com.itextpdf.awt 包中的 PdfGraphics2D 类自 iText 5.0.2 以来就存在:developers.itextpdf.com/reference/…。我还有一些内容需要说明,但找出要使用的正确版本是一个好的开始。谢谢!
    【解决方案4】:

    这是我从我在这里找到的帖子和官方示例中得出的:

    /**
     * Reads an SVG Image file into an com.itextpdf.text.Image instance to embed it into a PDF
     * @param svgPath SVG filepath
     * @param writer PdfWriter instance 
     * @return Instance of com.itextpdf.text.Image holding the SVG file
     * @throws IOException
     * @throws BadElementException
     */
    private static Image getSVGImage(String svgPath, PdfWriter writer) throws IOException, BadElementException {
        SVGDocument svgDoc = new SAXSVGDocumentFactory(null).createSVGDocument(null, new FileReader(svgPath));
    
        // Try to read embedded height and width
        float svgWidth = Float.parseFloat(svgDoc.getDocumentElement().getAttribute("width").replaceAll("[^0-9.,]",""));
        float svgHeight = Float.parseFloat(svgDoc.getDocumentElement().getAttribute("height").replaceAll("[^0-9.,]",""));
    
        PdfTemplate svgTempl = PdfTemplate.createTemplate(writer, svgWidth, svgHeight);
        Graphics2D g2d = new PdfGraphics2D(svgTempl, svgTempl.getWidth(), svgTempl.getHeight());
        GraphicsNode chartGfx = (new GVTBuilder()).build(new BridgeContext(new UserAgentAdapter()), svgDoc);
        chartGfx.paint(g2d);
        g2d.dispose();
    
        return new ImgTemplate(svgTempl);
    }
    

    Image 实例可以很容易地添加到 pdf 中(在我的例子中作为签名)。

    【讨论】:

    • 可以使用下一个 maven 依赖项导入上面代码中的类:&lt;dependency&gt; &lt;groupId&gt;org.apache.xmlgraphics&lt;/groupId&gt; &lt;artifactId&gt;batik-gvt&lt;/artifactId&gt; &lt;version&gt;1.7&lt;/version&gt; &lt;/dependency&gt;
    • 尝试使用它,但我在使用 batik-gvt v1.14 时遇到问题 - 课程已更改。该评论中提到的 v1.7 与我的 Java 11 maven 依赖项存在问题。最近有人用过这种方法吗?
    【解决方案5】:

    这对我使用 itext 7.1.3 通过 SVGConverter 渲染 SVG 图像很有用。

    PdfWriter writer = new PdfWriter(new FileOutputStream("/home/users/Documents/pdf/new.pdf"));
    
            PdfDocument pdfDoc = new PdfDocument(writer);
    
            Document doc = new Document(pdfDoc);
    
            URL svgUrl = new File(svg).toURI().toURL();
    
            doc.add(new Paragraph("new pikachu"));                      
    
            Image image = SvgConverter.convertToImage(svgUrl.openStream(), pdfDoc);                 
            doc.add(image);
    
            doc.close();
    

    【讨论】:

      【解决方案6】:

      新版本的 iText 也支持 SVG 文件。请参考this页面。

      【讨论】:

      • 同意。在这个答案中加入有用的细节,我很乐意支持它。
      【解决方案7】:

      我可以使它工作的唯一方法是将矢量 SVG 文件转换为矢量 PDF 文件,然后将其插入我的 itext/openpdf PDF。

      您理解正确:我在我的 PDF 中插入了一个矢量 PDF,它的工作原理就像一个魅力。您可以放大,一切正常。

      我在 Spring Boot 应用程序中使用此代码:

      public void addVectorToPDF(PdfWriter writer) throws IOException {
      
          // My SVG image that I converted to PDF format, keeping the vectorial format.
          URL resource = this.getClass().getResource("/resources/my-vectorial-image-pdf-format.pdf");
      
          PdfReader reader = new PdfReader(resource);
      
          // Getting the page 01 of the PDF. The PDF has only the
          // SVG image inside, so the page 01 is everything.
          PdfImportedPage page = writer.getImportedPage(reader, 1);
      
          float posX = 50;
          float posY = 300;
          float scale = 0.75F;
          PdfContentByte canvas = writer.getDirectContent();
      
          // adding vectorial image pdf to my pdf
          canvas.addTemplate(page, scale, 0, 0, scale, posX, posY);
      
      }
      

      您可以使用 zamzar.com 将您的矢量 SVG 转换为 PDF。对我来说效果很好。

      在我的例子中,使用 Batik 和 Graphics2D 的解决方案没有保留矢量格式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-18
        • 2014-04-02
        • 1970-01-01
        相关资源
        最近更新 更多