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