You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级至FOP 2.6生成PDF时自定义字体未渲染 报字体找不到警告

问题现象

升级Apache FOP至2.6版本后,生成PDF时调用自定义字体GT Super Display Light无法正常渲染,控制台输出字体替换警告:

org.apache.fop.events.LoggingEventListener processEvent
WARNING: Font "Symbol,normal,700" not found. Substituting with "Symbol,normal,400".
org.apache.fop.events.LoggingEventListener processEvent
WARNING: Font "ZapfDingbats,normal,700" not found. Substituting with "ZapfDingbats,normal,400".
org.apache.fop.events.LoggingEventListener processEvent
WARNING: Font "Arial,normal,700" not found. Substituting with "any,normal,700".
org.apache.fop.events.LoggingEventListener processEvent
WARNING: Font "GTSuperDisplayLight,normal,700" not found. Substituting with "any,normal,700".
现有配置

字体配置文件

已开启系统字体自动检测,配置了自定义字体GTSuperDisplayLight的嵌入规则:

<fonts>
   <!-- 自动检测操作系统已安装字体 -->
    <auto-detect/>  
    <!-- 嵌入字体配置 -->
    <!--
    配置信息必须和FO文件中指定的字体完全匹配,否则会自动使用默认字体
    支持的style值: normal | italic | oblique | backslant
    支持的weight值: normal | bold | 100 | 200 | 300 | 400
                      | 500 | 600 | 700 | 800 | 900
    (normal对应400, bold对应700)
    -->
    <font kerning="yes" embed-url="GTSuperDisplayLight.ttf" embedding-mode="subset">
        <font-triplet name="GTSuperDisplayLight" style="normal" weight="700" />
    </font>
</fonts>

FOP初始化Java代码

逻辑为读取XSL同路径下的userconfig.xml构建FopFactory实例:

/*
 * 注册Apache FOP使用指定字体,默认通过配置文件开启字体自动检测
 */
File xconf = new File( getConfigFileURI( xslFullName ) );
FopConfParser parser = new FopConfParser( xconf ); //解析配置
FopFactoryBuilder builder = parser.getFopFactoryBuilder(); //根据用户配置构建工厂
fopFactory = builder.build();

// 冗余代码:使用默认配置重新生成实例,覆盖自定义配置
fopFactory = FopFactory.newInstance( new File( "." ).toURI() );

foUserAgent = fopFactory.newFOUserAgent();
bufferedOutputStream = new FileOutputStream( pdfFilename );
bufferedOutputStream = new BufferedOutputStream( bufferedOutputStream );


private String getConfigFileURI( String xslFilePath )
{
    File f = new File( xslFilePath );
    String configFileURI = f.getParent() + "/userconfig.xml";
    return configFileURI;
}
问题根因
  • 核心代码逻辑错误:通过自定义配置构建完fopFactory实例后,代码立刻调用FopFactory.newInstance()用默认配置重新生成了一个新的fopFactory对象,直接覆盖了之前加载了字体规则的实例。新实例完全没有读取userconfig.xml中的配置,既不会自动检测系统字体,也不会加载自定义嵌入字体,这是所有字体找不到警告的根本原因。
  • 字体三元组配置不匹配:使用的GT Super Display Light本身是Light字重,对应字重值为300,现有配置仅配置了weight=700(粗体)的匹配规则,若FO文件中调用该字体时使用normal(400)、light(300)等其他字重,就算配置加载成功也无法匹配到对应字体。
  • 字体路径存在潜在风险:embed-url仅填写了字体文件名,属于相对路径,FOP会从程序运行工作目录查找字体文件,工作目录随启动方式变化时很容易出现找不到ttf文件的问题。
解决方案
  • 移除冗余的实例覆盖代码:直接删除fopFactory = FopFactory.newInstance( new File( "." ).toURI() );这行代码,保留通过FopConfParser解析自定义配置生成的fopFactory实例即可,修改后系统字体自动检测、自定义字体加载规则都会正常生效,Symbol、ZapfDingbats、Arial等系统字体找不到的警告会同步消失。
  • 修正字体三元组配置:根据实际使用的字体字重配置匹配规则,若需要让该Light字体兼容不同字重的请求,可补充配置多组triplet,参考配置如下:
<font kerning="yes" embed-url="GTSuperDisplayLight.ttf" embedding-mode="subset">
    <font-triplet name="GTSuperDisplayLight" style="normal" weight="300" />
    <font-triplet name="GTSuperDisplayLight" style="normal" weight="400" />
    <font-triplet name="GTSuperDisplayLight" style="normal" weight="700" />
</font>
  • 修正字体文件路径:测试阶段可先使用字体文件的绝对路径配置embed-url,确认字体可正常加载后,再调整为相对于配置文件的路径或classpath资源路径,避免因路径解析失败导致字体加载失败。
  • 若需要使用粗体、斜体等不同样式的GT Super Display字体,需引入对应样式、字重的字体文件单独配置triplet,不要用Light字重的字体强行匹配粗体规则,避免渲染效果不符合预期。

内容的提问来源于stack exchange,提问作者ashish sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 18:45:36