javaapache-fop

Apache FOP - is there a way to embed font programmatically?


When creating a PDF using Apache FOP it is possible to embed a font with configuration file. The problem emerges when the app is a web application and it is necessary to embed a font that is inside WAR file (so treated as resource).

It is not acceptable to use particular container's folder structure to determine where exactly the war is located (when in configuration xml file we set tag to ./, it is set to the base folder of running container like C:\Tomcat\bin).

So the question is: Do anyone know the way to embed a font programatically?


Solution

  • After going through lots of FOP java code I managed to get it to work.

    Descriptive version

    Main idea is to force FOP to use custom PDFRendererConfigurator that will return desired font list when getCustomFontCollection() is executed.

    In order to do it we need to create custom PDFDocumentHandlerMaker that will return custom PDFDocumentHandler (form method makeIFDocumentHandler()) which will in turn return our custom PDFRendererConfigurator (from getConfigurator() method) that, as above, will set out custom font list.

    Then just add custom PDFDocumentHandlerMaker to RendererFactory and it will work.

    FopFactory > RendererFactory > PDFDocumentHandlerMaker > PDFDocumentHandler > PDFRendererConfigurator

    Full code

    FopTest.java

    public class FopTest {
    
        public static void main(String[] args) throws Exception {
    
            // the XSL FO file
            StreamSource xsltFile = new StreamSource(
                    Thread.currentThread().getContextClassLoader().getResourceAsStream("template.xsl"));
            // the XML file which provides the input
            StreamSource xmlSource = new StreamSource(
                    Thread.currentThread().getContextClassLoader().getResourceAsStream("employees.xml"));
            // create an instance of fop factory
            FopFactory fopFactory = new FopFactoryBuilder(new File(".").toURI()).build();
    
            RendererFactory rendererFactory = fopFactory.getRendererFactory();
            rendererFactory.addDocumentHandlerMaker(new CustomPDFDocumentHandlerMaker());
    
            // a user agent is needed for transformation
            FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
    
            // Setup output
            OutputStream out;
            out = new java.io.FileOutputStream("employee.pdf");
    
            try {
                // Construct fop with desired output format
                Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
    
                // Setup XSLT
                TransformerFactory factory = TransformerFactory.newInstance();
                Transformer transformer = factory.newTransformer(xsltFile);
    
                // Resulting SAX events (the generated FO) must be piped through to
                // FOP
                Result res = new SAXResult(fop.getDefaultHandler());
    
                // Start XSLT transformation and FOP processing
                // That's where the XML is first transformed to XSL-FO and then
                // PDF is created
                transformer.transform(xmlSource, res);
            } finally {
                out.close();
            }
    
        }
    
    }
    

    CustomPDFDocumentHandlerMaker.java

    public class CustomPDFDocumentHandlerMaker extends PDFDocumentHandlerMaker {
    
        @Override
        public IFDocumentHandler makeIFDocumentHandler(IFContext ifContext) {
            CustomPDFDocumentHandler handler = new CustomPDFDocumentHandler(ifContext);
            FOUserAgent ua = ifContext.getUserAgent();
            if (ua.isAccessibilityEnabled()) {
                ua.setStructureTreeEventHandler(handler.getStructureTreeEventHandler());
            }
            return handler;
        }
    
    }
    

    CustomPDFDocumentHandler.java

    public class CustomPDFDocumentHandler extends PDFDocumentHandler {
    
        public CustomPDFDocumentHandler(IFContext context) {
            super(context);
        }
    
        @Override
        public IFDocumentHandlerConfigurator getConfigurator() {
            return new CustomPDFRendererConfigurator(getUserAgent(), new PDFRendererConfigParser());
        }
    
    }
    

    CustomPDFRendererConfigurator.java

    public class CustomPDFRendererConfigurator extends PDFRendererConfigurator {
    
        public CustomPDFRendererConfigurator(FOUserAgent userAgent, RendererConfigParser rendererConfigParser) {
            super(userAgent, rendererConfigParser);
        }
    
        @Override
        protected FontCollection getCustomFontCollection(InternalResourceResolver resolver, String mimeType)
                throws FOPException {
    
            List<EmbedFontInfo> fontList = new ArrayList<EmbedFontInfo>();
            try {
                FontUris fontUris = new FontUris(Thread.currentThread().getContextClassLoader().getResource("UbuntuMono-Bold.ttf").toURI(), null);
                List<FontTriplet> triplets = new ArrayList<FontTriplet>();
                triplets.add(new FontTriplet("UbuntuMono", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL));
                EmbedFontInfo fontInfo = new EmbedFontInfo(fontUris, false, false, triplets, null, EncodingMode.AUTO, EmbeddingMode.AUTO);
                fontList.add(fontInfo);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return createCollectionFromFontList(resolver, fontList);
        }
    
    }