FOP 2.8 SVG渲染空指针问题及最优语法咨询
问题:FOP 2.8中两种SVG字体引用写法哪种更合适?
环境与配置
使用FOP 2.8 + XSLT 2,相关XSLT样式表定义如下:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/TR/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xalan="http://xml.apache.org/xalan" xmlns:fox="http://xml.apache.org/fop/extensions" xmlns:pre="xalan://org.apache.xalan.Version" xmlns:svg="http://www.w3.org/2000/svg">
执行命令与报错
执行以下FOP命令时出现错误:
fop -c /path/to/config/config.xml -xml /path/to/test.xml -xsl /path/to/test.xslt -pdf /path/to/tst.pdf -param img /path/to/param
错误信息:
[ERROR] FOUserAgent - SVG graphic could not be rendered. Reason: java.lang.NullPointerException: Cannot load from object array because "this.boundingBoxes" is null <java.lang.NullPointerException: Cannot load from object array because "this.boundingBoxes" is null>java.lang.NullPointerException: Cannot load from object array because "this.boundingBoxes" is null at org.apache.fop.fonts.MultiByteFont.getBoundingBox(MultiByteFont.java:212) at org.apache.fop.fonts.LazyFont.getBoundingBox(LazyFont.java:305) at org.apache.fop.svg.font.FOPGVTGlyphVector.buildBoundingBoxes(FOPGVTGlyphVector.java:435) at org.apache.fop.svg.font.FOPGVTGlyphVector.getBoundingBoxes(FOPGVTGlyphVector.java:427) at org.apache.fop.svg.font.FOPGVTGlyphVector.getGlyphMetrics(FOPGVTGlyphVector.java:297) at org.apache.batik.bridge.GlyphLayout.doExplicitGlyphLayout(GlyphLayout.java:1107) at org.apache.batik.bridge.GlyphLayout.adjustTextSpacing(GlyphLayout.java:1465) at org.apache.batik.bridge.GlyphLayout.getAdvance2D(GlyphLayout.java:386)
初始SVG代码(报错版本)
XSLT中导致报错的SVG片段:
<svg:svg xmlns:svg="http://www.w3.org/2000/svg" xml:base="http://example.org/today/" width="1100" height="1150"> <svg:defs> <svg:font-face font-family="Test"> <svg:font-face-src> <svg:font-face-uri href="TestReg.ttf"/> </svg:font-face-src> </svg:font-face> </svg:defs> <svg:text font-family="Test" font-size="150pt" x="38.5" y="654.5" transform="rotate(-76.5,40,407)" textLength="125mm" lengthAdjust="spacingAndGlyphs" style="fill:rgb(0,0,0);fill-opacity:0.3"> DRAFT </svg:text> </svg:svg>
修复后的两种可行写法
排查后发现是FOP配置文件问题,修复后以下两种写法均可正常运行:
写法一:显式使用svg:defs定义字体
<fo:instream-foreign-object> <svg:svg xmlns:svg="http://www.w3.org/2000/svg" xml:base="http://example.org/today/" width="1100" height="1150"> <svg:defs> <svg:font-face font-family="TestReg"> <svg:font-face-src> <svg:font-face-uri href="TstReg.ttf"/> </svg:font-face-src> </svg:font-face> </svg:defs> <svg:text font-family="TestReg" font-size="150pt" x="68.5" y="354.5" transform="rotate(-46.5,50,350)" textLength="125mm" lengthAdjust="spacingAndGlyphs" style="fill:rgb(0,0,0);fill-opacity:0.3"> TEST </svg:text> </svg:svg> </fo:instream-foreign-object>
写法二:依赖FOP配置文件加载字体,不使用svg:defs
<fo:instream-foreign-object> <svg:svg xmlns:svg="http://www.w3.org/2000/svg" xml:base="http://example.org/today/" width="1100" height="1150"> <svg:text font-family="TestReg" font-size="150pt" x="68.5" y="354.5" transform="rotate(-46.5,50,350)" textLength="125mm" lengthAdjust="spacingAndGlyphs" style="fill:rgb(0,0,0);fill-opacity:0.3"> TEST </svg:text> </svg:svg> </fo:instream-foreign-object>
推荐方案与原因
优先选择写法二(依赖FOP配置加载字体),理由如下:
- 集中管理,减少冗余:字体配置统一放在FOP的config.xml中,所有SVG/FO内容可共用该字体,不用在每个SVG里重复定义font-face,降低维护成本。
- 规避字体加载风险:之前出现的NullPointerException是因为字体初始化不完整,通过FOP配置加载时,FOP会提前完成字体的全量初始化(包括boundingBoxes这类核心数据),从根源避免这类底层渲染错误。
- 简化SVG代码:SVG只需负责文本的样式与布局,无需额外维护字体定义逻辑,代码更简洁清晰。
如果是单个SVG需要使用特殊字体且不想全局配置的场景,写法一也可以使用,但要注意:
- 确保字体文件路径正确(结合xml:base或使用绝对路径)
- 多环境测试字体加载稳定性,避免出现类似初始化问题
内容的提问来源于stack exchange,提问作者Rito Sarkar
相关产品推荐
相关产品推荐

