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

如何为Distroless容器添加字体解决Jasper Reports字体缺失报错

在Distroless容器中为Spring Boot + Jasper Reports解决字体缺失问题

我正在开发一个基于Spring Boot的应用,使用Jasper Reports生成PDF报表,但在Distroless容器中遇到了字体缺失问题。Distroless容器默认不包含任何字体,而Jasper Reports运行依赖系统中存在可用字体。

此前查阅的方案均针对带有包管理器的Docker镜像(如Alpine),无法直接应用在无包管理器、无Shell的Distroless环境中,包括:

  • 《Docker and Java - FontConfiguration issue》:针对Alpine发行版的方案
  • 《Docker and Spring boot giving "sun.awt.FontConfiguration.head" is null》:需要执行RUN apk add命令,无法在Distroless中运行
  • 某Stack Overflow回答:非推荐方案

报错信息

无可用字体时会抛出以下异常:

java.lang.NullPointerException: Cannot load from short array because "sun.awt.FontConfiguration.head" is null
        at java.desktop/sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)
        at java.desktop/sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:224)

或

java.lang.RuntimeException: Fontconfig head is null, check your fonts or fonts configuration
        at java.desktop/sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1271)

已尝试的方法

我尝试从带有字体的Ubuntu构建镜像中复制字体到Distroless镜像,Dockerfile内容如下:

FROM myregistry:5000/xyz/builder-base:mytag as BUILDER
# ubuntu image with fonts in it
# do some stuff here, build artifacts
FROM myregistry:5000/xyz/mydistroless:mytag
# Copy app.jar
COPY --from=BUILDER /build/target/myapp.jar /opt/iamboss/app.jar
COPY --from=BUILDER /usr/share/fonts /usr/local/share/fonts
## Run my app
ENTRYPOINT ["java", "-jar","app.jar"]

但问题仍然存在。由于Distroless镜像没有包管理器和Shell,无法执行fc-cache更新字体缓存,根据Debian字体相关规范,字体放在/usr/local/share/fonts且权限为644(-rw-r--r--)时无需执行该操作,但依旧无效。

可行解决方案

方法1:复制字体到自定义目录并指定JVM参数

Java可通过系统属性指定字体目录,无需依赖系统字体缓存。步骤如下:

  1. 在构建阶段(BUILDER镜像)准备好需要的字体文件(比如从Ubuntu镜像复制,或直接下载开源字体如Noto Sans)
  2. 将字体复制到Distroless镜像的自定义目录,比如/opt/fonts
  3. 在启动命令中添加JVM参数,指定字体路径并禁用系统字体配置检查:
FROM myregistry:5000/xyz/builder-base:mytag as BUILDER
# 示例:安装Noto字体并复制到构建镜像的/fonts目录
RUN apt-get update && apt-get install -y fonts-noto && cp -r /usr/share/fonts/truetype/noto /fonts

FROM myregistry:5000/xyz/mydistroless:mytag
COPY --from=BUILDER /build/target/myapp.jar /opt/iamboss/app.jar
COPY --from=BUILDER /fonts /opt/fonts
# 添加JVM参数指定字体目录,同时禁用fontconfig检查
ENTRYPOINT ["java", "-Djava.awt.headless=true", "-Dsun.awt.fontconfig=/dev/null", "-Dfont.dir=/opt/fonts", "-jar","/opt/iamboss/app.jar"]

说明:-Dsun.awt.fontconfig=/dev/null会让Java跳过系统字体配置文件的读取,直接使用指定目录下的字体;-Djava.awt.headless=true确保Spring Boot在无图形环境的容器中正常工作。

方法2:在Jasper Reports模板中嵌入字体

直接在Jasper Reports的报表模板中嵌入字体,无需依赖系统字体:

  1. 将字体文件(如.ttf)添加到项目资源目录(比如src/main/resources/fonts)
  2. 在Jasper Reports的字体配置文件(font.xml)中定义该字体:
<fontFamilies>
    <fontFamily name="Noto Sans">
        <normal>fonts/NotoSans-Regular.ttf</normal>
        <bold>fonts/NotoSans-Bold.ttf</bold>
        <italic>fonts/NotoSans-Italic.ttf</italic>
        <boldItalic>fonts/NotoSans-BoldItalic.ttf</boldItalic>
        <pdfEncoding>Identity-H</pdfEncoding>
        <pdfEmbedded>true</pdfEmbedded>
        <exportFonts>
            <export key="pdf">Noto Sans</export>
        </exportFonts>
    </fontFamily>
</fontFamilies>
  1. 在报表模板中引用该字体,编译报表时Jasper会将字体嵌入到PDF中,彻底脱离对系统字体的依赖。

方法3:构建预生成字体缓存的自定义Distroless基础镜像

如果需要多个应用共用字体,可提前在有包管理器的镜像中生成字体缓存,再复制到Distroless镜像:

# 第一步:在Ubuntu镜像中安装字体并生成缓存
FROM ubuntu:latest as FONT_BUILDER
RUN apt-get update && apt-get install -y fonts-noto fontconfig
RUN fc-cache -fv

# 第二步:构建自定义Distroless镜像
FROM myregistry:5000/xyz/mydistroless:mytag
COPY --from=FONT_BUILDER /usr/share/fonts /usr/share/fonts
COPY --from=FONT_BUILDER /var/cache/fontconfig /var/cache/fontconfig
COPY --from=FONT_BUILDER /root/.cache/fontconfig /root/.cache/fontconfig

后续应用镜像基于这个自定义Distroless镜像构建,即可直接使用系统字体。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:31:09