Apache FOP - 有没有办法以编程方式嵌入字体?

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

使用 Apache FOP 创建 PDF 时,可以在配置文件中嵌入字体。当应用程序是 Web 应用程序并且需要嵌入 WAR 文件中的字体(因此被视为资源)时,问题就会出现。

使用特定容器的文件夹结构来确定 war 的确切位置是不可接受的(当在配置 xml 文件中我们将标记设置为 ./,它被设置到 运行 容器的基础文件夹,如 C:\Tomcat\bin)。

所以问题是:有人知道以编程方式嵌入字体的方法吗?

是的,你可以做到。您需要以编程方式设置 FOP 的第一个基本目录。

    fopFactory = FopFactory.newInstance();
    // for image base URL : images from Resource path of project
    String serverPath = request.getSession().getServletContext().getRealPath("/");
    fopFactory.setBaseURL(serverPath);
    // for fonts base URL :  .ttf from Resource path of project
    fopFactory.getFontManager().setFontBaseURL(serverPath);

然后使用 FOB 字体配置 file.It 将使用上面的基本路径。

只需将您的字体文件放在 Web 应用程序资源文件夹中,然后在 FOP 的字体配置文件中引用该路径。

评论后:以编程方式读取字体配置(不是首选且仍然按照要求的干净方式)

    //This is NON tested and PSEUDO code to get understanding of logic
    FontUris fontUris = new FontUris(new URI("<font.ttf relative path>"), null);
    EmbedFontInfo fontInfo = new EmbedFontInfo(fontUris, "is kerning enabled boolean", "is aldvaned enabled boolean", null, "subFontName");
    List<EmbedFontInfo> fontInfoList = new ArrayList<>();
    fontInfoList.add(fontInfo);
    //set base URL for Font Manager to use relative path of ttf file.
    fopFactory.getFontManager().updateReferencedFonts(fontInfoList);

您可以获得更多关于 FOP 相对路径的信息 https://xmlgraphics.apache.org/fop/2.2/configuration.html

经过大量 FOP java 代码后,我设法让它工作。

描述性版本

主要思想是强制 FOP 使用自定义 PDFRendererConfigurator,在执行 getCustomFontCollection() 时将 return 所需的字体列表。

为了做到这一点,我们需要创建自定义 PDFDocumentHandlerMaker,它将 return 自定义 PDFDocumentHandler(表单方法 makeIFDocumentHandler()),这又将 return 我们的自定义 PDFRendererConfigurator(来自 getConfigurator() 方法),如上所述,将设置自定义字体列表。

然后只需将自定义 PDFDocumentHandlerMaker 添加到 RendererFactory 即可。

FopFactory > RendererFactory > PDFDocumentHandlerMaker > PDFDocumentHandler > PDFRendererConfigurator

完整代码

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

}