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

Quarkus中Qute模板使用Open Sans字体生成PDF失效问题排查

解决Quarkus+Qute+ITextRenderer生成PDF时Open Sans字体加载失败问题

核心问题分析

你遇到的问题根源在于Flying Saucer(即ITextRenderer)对Web字体的支持限制:

  • 不支持通过<link>加载远程Google Fonts的CSS,无法解析并下载远程字体文件
  • 本地字体路径未正确指向资源目录,且未手动注册字体到渲染器

正确解决方案:本地字体嵌入+手动注册

1. 准备字体文件

将Open Sans各字重的字体文件(比如OpenSans-Regular.ttf、OpenSans-Medium.ttf等)放在Quarkus项目的src/main/resources/fonts/目录下,确保打包后能被classpath访问。

2. 修改HTML模板的@font-face定义

替换原有字体引入代码,使用classpath路径并明确字重:

<style>
    @page {
        size: A4 landscape;
        margin: 0;
    }
    /* 注册各字重的Open Sans字体 */
    @font-face {
        font-family: 'Open Sans';
        src: url('classpath:/fonts/OpenSans-Regular.ttf') format('truetype');
        font-weight: 400;
        font-style: normal;
    }
    @font-face {
        font-family: 'Open Sans';
        src: url('classpath:/fonts/OpenSans-Medium.ttf') format('truetype');
        font-weight: 500;
        font-style: normal;
    }
    /* 按需添加其他字重:300(Light)、600(SemiBold)等 */
    body {
        margin: 0 20px;
        font-family: "Open Sans", sans-serif;
        color: #031434;
    }
    /* 其余样式保持不变 */
</style>

3. 在Java渲染代码中手动注册字体

Flying Saucer需要显式注册字体才能正确识别,修改createClaimDetailReport方法:

public ByteArrayInputStream createClaimDetailReport(ClaimDetails claim) {
    ClaimsPdfData claimsPdfData = new ClaimsPdfData();
    List<ClaimDetails> claims = new ArrayList<>();
    claims.add(claim);
    addClaimFormattedDateFromClaimDate(claims);
    claimsPdfData.setClaims(claims);
    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
        String xdata = htmlToXhtml(insertClaimsValuesHtml(claimsPdfData, false));
        ITextRenderer renderer = new ITextRenderer();
        
        // 注册本地字体到渲染器,确保字体嵌入PDF
        renderer.getFontResolver().addFont("/fonts/OpenSans-Regular.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        renderer.getFontResolver().addFont("/fonts/OpenSans-Medium.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        // 对应模板中用到的其他字重,同步注册
        
        renderer.setDocumentFromString(xdata);
        renderer.layout();
        renderer.createPDF(byteArrayOutputStream);
        renderer.finishPDF();
        return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    } catch (IOException | DocumentException e) {
        LOG.error("Error creation pdf ", e);
        throw new RuntimeException(e);
    }
}

关键注意事项

  • 确保字体文件名与代码中的路径完全匹配,区分大小写
  • 每个字重对应单独的字体文件和@font-face定义,避免字重失效
  • 使用BaseFont.EMBEDDED参数将字体嵌入PDF,防止在无字体的环境下回退到默认字体
  • 移除模板中所有远程Google Fonts的<link>标签,避免渲染冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:34:51