I am trying to embed a SVG into a PDF using both Apache Batik and OpenPDF libraries in Java.
For this purpose, following a tutorial, I need to create a GraphicsNode, that later will be converted into a ImgTemplate to be use on the PDF document.
My code is:
private GraphicsNode convert(String svgCode) throws IOException {
final SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());
final SVGDocument svgDocument = factory.createSVGDocument("", new ByteArrayInputStream(svgCode.getBytes(StandardCharsets.UTF_8)));
final UserAgent userAgent = new UserAgentAdapter();
final DocumentLoader loader = new DocumentLoader(userAgent);
final BridgeContext context = new BridgeContext(userAgent, loader);
context.setDynamicState(BridgeContext.DYNAMIC);
final GVTBuilder builder = new GVTBuilder();
return builder.build(context, svgDocument);
}
As the SVG code is using a Font that is on my resources folder, I get a:
java.lang.NullPointerException: Cannot invoke "java.util.List.iterator()" because "this.srcs" is null
at org.apache.batik.bridge.FontFace.getFontFamily(FontFace.java:98)
at org.apache.batik.bridge.CSSFontFace.getFontFamily(CSSFontFace.java:176)
at org.apache.batik.bridge.SVGFontUtilities.getFontFamily(SVGFontUtilities.java:144)
FontFace has two constructors and is using when debuggin:
protected FontFace(String familyName) {
super(familyName);
}
That is not initializing the srcs
element. And therefore I get a NPE.
Maybe I need to register the fonts in some way. Then I have added some extra lines to the code:
private GraphicsNode convert(String svgCode) throws IOException {
//Create org.w3c.dom.svg.SVGDocument
final SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());
final SVGDocument svgDocument = factory.createSVGDocument("", new ByteArrayInputStream(svgCode.getBytes(StandardCharsets.UTF_8)));
//Create a org.apache.batik.gvt.GraphicsNode from the org.w3c.dom.svg.SVGDocument
final UserAgent userAgent = new UserAgentAdapter();
final DocumentLoader loader = new DocumentLoader(userAgent);
// Notice, that you should use org.apache.batik.bridge.svg12.SVG12BridgeContext.SVG12BridgeContext for the svg version 1.2
final BridgeContext context = new SVG12BridgeContext(userAgent, loader);
context.setDynamicState(BridgeContext.DYNAMIC);
//Register fonts
final GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
final Set<Font> loadedFonts = com.custom.FontFactory.getLoadedFonts();
loadedFonts.forEach(graphicsEnvironment::registerFont);
final GVTBuilder builder = new GVTBuilder();
return builder.build(context, svgDocument);
}
My com.custom.FontFactory
returns a Set<Font>
that are already loaded from resources.
That I am not sure if it is the correct way or not for registering fonts. But seems that have some effect, as now I get a:
java.lang.NullPointerException: Cannot invoke "java.util.Collection.toArray()" because "c" is null
at java.base/java.util.LinkedList.addAll(LinkedList.java:419)
at java.base/java.util.LinkedList.addAll(LinkedList.java:398)
at java.base/java.util.LinkedList.<init>(LinkedList.java:130)
at org.apache.batik.bridge.FontFace.createFontFace(FontFace.java:78)
That is a different error on a different place. I have also tried to directly register the fonts as follows:
graphicsEnvironment.registerFont(Font.createFont(Font.TRUETYPE_FONT,
new File("<pathToTheResource>"))));
But there is no difference with the last error.
Then the question is.. how can I user a custom fonts on Batik for converting a SVG to a GraphicNode?
If I convert a SVG without any embedded font, it is working fine. Then I am assuming that the issue is related to the fonts used.
Ok. Seems that the issue was not related to the fonts that I want to register with GraphicsEnvironment
, but with the fonts that are embedded on the svg. Ia have followed this recommendation to have fonts inside the SVG. For this purpose, I create a font-face
with a base64 codified font in the src
field.
Seems that Batik does not like this content on the src
. But, as I am embedding the fonts using GraphicsEnvironment
, I do not need the embeded font on the SVG.
TL;DR Embed the fonts inside the SVG xml when you are using the SVG on a webpage, but do not do it when using Batik to make conversions to PNG, JPEG, or any other rendering.