Vector graphics in iText PDF

33,897

Solution 1

According to the documentation iText supports the following image formats: JPEG, GIF, PNG, TIFF, BMP, WMF and EPS. I don't know if this might be of any help but I have successfully used iTextSharp to embed vector WMF image in a pdf file:

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();
        }
    }
}

Solution 2

I found a couple of examples by the iText author that use the Graphics2D API and the Apache Batik library to draw the SVG in a PDF.

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

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

For my purposes, I needed to take a string of SVG and draw that in a PDF at a certain size and location while maintaining the vector nature of the image (no rasterization).

I wanted to bypass the SVG file that seems prevalent in the SAXSVGDocumentFactory.createSVGDocument() functions. I found the following post helpful for using a SVG text string instead of a flat file.

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

You have to create a StringReader from your String and pass that to the SAXSVGDocumentFactory#createDocument(String, Reader) method. The URI that you pass as the first parameter as a String will be the base document URI of the SVG document. This should only be important if your SVG references any external files.

Best regards,

Daniel

Java Source derived from the iText examples:

// 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();

Note that this will draw a SVG to the PDF you are working on. The SVG appears as a floating layer above text. I'm still working on moving/scaling it and having it rest inline with text, but hopefully that is outside the immediate scope of the question.

Hope this was able to help.

Cheers

EDIT: I was able to implement my svg as an inline object using the following. The commented lines are for adding a quick border to check positioning.

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);

Solution 3

This what i derived from posts i found here and the official examples:

/**
 * 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);
}

The Image instance can the be added easily to the pdf (in my case as a signature).

Solution 4

I recently learned that you can send your Graphics2D object directly to iText, and the resulting PDF files are just as good as scalable vector graphics. From your post, it sounds like this might solve your problem.

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();

Solution 5

This worked for me using itext 7.1.3 to render SVG image by SVGConverter.

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();
Share:
33,897
Marcus Downing
Author by

Marcus Downing

Full stack web developer and sysadmin with an interest in language design, anime and all things oriental.

Updated on February 25, 2020

Comments

  • Marcus Downing
    Marcus Downing about 4 years

    We use iText to generate PDFs from Java (based partly on recommendations on this site). However, embedding a copy of our logo in an image format like GIF results in it looking a bit strange as people zoom in and out.

    Ideally we'd like to embed the image in a vector format, such as EPS, SVG or just a PDF template. The website claims that EPS support has been dropped, that embedding a PDF or PS within a PDF can result in errors, and it doesn't even mention SVG.

    Our code uses the Graphics2D API rather than iText directly, but we'd be willing to break out of AWT mode and use iText itself if it achieved the result. How can this be done?

  • Marcus Downing
    Marcus Downing over 15 years
    That's what we're already doing - using Graphics2D to draw to the page. What we need is to add an image in a vector format.
  • RJardines
    RJardines over 12 years
    I get an exception when i tried your code, this is not working for me, i read recently that eps is not supported, you must convert it to WMF
  • Jose Tepedino
    Jose Tepedino about 8 years
    Hello, I'm trying to find a way to do something similar, to render SVG files into a PDF document. Which version of Itext are you using? I've noticed that constructors are private for class com.lowagie.text.pdf.PdfGraphics2D, at least in iText 2.1.2, and I could not find such a class in IText 5.5.4. Thanks! – Jose Tepedino 6 mins ago
  • clayzermk1
    clayzermk1 about 8 years
    @JoseTepedino I'm sorry, but I haven't touched this in years (since I wrote the answer). I would imagine that I was using a version that was released around Sep 2012, so that would probably be iText 5.3.2 which was released in Aug 2012 according to their GitHub releases - github.com/itext/itextpdf/releases/tag/5.3.2. Hope that helps.
  • Jose Tepedino
    Jose Tepedino about 8 years
    thanks for your message! I've made a little progress, by finding an API page stating that PdfGraphics2D class from package com.itextpdf.awt exists since iText 5.0.2: developers.itextpdf.com/reference/…. I still have some ground to cover, but finding out the right version to use is a good beginning.Thanks!
  • Serhii Maksymchuk
    Serhii Maksymchuk about 7 years
    Classes in code above could be imported using next maven dependency: <dependency> <groupId>org.apache.xmlgraphics</groupId> <artifactId>batik-gvt</artifactId> <version>1.7</version> </dependency>
  • Marcus Downing
    Marcus Downing about 5 years
    Agreed. Put useful details into this answer, and I'll gladly upvote it.
  • Daniel Bleisteiner
    Daniel Bleisteiner almost 3 years
    Trying to use this but I've problems using v1.14 of batik-gvt – classes have changed. And v1.7 as mentioned in that comment has problems with my Java 11 maven dependencies. Has anyone used this approach recently?