跳到正文

开发规约 ​

本规约面向 Java 实现、测试、示例和构建配置。当前已建立 10 个 Gradle 模块,按以下要求持续开发。

技术与构建 ​

  • 使用 Java 21 LTS 作为初始基线,正式采用依赖前验证兼容性。
  • 使用 Gradle 多模块与 Kotlin DSL,提交完整 Gradle Wrapper。
  • Java 版本通过 toolchain 统一,源码编码统一为 UTF-8。
  • Java 根包名为 com.mirakyux.export4j,对应 src/main/java/com/mirakyux/export4j;测试采用相同包结构,Gradle 项目 group 与根包名一致。
  • Gradle 版本和插件版本固定;第三方依赖通过 version catalog 集中管理。
  • 对外模块由 export4j-bom 对齐版本;对外发布坐标在确认命名空间后记录,不猜测 Maven Central 权限。
  • 核心只引入必要依赖。新增框架或工具应说明用途、替代方案和维护成本。

仓库内容 ​

文档、示例和验证命令使用仓库相对路径;外部工具使用 PATH 中的命令或明确标注的配置项。不得写入个人目录、机器标识、真实凭据、会话授权和代理分工。资源与样例只使用可公开的内容。

构建输出、日志、私有配置和签名材料应放入 .gitignore 覆盖的目录。忽略规则只影响尚未跟踪的文件;已提交的敏感内容仍存在于 Git 历史,删除工作区文件不能清除历史。

构建命令 ​

Windows 使用 .\gradlew.bat build,Linux/macOS 使用 bash ./gradlew build。根 build 聚合全部模块;根 test 仅运行测试,:examples:run 运行精确绘制示例,:examples:textPdf 运行中英文行布局示例,:examples:flowPdf 运行语义段落自动分页报告,:examples:tablePdf 运行跨页表格报告,:examples:componentPdf 运行基础交互报告,:examples:advancedPdf 运行综合报告,:examples:benchmark 记录大文档布局基线。

常用任务和输出目录集中在示例索引。本地完整制品与调用方验证可执行:

powershell
.\gradlew.bat build sbom publishLocalBundle
powershell -NoProfile -File scripts/verify_local_consumer.ps1

独立格式和渲染检查使用 Python 3.11+、隔离环境中的固定验证依赖及 Poppler;PDF/A、签名或应用兼容检查另按任务准备 veraPDF、OpenSSL、Word 等工具。这些工具不进入库运行依赖,验证入口按验证矩阵选择。

GitHub Actions由推送 main、拉取请求及手动触发,覆盖 Java 21 三平台构建、API 检查、坐标消费、无打印依赖运行和临时密钥签名验证。实际远端结果、平台架构及未覆盖范围见阶段一百零五。

固定语料使用 python scripts/create_compatibility_corpus.py 生成独立 OOXML 正/负控制,再运行消费者 compatibilityCorpus,最后用 scripts/verify_compatibility_corpus.py 独立检查内容、损失和双渲染。生成器要求新输出目录,避免复用残留文件。运行依赖公告门禁使用 scripts/test_dependency_audit.py 与 scripts/audit_dependencies.py,后者需要 OSV 网络访问。完整命令、依赖升级和本批新 CI 尚未远端执行的边界见阶段一百零六。

Java 编译启用 -Xlint:all -Werror。依赖版本目录位于 gradle/libs.versions.toml,模块锁文件固定实际解析版本;有意升级时通过 --write-locks 更新,并同步依赖与许可证记录。

源码组织 ​

生产代码位于 src/main/java,测试位于 src/test/java。资源分别放入对应的 resources 目录,测试输出进入构建目录。

固定字体集中保存在 assets/fonts,由示例和相关模块的测试资源集引用。核心库不打包示例字体。更新资源时核对字节哈希、来源、嵌入权限和同目录许可文件。

包按领域职责组织,避免无边界的 util、common 和超大管理类。模块、文件与类型名称应表达职责;外部制品名称使用 export4j-*。

公共扩展接口与内部实现分开。默认采用最小访问范围,不为便于测试而扩大生产 API 的可见性。

代码风格 ​

  • 使用 4 个空格缩进,不使用制表符;文件采用 UTF-8 和 LF。
  • 类型使用 UpperCamelCase,方法和变量使用 lowerCamelCase,常量使用 UPPER_SNAKE_CASE。
  • 使用显式导入,不使用通配符导入。
  • 优先简单、清晰的代码;var 仅用于类型可直接识别的局部变量。
  • 默认不使用预览特性,不引入 Lombok 隐藏模型行为。
  • 删除未使用导入、局部变量和不可达代码。泛型、空值和资源警告应从契约与实现上修复。
  • @SuppressWarnings 仅能限定在必要的最小范围,并解释原因。
  • 注释解释单位、算法依据和取舍,不重复代码字面含义。

新增自研 Java 文件使用以下许可证标识;有版权声明时填写真实权利信息:

java
// SPDX-License-Identifier: Apache-2.0

数据模型与公共 API ​

公共编写和配置 API 优先提供 builder()、具名设置与 build();必填业务标识可作为 Builder 工厂参数。嵌套结构使用同步 Consumer<Builder>,回调返回时取快照,不保留可变子 Builder。可复用样式提供 toBuilder()。保留已有短构造器和静态工厂,不对结果、绘制指令、异常或 SPI 机械添加构建器。详细约定见链式 API和 ADR-0018。

值对象优先不可变。record 不自动保证深度不可变,集合、数组和可变对象仍需防御性复制或明确所有权。

必填参数在边界验证。返回集合使用空集合表示无元素;可缺省的单一返回值使用 Optional,不将 Optional 用作字段或参数。特殊的空值语义须在契约中明确说明。

公共 API 与 SPI 的 Javadoc 至少说明:

  • 参数、返回值、计量单位、坐标方向和基线含义。
  • 可缺省行为、数值范围和输入校验。
  • 资源所有权、关闭行为及线程安全。
  • 可预期异常、不支持能力和降级行为。

对开放扩展点使用接口。只有确实封闭的内部层次才采用 sealed,避免限制第三方后端或组件扩展。

公共签名、异常和返回对象不得引用 PDFBox 等实现类型。检查这一约束时,也要检查公共泛型参数和父类型。

数值与文字处理 ​

长度和坐标遵守架构约定。避免使用含义不明的裸数字;转换在输入输出边界集中处理,不在布局代码中散布毫米、像素与 point 换算。

几何测试采用有依据的误差范围,注明绝对误差或相对误差,不以统一的大误差掩盖算法问题。像素分辨率必须与逻辑长度区分。

不得按 Java char 数量推断文字宽度或字形数量。测试应覆盖中文、混排、代理对、组合字符和缺字处理;复杂脚本的支持范围以实际实现与验证为准。

布局和绘制应消费一致的字体、字形及度量。后端不得用默认字体再次测量并改变排版。

当前基础文本接口见字体和文本 API。行高表示固定基线间距,字体栈统一上升与下降高度。换行使用 JDK ROOT 区域设置规则,测试记录 JDK 版本;极窄区域的紧急断行不承诺完整中文禁则。

分页接口见文档布局 API。区分语义段落、段落内行布局和页面上的段落片段。跨页保留原文来源;数值边界只允许浮点舍入量级的容差,不以缩小或裁切代替不可容纳内容的失败。

混合内容统一遍历 Document.blocks() 和 LayoutPage.elements()。表格单元格来源使用块索引、行索引、列索引与 UTF-16 范围定位;重复表头保留原始来源并单独标记。不得用只含段落的兼容视图绘制完整文档。表格约束与预算见表格 API。

组件遵循核心 SPI:使用局部左上角坐标,返回独立平衡的指令快照和诊断。普通正文组件在整页内容区域测量一次,PageableComponent 返回有序非空片段,首片段可使用当前剩余高度;不能读取最终页码改变尺寸。页区组件在最终页数确定后测量;定位区域必须位于父区域内。自定义指令超出视口时不会自动裁剪,实现者应显式裁剪。

先通过 PageTemplate.bodySettings 预留页眉页脚,再分页正文。分节使用最大页区,区分显示页码与物理页索引。脚注预留与续页必须有界,并为当页新增脚注保留首行;多行引用在最终分页前预留尺寸。目录和书签按最终页偏移解析。表单同时检查字段树、widget、有效值和外观流,并独立填写、保存和重开。详细契约见组件 API与生产能力 API。

异常与诊断 ​

入口参数错误应明确指向无效参数。资源读取、布局和导出失败保留原始异常原因,增加阶段与资源上下文。

禁止吞异常、空 catch、无上限重试和“失败后返回成功”。部分输出不等于成功;失败后输出流及生成文件的状态应在契约中说明。

诊断使用稳定代码与结构化位置,面向人的消息可调整。调用方可接受的降级和不可恢复错误应有明确区分。

库默认不向标准输出打印信息,不自行设置调用方的全局日志配置,也不记录完整正文或凭证。FontGroup 的严格/占位策略必须贯穿正文、目录、页区和嵌套组件;替换输出结构化 WARNING,原字符码点与 UTF-16 位置不变,由调用方决定如何打印警告。表单值缺字保持失败。

资源、并发与安全 ​

自身创建的文件、后端文档和解码资源使用 try-with-resources 管理。借用的调用方流默认不关闭;所有权转移必须显式约定。

可变构建器和文档级上下文默认限定在单个任务内。不可变布局结果可在契约允许时共享;共享缓存必须有容量、同步、失效和释放策略。

输入文件、字体、图片及压缩内容按不可信数据处理。限制应包含单位、默认行为和超限诊断,并在大输入或恶意输入上验证。

默认不自动下载字体或远程图片,不执行宏或脚本。导入模块防止压缩炸弹、路径穿越和非预期外部实体解析。

生成、导入、编辑及转换不得依赖打印机驱动、打印服务或 Office/浏览器进程;普通路径支持无桌面 JVM,字体和资源由调用方提供。外部应用只用于独立兼容性验收。运行依赖变化时执行 scripts/verify_driverless.py,核对生产 JAR、实际运行库摘要及拒绝加载打印类后的生成/转换,详见架构约束。

测试策略 ​

变更类型应提供的验证
纯文档链接、标题结构、术语和事实状态
模型与校验有效输入、无效输入、不可变性和公共契约
几何与绘制坐标、变换、裁剪、状态恢复和输出结构
字体与文本测量与输出一致、Unicode 映射、缺字和回退
布局与分页行框与边界、内容顺序、无丢失重复、分页推进与超大内容
格式适配声明支持范围内的往返、降级诊断与失败行为
资源与并发正常和失败路径释放、预算限制及任务隔离

使用 JUnit 建立自动化测试。断言可观察行为、算法边界和真实回归,不只验证访问器或复制实现步骤。

PDF 输出结合文件结构、文本提取、字体与图片资源检查。阶段验收还应使用独立 PDF 工具解析或渲染;只用生成后端自行读回不足以证明正确。

视觉回归固定渲染器版本、字体、分辨率和资源。运行 python scripts/verify_advanced_pdf.py 检查结构、交互填写保存、Poppler/PDFium 渲染和仓库 PNG 基线;依赖由 scripts/requirements-verification.txt 固定。说明差异阈值及原因,全部页面检查后才显式 --update-baseline,不能只为通过测试批量刷新基线。环境与边界见进阶验收。

性能数据记录样本文档、字体资源、JDK、硬件、预热、并发和测量方法。先建立基线,再制定目标;不预设未经测量的吞吐量或内存指标。

生产能力验证使用 build sbom publishLocalBundle :examples:production、scripts/verify_local_consumer.ps1 和 python scripts/verify_production.py。独立检查全部新增 PDF 页面,并核对 DOCX 语义、静态渲染、实际 SBOM 哈希和 BOM 依赖。JDK java.desktop 可 headless 运行,但复杂文字只使用显式字体。本地 Maven 目标为 build/repository。

完成条件 ​

一次变更交付前应满足:

  1. 行为与任务验收一致,模块依赖保持无环。
  2. 公共 API、资源所有权和失败行为有明确契约。
  3. 已执行相关验证,新增警告已处理。
  4. 新增依赖、资源及其许可证信息已登记。
  5. 文档、示例和 ADR 与最终实现一致。
  6. 交付说明提供实际命令、结果及未验证范围。

环境缺失或命令失败时记录原因。静态检查、单元测试、独立工具验证和正式发布是不同结论,不互相替代。