Skip to content

Officia.Words — Word 转 PDF 与模板填充

能力摘要(AI 友好)OfficiaWords 把 Word/DOCX 转 PDF(toPdf),并支持 docx 模板占位符填充与邮件合并(fillTemplate*)。门面静态方法,byte[]byte[] 出,纯 JDK 实现。

将 Word 文档(DOCX,及部分 DOC)转换为 PDF,或以 docx 为模板做数据填充与邮件合并。中文/CJK 通过 CID 子集嵌入正确显示且可复制。

引入

xml
<dependency>
  <groupId>plus.ruoyi</groupId>
  <artifactId>officia-all</artifactId>
  <version>1.0.0</version>
</dependency>

这是唯一需要引的依赖:Maven Central 上只发布 officia-all 这一个构件, 它是含全部能力模块与授权客户端的单一 jar,且无任何第三方传递依赖。

Word → PDF

最小示例:

java
import plus.ruoyi.officia.words.OfficiaWords;

byte[] pdf = OfficiaWords.toPdf(docxBytes);

来源重载 —— 字节 / 输入流 / 文件都能直接调:

java
OfficiaWords.toPdf(byte[] docx);
OfficiaWords.toPdf(InputStream in);
OfficiaWords.toPdf(File file);

需要页数等元信息时用 convert 拿富结果:

java
ConvertResult r = OfficiaWords.convert(docxBytes, ConvertOptions.defaults());
byte[] pdf = r.getData();
int pages  = r.getPageCount();

转换选项

ConvertOptions 链式配置,传给任意带 options 的重载:

java
import plus.ruoyi.officia.engine.api.ConvertOptions;
import plus.ruoyi.officia.engine.api.enums.PageSize;

ConvertOptions opts = ConvertOptions.defaults()
    .setDefaultPageSize(PageSize.A4)
    .setFontDirectory("/usr/share/fonts")
    .setEmbedFonts(true)
    .setMaxImageDpi(150)
    .setTimeoutMillis(30_000)
    .setReportFontSubstitutions(false);

byte[] pdf = OfficiaWords.toPdf(docxBytes, opts);
选项默认说明
defaultPageSizePageSize.A4仅作默认;文档自身声明了纸张时以文档为准
fontDirectorynull追加字体扫描目录,中文场景建议指定
embedFontstrue嵌入字体子集,保证换机器显示一致
maxImageDpi150位图按其在页面上占的面积降采样;设 0 保留源图原始像素
timeoutMillis0(不限)超时保护,处理不可信来源文档时建议设
reportFontSubstitutionsfalse记录字体替换/回退,供排查跨平台版式差异,见下

字体替换诊断

同一份文档在本机正常、部署到 Linux 后版式变了时,用它查清哪个字体被换掉、换成了什么:

java
ConvertResult r = OfficiaWords.convert(docxBytes,
    ConvertOptions.defaults().setReportFontSubstitutions(true));

for (FontSubstitution s : r.getFontSubstitutions()) {
    System.out.println(s.describe());
    // 字体「宋体」字体不存在,实际使用「noto sans sc」
}

FontSubstitution 提供 requestedFamily(文档要什么)、bold / italic(哪个字面)、 resolvedFamily(实际用了什么)与 reasonreason 分两种,处置方式不同:

reason含义处置
FAMILY_NOT_FOUND整个字体在运行环境中不存在安装字体或配置 fontDirectory
GLYPH_NOT_IN_FAMILY字体存在,但不含该字形(如拉丁字体遇到汉字)正常的字形级回退,一般无需处理

记录按「族名 + 字重 + 倾斜」去重,数万字文档也只有数条。默认关闭(收集有开销), 排查时再开启;开启不会改变转换结果。

关于 maxImageDpi

文档里的图片像素数往往远超它在页面上占的面积,多出来的部分对观感没有贡献,只增加体积。 默认 150 dpi 在屏幕阅读与一般打印下看不出差别,却能显著压缩产物。只有高精度印刷或产物 需要二次编辑时才设 0。JPEG 源图始终按原字节直通嵌入,不受该选项影响。

Word → 图片(一页一张)

用于文档在线预览:图片在任何浏览器与移动端 webview 里都能直接 <img> 显示, 不依赖 PDF 阅读器。返回顺序即页码顺序。

java
List<byte[]> pages = OfficiaWords.toImages(docx);        // 默认 96 DPI PNG

// 指定 DPI 与格式
List<byte[]> jpgs = OfficiaWords.toImages(docx, null,
        ImageRenderOptions.defaults().dpi(150).format("jpg"));

ImageRenderOptionsplus.ruoyi.officia.render.image):

方法默认说明
dpi(int)96取值 36–600,越界抛异常而非静默钳位
format(String)pngpng / jpg / jpeg
backgroundRgb(int)页面底色
antiAlias(boolean)true抗锯齿
watermark(...)自定义水印,见下

水印

水印烧进像素,读者另存图片后仍然带着。

java
// 最简:对角灰字
OfficiaWords.toImages(docx, null,
        ImageRenderOptions.defaults().watermark("机密 · 仅供内部"));

// 平铺:防止截图裁掉水印
OfficiaWords.toImages(docx, null, ImageRenderOptions.defaults()
        .watermark(WatermarkOptions.text("张三 · 2026-08-28 · 禁止外传")
                .tile(true)
                .fontSizePt(13)
                .opacity(0.13f)));
方法默认说明
text(String)必填空字符串抛异常
fontSizePt(float)0 = 自动自动时单个按页宽 1/14、平铺按 1/40;上限 400
colorRgb(int)0x8080800xRRGGBB
opacity(float)0.150–1
rotationDegrees(float)-38逆时针为正,0 为水平
tile(boolean)false整页平铺,用于防截图裁剪
fontFamily(String)自动不指定时按水印文字自动挑覆盖该字符集的字体

中文水印无需自备字体

水印由渲染器在出图时画,用的是库内部的字体栈(含内置中文兜底), 所以中文水印不会变方框,你也不用传字体文件。

要给非 Officia 产出的图片加水印才用 OfficiaImaging.textWatermark

什么时候该用图片、什么时候该用 PDF

桌面浏览器(Chrome / Edge / Firefox / Safari)都内置 PDF 阅读器、不需要插件, 直接给 PDF 更省体积、文字还能选中搜索。

图片的优势场景是:移动端 webview(微信 / 钉钉 / 企业微信内置浏览器对 PDF 支持差)、 需要防止读者直接拿到可编辑原件、或要把水印烧进像素。

体积远大于 PDF

实测 A4 三页合同(含表格与中英混排):96dpi 约 109 KB/页、150dpi 约 198 KB/页, 而同内容 PDF 全文仅 145 KB。在线预览请考虑懒加载。

当前尚未覆盖的图元

图案填充的真实平铺(按等效纯色近似)、下划线 / 删除线 / 高亮、行内图片、 图片旋转裁剪变换、水平分隔线。文本、纯色填充、表格边框、内嵌图片、定位文本框已覆盖。

水印方面:只支持文字水印,不支持图片水印;一份水印设置作用于全部页面, 不支持逐页不同的动态水印。

Markdown → PDF / docx

Markdown 是 Officia.Words 的一种输入格式(与 docx / doc 并列),无需引入额外模块。

java
// Markdown 文本 → PDF(版式由排版引擎定版)
byte[] pdf  = OfficiaWords.markdownToPdf("# 标题\n\n正文**加粗**");
// Markdown 文本 → 可编辑 docx
byte[] docx = OfficiaWords.markdownToDocx("# 标题\n\n正文**加粗**");

// 字节 / 文件 / 输入流重载(编码自动判定:BOM → 严格 UTF-8 → GBK 回退)
byte[] pdf2 = OfficiaWords.markdownToPdf(new File("readme.md"));
// 流式:边生成边写盘,返回页数
int pages = OfficiaWords.markdownToPdf(new File("in.md"), new File("out.pdf"));

支持 CommonMark 常用子集 + GFM 表格:标题(带大纲级别,转 PDF 时成为书签)、段落(中文软换行不插空格)、 任意层级嵌套列表、围栏代码块、引用块、GFM 表格三种对齐、分隔线、YAML front matter → 文档属性, 以及加粗 / 斜体 / 删除线 / 行内代码 / 链接 / 自动链接 / 反斜杠转义。

当前边界

转 PDF 的段落缩进、表格边框、代码块外框均已生效;转 docx 目前只写字符级样式 (文字、加粗斜体、字号、等宽字体与底纹完整),段落缩进、边框与表格结构尚未写出,表格会被平铺为段落—— docx 写侧补齐后自动生效,调用方代码无需改动。

列表编号为展平文本前缀(非 Word 自动编号);图片 ![]() 降级为「[图片] 替代文字」+ 指向原地址的链接, 不会下载外链图;缩进代码块(4 空格)请改用围栏写法。

模板填充

以 docx 为模板,用占位符 + 数据生成填充后的 docx 或直接出 PDF。在 OOXML DOM 上就地替换、保留原样式。

java
// 单份填充 → docx
byte[] docx = OfficiaWords.fillTemplate(templateBytes, data);
// 单份填充 → 直接出 PDF
byte[] pdf  = OfficiaWords.fillTemplateToPdf(templateBytes, data);

邮件合并(一模板多数据)

java
// 每条数据各出一份,返回多份 docx
List<byte[]> docs = OfficiaWords.fillTemplateEach(templateBytes, dataList);
// 每条各出一份并转 PDF
List<byte[]> pdfs = OfficiaWords.fillTemplateEachToPdf(templateBytes, dataList);

// 多条数据合并进一份文档(连续多页)
byte[] mergedDocx = OfficiaWords.fillTemplateMerged(templateBytes, dataList);
byte[] mergedPdf  = OfficiaWords.fillTemplateMergedToPdf(templateBytes, dataList);

方法一览

方法说明返回
toPdf(docx)Word → PDF(字节 / 流 / 文件重载)byte[]
toImages(docx)Word → 图片,一页一张(字节 / 流 / 文件重载)List<byte[]>
convert(docx, options)转换并返回富结果(含页数)ConvertResult
markdownToPdf(md)Markdown → PDF(字节 / 流 / 文件重载,含流式)byte[]
markdownToDocx(md)Markdown → 可编辑 docx(同上重载)byte[] (docx)
convertMarkdown(md, options)Markdown → PDF 并返回富结果ConvertResult
fillTemplate(tpl, data)单份模板填充byte[] (docx)
fillTemplateToPdf(tpl, data)单份填充并转 PDFbyte[] (pdf)
fillTemplateEach(tpl, list)每条数据各出一份List<byte[]> (docx)
fillTemplateEachToPdf(tpl, list)每条各出一份并转 PDFList<byte[]> (pdf)
fillTemplateMerged(tpl, list)多条合并进一份byte[] (docx)
fillTemplateMergedToPdf(tpl, list)多条合并并转 PDFbyte[] (pdf)

常见问题

  • 中文变方块 / 不可复制:Officia 通过 CID Identity-H 子集嵌入字体,正常不会出现;若自定义字体缺字,确认字体覆盖了所需字符集。
  • 未授权水印:评估态输出带水印并限页,加载 officia.lic 后消失(见 授权与加载)。
  • 异常:入参为空或格式非法抛 OfficiaException(非受检),不会向外抛底层 IOException