# pdfforge **Repository Path**: pdfforge/pdfforge ## Basic Information - **Project Name**: pdfforge - **Description**: 基于 Windows + WPS COM 的办公文件(Word/Excel/PPT/图片/文本)转 PDF 轻量服务,JDK 17 + Spring Boot 3 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-23 - **Last Updated**: 2026-10-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # PdfForge 办公文件转 PDF 服务 —— 基于 Windows + WPS Office COM 自动化,支持 Word / Excel / PPT / 图片 / 文本 转 PDF。 ## 特性 - **高保真转换**:Word/Excel/PPT 通过 JACOB 调用本机 WPS(KWPS/KET/KWPP)COM 接口转换 - **纯 Java 轻量转换**:图片、文本基于 Apache PDFBox,不依赖 WPS - **任意输入输出组合**:S3 对象存储(多存储源)、Base64、multipart 上传 × S3、Base64、响应流 - **同步接口**:无队列、无回调,转换结果即时返回,异步编排交给客户端 - **高可靠**:WPS COM 实例池(默认 CPU/2)+ 存活检测 + 超时保护 + 进程僵死自愈 - **加密文件快速失败**:默认哑密码避免 WPS 弹窗卡死,支持显式传密码尝试 - **格式可配置**:各类别支持的扩展名列表可通过配置覆盖 - **S3 可选装配**:未配置 S3 时服务照常启动,其余功能完整可用 ## 技术栈 JDK 17 · Spring Boot 3.5 · JACOB 1.20 · Apache PDFBox 3.0 · AWS SDK v2 (S3) · Maven 多模块 ## 模块结构 ``` pdfforge ├── pdfforge-api 对外契约:请求/响应对象 + 错误码(JDK 8 可引入) ├── pdfforge-common 错误码 / 异常 / 通用模型 / 工具 ├── pdfforge-core 转换策略引擎 / WPS COM 实例池 / 临时文件管理 ├── pdfforge-storage FileSource·FileSink 抽象 / S3·Base64·multipart 实现 ├── pdfforge-web REST API / 全局异常 / 配置绑定(可执行 jar) ├── pdfforge-postproc PDF 后处理:合并 / 压缩 / 水印(已实现),纯 PDFBox、不调 WPS └── pdfforge-console 管理控制台【尚未创建】:M9 第一批的只读流水查询落在 pdfforge-web 的 console 包,配置在线修改推迟(基线在 yml,增删改无法永久固化) ``` ## 运行前提 - Windows 服务器(64 位),已安装 WPS Office(支持 COM 自动化) - JDK 17 - `jacob-1.20-x64.dll` 与 `com.jacob:jacob:1.20` **主次版本必须配对**,并把 dll 所在目录显式传给 `java.library.path` - JDK 17 上 JACOB 需要反射访问 `java.lang`,必须加 `--add-opens java.base/java.lang=ALL-UNNAMED`,否则加载原生库时报 `InaccessibleObjectException` - JACOB 1.20 不在 Maven Central(阿里云镜像也没有),需自行安装到本地仓库: `mvn install:install-file -Dfile=jacob-1.20.jar -DgroupId=com.jacob -DartifactId=jacob -Dversion=1.20 -Dpackaging=jar` ## 快速开始 ```bash mvn clean package java --add-opens java.base/java.lang=ALL-UNNAMED \ -Djava.library.path="C:/path/to/dir/with/jacob-1.20-x64.dll" \ -jar pdfforge-web/target/pdfforge-web.jar ``` Windows 下可直接用 `scripts\start.bat`(默认值都能用环境变量覆盖),服务化注册见 `scripts/install-service.md`。 上传文件并把 PDF 作为响应流下载(不依赖 WPS 的最小验证可用 `.txt`/图片): ```bash curl -X POST http://localhost:8080/api/v1/convert/upload \ -F "file=@报告.docx" -F 'output={"type":"stream"}' --output 报告.docx.pdf ``` 配置项(临时目录、大小上限、格式列表、实例池、字体库目录、鉴权、S3 存储源、后处理的源数量与总量上限及压缩档位分辨率、流水落盘开关与保留天数)全部在 `application.yml` 的 `pdfforge.*` 下,S3 整段可省略。 ## 接口说明 统一前缀 `/api/v1`,全部同步:响应返回即转换完成。字段定义、curl 示例与错误码见 **[API.md](API.md)**。 | 端点 | 用途 | |------|------| | `POST /api/v1/convert` | 统一转换端点,JSON 描述符指定输入(`s3` / `base64`)与输出(`s3` / `base64` / `stream`) | | `POST /api/v1/convert/upload` | multipart 上传源文件,输出方式由 `output` 描述符指定 | | `GET /api/v1/convert/supported` | 查询生效的格式列表、实例池状态、S3 装配状态、中文字体可用性(本项目不使用 actuator,运行状态看这里) | | `GET /api/v1/convert/fonts` | 盘点服务器装了哪些字体(JVM 字体族名两份 + 字体库目录里的可嵌入字体文件),带 `?names=` 时逐个核对字体名 | | `POST /api/v1/pdf/merge` | 合并多个 PDF(`sources[].pages` 可只取其中几页,如 `1-3,7`),输入/输出沿用同一套描述符;不调 WPS,因此不涉及实例池与超时 | | `POST /api/v1/pdf/watermark` | 给 PDF 加文字或图片水印(可旋转、可平铺、可按页码区间),中文走系统字体库;同样不调 WPS | | `POST /api/v1/pdf/compress` | 给 PDF 瘦身,`mode` 两档:`FIDELITY_FIRST`(只做无损,缺省)与 `SIZE_FIRST`(把过采样的图片降到目标分辨率);压不动时交回原文件并置 `compressed=false` | | `GET /console/v1/records` | 查转换流水(只读,前缀是 `/console/v1` 不是 `/api/v1`):按时间区间/traceId/调用方/操作/成败/错误码过滤并分页,token 需额外具备 `console` 角色 | 本项目不引入 Swagger UI 等在线接口文档框架,[API.md](API.md) 随接口变更同步维护。 > **部署提醒**:鉴权已落地(Bearer token + IP 白名单,见 [API.md](API.md) 的「鉴权」一节),但 `pdfforge.auth.enabled` **默认是 `false`**——这是给本机开发与真机联调留的,部署到内网必须显式置 `true` 并配置 `tokens`,否则等于把 WPS 算力白送内网任何机器。关闭状态下启动日志会打一条醒目 WARN。 ## 性能参考 单机 8 核 + 本机 WPS,走 `s3→s3` 的单请求串行实测(**不含并发与池争用,只是分段耗时基线**): | 阶段 | 首个请求(实例冷启动) | 实例常驻后 | |------|----------------------|-----------| | 借用实例 | 1.0–5.3s,每个组件只付一次 | 0–1ms | | 打开文档 | 50–690ms(19.6MB 的老式 `.doc` 是 692ms) | 20–250ms | | 导出 PDF | 小文件 0.16–1.3s;19.6MB `.doc` 59.7s | 同左,随内容量增长 | | 取源 + 投递(`wall − cost`) | 小文件约 0.1s(S3 往返延迟);19.6MB 约 28.5s | 同左 | 读日志时:`cost` 是服务端计量的纯转换耗时,`wall` 是这次调用的总耗时。大文件先比 `wall − cost`——取源慢与转换慢的处置方式完全不同,混成一个数字就会误判。 ## 文档 - [API.md](API.md) —— 接口契约、请求/响应示例、错误码(随代码开源) - 详细设计文档(架构、决策记录、配置说明)为内部资料,不随本仓库开源 ## 路线图 - [x] 设计文档 v1.3 - [x] v1.0 核心转换(M1–M5:四模块 + 客户端契约模块 + 真实 WPS/MinIO 联调通过) - [x] v1.0 发布(M6:真机压测、启动脚本与 Windows 服务注册说明) - [x] v1.0.x 鉴权(M7:Bearer token + IP 白名单 + 免鉴权路径 + subject 流水归因;`enabled` 默认仍为 `false`) - [x] v1.x PDF 后处理(M8:合并 / 压缩 / 水印三端点,真机验证通过) - [x] v1.x 管理控制台(M9 第一批:**转换日志查询**已实现并真机验证,纯 REST 只读;配置动态修改本轮不做——基线配置在 yml,在线增删改既无法永久固化又会造出第二个真值来源,等这部分配置落库后再议) ## License Apache-2.0