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