HotPDF 中文更新日志

HotPDF 面向最终用户的版本历史,包含功能增强、修复、兼容性更新和文档变化。

Version 2.137.6

  • 新增 THotPDF.DACopyFile,面向只需要页数和原样字节保存副本的大文件 workflow,避免构建完整 Normal API 对象图。
  • 改进基于文件的 encrypted PDF 加载路径:password open 不再为了恢复 raw encryption dictionary 值而缓存整份源 PDF。
  • 减少 AES-256 stream 解密时的内存复制,尽量直接从已有 buffer 解密,包括大尺寸 encrypted image stream 的已加载 stream memory。
  • HugeFileBenchmark demo 新增 direct-copy 行,同时默认操作集仍覆盖完整 LoadFromFile + SaveLoadedDocument 路径。

Version 2.137.5

  • 新增 Direct File API 的 object-source 编辑能力:应用可用 DAOpenFileReadOnly 打开 PDF,读取对象数量和对象源码,替换或追加对象正文,更新文档信息,并保存为全量重写副本。
  • 扩展 Direct File API 索引能力,覆盖传统 xref table 与 xref stream,包括 compressed object entry,使全图像大 PDF 和超多对象 PDF 都能走轻量路径。
  • 优化 Direct File API 的全量重写保存:未修改的直接对象按源文件偏移顺序复制,并合并连续区间,显著减少数十万对象 PDF 的随机 I/O。
  • HugeFileBenchmark demo 新增 direct-rewrite 操作,并补充全图像与图文混合巨大 PDF 的 HTML/CSV benchmark 说明。

Version 2.137.4

  • 将 Delphi PreflightReport demo 从 console sample 升级为交互式 VCL GUI 工具,用于单文件 preflight workflow。
  • GUI 现在覆盖 standard text、JSON、HTML report,可选 password、profile INI、内置 preset、单文件 PDF/VT validation、embedded-report PDF 输出、report preview、status logging、automatic output naming,以及快速打开生成文件。
  • 移除旧的启动行为:未提供 command-line argument 时不再自动创建 sample PDF 和 report。

2026-05-26 Version 2.137.3

  • PDF/VT validation 现在同时识别以 RDF attribute 形式和 element text 形式写入的 XMP property,覆盖常见的 pdfvtid:GTS_PDFVTVersionpdfvtid:GTS_PDFVTModDatexmp:ModifyDate packet 样式。
  • 新增 Delphi regression coverage,确保 attribute-style PDF/VT metadata detection 与其余 structural PDF/VT checks 分开验证。

2026-05-26 Version 2.137.2

  • 改进 embedded preflight report validation:source PDF 在 %%EOF 后合法保留 final line break 时,也能继续匹配已存 fingerprint。
  • Preflight profile 以 UTF-8 BOM 保存且首行为 section header 时,现在可正确加载。
  • 加固 PDF literal string escape decoding,改善 owner 或 user entry 带转义字节的 encrypted PDF password validation 与 unencrypted-copy 输出。

2026-05-26 Version 2.137.1

  • 将 CFF parser 的安全处理扩展到 direct byte-array check:malformed input 后恢复 reader position,并在 failed check 时清理 stale layout state。
  • 加固 C++Builder wrapper-compatible CFF stream loader:nil stream input 会返回 False 并重置 wrapper state,而不是解引用该 stream。

2026-05-26 Version 2.137.0

  • 新增 THotPDF.ValidatePDFVT 及 password-aware overload,用于聚焦 PDF/VT validation。文本报告会检查 XMP PDF/VT claim、metadata namespace、PDF/VT modification date matching、PDF/X base marker、output intent、catalog DPartRoot structure、loadable pages 与 page-level DPart coverage。
  • 独立 HotPDFPreflight CLI 新增 --pdfvt 参数,可为单个 PDF 或递归目录扫描写出 .pdfvt.txt validation report。

2026-05-26 Version 2.136.8

  • 改进 Type 1 PFB 与 CFF parser 的失败处理:malformed stream check 会保留输入位置,并清除旧元数据,避免 failed check 后仍暴露上一次解析得到的字体名。
  • 补充 Type 1/CFF smoke 覆盖,加入 malformed CFF 与 Type 1 stream,检查失败调用后的 stream-position preservation 与 stale-metadata cleanup。

2026-05-26 Version 2.136.7

  • 加固 Type 1 PFB 与 CFF parser 入口:stream-based check 会保留调用方 stream 位置,遇到 malformed data 时安全返回失败,并通过 Delphi 与 C++Builder wrapper 路径暴露基础 Type 1/CFF 字体元数据。
  • 扩展 Type 1/CFF smoke test,加入 in-memory CFF data、ASCII Type 1 font data、wrapper-level PFB file loading 与 wrapper-level CFF stream loading,覆盖 Delphi 和 C++Builder 两侧集成面。

2026-05-26 Version 2.136.6

  • 新增 Delphi 功能示例,覆盖 CJK Unicode 文本输出、大型多页文档生成、PFX 签名,并扩展图像压缩示例以输出 CCITT Group 3 1D 与 2D 文件。

2026-05-26 Version 2.136.5

  • Preflight 报告现在遇到已加载页面无法返回 MediaBox 时会继续生成报告。该页会标记为 unavailable,而不是中断整份诊断输出,因此大型或不一致的页树仍可得到报告。
  • 优化多处 byte-level preflight 扫描,避免反复复制文件尾部字符串和线性查找重复对象号。10,000 页 implementation-limit 样本现在可在 120 秒压力测试预算内完成。
  • Preflight 压力 runner 的默认单文件超时提高到 120 秒;PreflightReport demo 在 profile-aware 输出路径后也可以干净重编。

2026-05-26 Version 2.136.4

  • 新增 preflight cookbook、自动生成的 preflight API map,以及面向 HotPDFPreflight 命令行工具的大目录压力测试 workflow。压力 runner 对每个 PDF 设置单文件超时,并记录 JSON、Markdown、CSV、report 和 log 证据。
  • 修复 preflight fingerprint helper 的完整重编译问题:在 CreatePreflightReport 使用该 helper 前先声明它,因此从源码重编 HPDFDoc.pas 的 console tools 可以干净编译。
  • 记录了基于 D:\PDFdoc\PDF-Samples 的 1000-file baseline:997 passed、1 failed、2 timed out、耗时 111.422 seconds,吞吐 8.975 files per second。

2026-05-25 Version 2.136.3

  • 独立 HotPDFPreflight CLI 工具新增 --aggregate <file> 选项,per-file 报告写完后调用 THotPDF.AggregatePreflightReports 输出批量汇总到指定文件。汇总列出每个处理的 PDF 的状态、大小、警告数加总计。
  • 支持单文件和递归目录扫描,可与 --profile / --preset 协同使用。汇总基于每份报告的 text body 计算,profile 过滤的效果会反映到汇总里。
  • 示例:HotPDFPreflight C:\Archive -r --preset silent-actions -f json -o C:\Archive\reports --aggregate C:\Archive\Aggregate.txt 对每个 PDF 输出 JSON 报告并写一份批量汇总。

2026-05-25 Version 2.136.2

  • 独立 HotPDFPreflight CLI 工具新增 --profile <file>--preset <name> 选项,在写每份报告前先经 preflight profile 过滤。与既有 -f text|json|html-p <password>-r 递归目录扫描协同使用。
  • 两个新选项互斥(同时给定时后者胜)。preset 值与 THotPDF.GetBuiltInPreflightProfile 识别的名称一致,CI pipeline 无需上传 INI 文件即可使用标准配置。
  • 示例:HotPDFPreflight C:\Archive -r --preset silent-actions -f json -o C:\Archive\reports 对目录树中每个 PDF 输出 JSON 报告,并应用 silent-actions 预设。

2026-05-25 Version 2.136.1

  • Delphi PreflightReport demo 现在接受 profile=<file>preset=<name> 作为额外 CLI 参数,在写报告前先经 profile 过滤。与既有 json / html / embed 输出格式 flag 协同使用。
  • 示例:PreflightReport.exe Input.pdf Report.txt "" text profile=tuned.inipreset=compact / preset=silent-actions
  • preset 取值与 THotPDF.GetBuiltInPreflightProfile 识别的名称一致,所以无需自己写 INI 文件即可在 demo 中试用 profile。

2026-05-25 Version 2.136.0

  • 新增 THotPDF.MergePreflightProfiles 支持 profile 分层。结果是两个输入的去重并集(DisableChecksDisableWarnings 合并去重,DisableHints 取逻辑或),适用于把 compact 之类预设与项目特定微调组合。
  • 新增 THotPDF.DiffPreflightProfiles 做 profile 结构对比。两个 profile 等价时返回 True,否则 OnlyInAOnlyInB out 参数以换行分隔列出每侧独有的条目,形如 check:<name> / warn:<name> / option:hints=false
  • 两个新 helper 配齐 profile 生命周期:Load / GetBuiltIn / Save / Apply / Validate / Merge / Diff

2026-05-25 Version 2.135.0

  • 新增 THotPDF.CreatePreflightReportWithProfile 一站式便捷 wrapper,把 CreatePreflightReportLoadPreflightProfileApplyPreflightProfile 和格式转换器组合为单一调用。
  • 新 overload 接受源 PDF、可选密码、可选 profile 文件、目标格式(pfText / pfJSON / pfHTML),返回最终报告 body。传入空 ProfileFile 跳过 profile 步骤,调用方无论是否配置了 profile 都可保持单一调用点。
  • 底层四个 API 保持可用,既有调用链不受影响;新 wrapper 纯粹增量。

2026-05-25 Version 2.134.0

  • 新增 THotPDF.SavePreflightProfileTHPDFPreflightProfile 记录写回 INI 文件。输出格式与 LoadPreflightProfile 消费的一致,两个函数对良构 profile 是精确的逆操作。
  • 支持 "加载预设、调整、保存" 工作流:用 v2.133.0 GetBuiltInPreflightProfile 取内置预设,修改列表或 flag,然后 SavePreflightProfile 写出供后续复用或跨项目共享。
  • 无内容的 section 不输出,空 profile 仅产生一行注释而不是三个空 section header。

2026-05-25 Version 2.133.0

  • 新增 THotPDF.GetBuiltInPreflightProfile,为常见工作流返回开箱即用的 THPDFPreflightProfile 记录,调用方无需为标准配置维护 INI 文件。
  • 识别名称(大小写不敏感):default 或空字符串返回空 profile;compact 屏蔽所有 Hint 行以缩短报告;silent-actions 屏蔽所有 PDF 1.7 §12.6.4 action warning 加 EmbeddedFileRichMedia,适用于有意嵌入多媒体或交互动作的工作流。
  • 未知名称也返回空 profile,函数永不因 typo 抛错;如需让 typo 报错,配合 v2.132.0 ValidatePreflightProfile 使用。

2026-05-25 Version 2.132.0

  • 新增 THotPDF.ValidatePreflightProfile 用于防御性 profile 检查。函数遍历已加载 profile 的 DisableChecksDisableWarnings 列表,标记当前 preflight 实现无法识别的名称,并将未知名称用 ', ' 连接放入 UnknownNames out 参数。
  • 用于检测针对其他 HotPDF 版本编写的 profile 文件。不验证时,typo(如 OpneAction 而非 OpenAction)或被重命名的 check 会静默 disable 任何东西。
  • 该检查纯防御性,不修改 profile 本身。调用方自行决定是中止、记录 warning 还是继续使用部分生效的 profile。

2026-05-25 Version 2.131.0

  • 新增 THotPDF.ConvertPreflightReportToVeraPDFStyle 转换器,把 HotPDF preflight 报告塑形为参照 veraPDF 验证输出结构的 JSON 文档:顶层 profilejobs 数组(含 itemDetails / taskResult / validationResult),以及 validationResult.details 下的 ruleViolations 数组。
  • HotPDF 风格而非与 veraPDF wire-level 兼容;目标是让已经消费 veraPDF JSON 的下游工具用最小的字段重命名适配 HotPDF 输出,而不必重新学习另一套数据结构。
  • Rule violations 保留原生 HotPDF JSON 输出中相同的 specification ISO 章节交叉引用,调用方走 veraPDF-style 路径仍保留 spec 级别的可追溯性。

2026-05-25 Version 2.130.0

  • 新增 THotPDF.EmbedPreflightReportAsXMP,含两个 overload(带/不带可选 Password 参数)。函数把源 PDF 复制到目标文件,并在末尾追加一段 PDF 注释块,payload 是带 xmlns:hotpdf 命名空间的 XMP / RDF。
  • 报告每行映射为 hotpdf:<name> 元素。PASS / FAIL check 把 severity 写入属性;warning、hint、info 行用 warnhintinfo 前缀加在元素名前。
  • 扫描 xpacket 标记的 XMP 工具可看到嵌入报告。PDF 阅读器继续忽略追加字节(不在对象图内)。这是归档友好的 XMP 嵌入,不是 spec 完整的 XMP 集成;XMP payload 没有从 catalog /Metadata 引用过来。

2026-05-25 Version 2.129.0

  • 新增 THotPDF.LoadPreflightProfileTHotPDF.ApplyPreflightProfile,配合新 THPDFPreflightProfile record。Profile 让调用方在不重跑分析的前提下定制报告输出:屏蔽特定 check / warning 名称,或屏蔽所有 Hint 行。
  • Profile 文件用 INI 风格:[disable-checks][disable-warnings][options] 三个可选段。注释和空行忽略。空 profile 透传原报告。
  • 过滤后,结尾 SummaryWarnings 行重新计算,被屏蔽条目不再计入 Failed / Warnings 总数。其他行原样透传,JSON / HTML 转换器、diff helper、validate / embed / aggregate 等 API 无需改动即可在过滤后报告上继续工作。

2026-05-25 Version 2.128.2

  • 新增 Delphi PreflightDashboard console demo,扫描一个 PDF 目录并输出自包含的静态 HTML dashboard:每个 PDF 生成 <stem>.preflight.html,外加一份 index.html 汇总表。
  • Dashboard 顶部显示 total / passed / failed 计数,下面是每个 PDF 的状态、大小、summary 和详细报告链接的表格。失败行高亮;PASS / FAIL 徽章着色,扫一眼即知。
  • 输出是一个静态目录(无需 HTTP server);发布到 CI artifact 页面、用任意 web server 提供,或者直接在浏览器打开 index.html

2026-05-25 Version 2.128.1

  • 新增 tools/validate-preflight-json.py Python 工具,对照已发布的 schema(Docs/preflight-schema.json)验证 HotPDF preflight JSON 输出。脚本对每个输入打印一行 PASS/FAIL,验证失败时退出码非零,CI pipeline 可据此做 schema 合规性门禁。
  • 因 schema 在每个对象上都声明 additionalProperties: false,验证为严格模式:未知字段触发硬失败,而不是悄悄混入。所需依赖为标准 jsonschema Python 包。

2026-05-25 Version 2.128.0

  • 新增 THotPDF.AggregatePreflightReports 用于批量汇总。函数接受 per-file preflight 报告数组,输出单一汇总:每个文件一行 PASS/FAIL 状态 + 字节大小 + 警告数,加上 passed / failed / warnings / 总扫描字节数 等总计行。
  • 与 v2.125.1 HotPDFPreflight 独立 CLI 天然成对:跑 CLI 对每个 PDF 产出独立报告,然后把报告 body 喂给 AggregatePreflightReports,拿到 dashboard-ready 批量汇总。
  • 空输入数组返回空字符串,调用方可以无条件追加该汇总输出,不必为零文件目录单独防御。

2026-05-25 Version 2.127.0

  • 新增 THotPDF.RepairPDFFromPreflightReport,含两个 overload(带/不带可选 Password 参数)。函数对损坏的 PDF 应用一组保守的字节级修复:截掉最后一个 %%EOF 标记之后的所有追加字节、补上完全缺失的 %%EOF 标记。
  • 当至少应用了一次修复时返回 TrueRepairsApplied 是 out 参数,每行列一项修复,调用方据此判断保留修复版还是升级到更强力的恢复工具。
  • 对象图层面的修复(重建 xref、修复 stream length、补全 trailer)刻意排除在外,因为此类修复可能把部分可恢复的文件变得更不可恢复。本接口的修复都限于在"完好/轻损"PDF 上都明确安全的操作。

2026-05-25 Version 2.126.1

  • Docs/preflight-schema.json 发布 preflight JSON 输出的正式 JSON Schema,符合 JSON Schema draft 2020-12 规范。
  • Schema 描述每个顶层字段(inputsizepdfVersionxrefStyle)、checks / info / hints / warnings 数组和 summary 对象,让消费 pipeline 在解析前先做结构验证。
  • 帮助主题现在指向该 schema 文件供下游消费方参考。

2026-05-25 Version 2.126.0

  • 新增 THotPDF.ComparePreflightReports,逐行比对两份 preflight 报告。函数输出 unified-diff 样式:共享行用两个空格前缀;仅在 ReportA 出现的行用 ;仅在 ReportB 出现的行用
  • LoadAndValidatePreflightReport 天然成对:归档验证失败时把 OriginalReport 与 CurrentReport 传入 ComparePreflightReports,可直接看出哪几行变了。
  • diff 算法是一个小型 greedy 遍历,针对 CreatePreflightReport 的确定性 key/value 输出做了优化;对典型输入产出紧凑 diff,不引入通用 LCS 实现。

2026-05-25 Version 2.125.1

  • 新增独立 Delphi console 工具 HotPDFPreflight,用于批量对单个文件或整个目录跑 preflight 报告。支持 -o 输出目录、-f text|json|html 输出格式、-p 密码、-r 递归目录遍历、-v 详细进度日志等参数。
  • 退出码遵守标准 CLI 约定:0 成功、1 用法错误、2 输入不存在、3 至少一个 PDF preflight 失败,便于集成进 CI pipeline 和 shell 脚本。
  • 每个报告文件命名为 <basename>.preflight.<ext>,扩展名取决于请求的格式。

2026-05-25 Version 2.125.0

  • 新增 THotPDF.LoadAndValidatePreflightReport,含两个 overload(带/不带可选 Password 参数)。该函数从 EmbedPreflightReportInPDF 写出的 PDF 中提取嵌入报告,重跑当前 preflight 算法,对比嵌入的 InputFingerprint 与新生成的指纹,一致时返回 True
  • out 参数 OriginalReportCurrentReport 同时返回两份报告,便于验证失败时调用方比对差异,定位嵌入后到验证之间的变化。
  • EmbedPreflightReportInPDF 天然成对:归档时嵌入一次,稍后任意时间验证是否被篡改或意外修改。

2026-05-25 Version 2.124.2

  • THotPDF.CreatePreflightReport 新增两条指纹行,便于 CI 稳定性比对。InputFingerprintSize: 行之后输出源 PDF 字节的 64-bit FNV-1a 哈希(16 位小写 16 进制)。ReportFingerprint 在报告末尾输出已组装报告内容(指纹行之上一切)的 64-bit FNV-1a 哈希。
  • CI pipeline 可跨多次运行比对两个指纹,检测输入文件或报告内容的意外漂移,无需逐行解析。
  • 两个指纹都是非密码学;选用 FNV-1a 是为低开销、内联实现,不依赖外部哈希模块。
  • JSON 输出把 InputFingerprint 放入 info 数组,ReportFingerprint 放在 info 末尾;两条 entry 的 spec 字段都标为 "FNV-1a 64-bit (non-cryptographic)"。

2026-05-25 Version 2.124.1

  • THotPDF.CreatePreflightReport 新增两条条件性增量更新版本诊断行。当 IncrementalUpdates > 0 时,报告新增 RevisionStartXRefPositions(每个 startxref 关键字的字节偏移,按从旧到新排序)和 RevisionXRefTargets(每个标记指向的 xref 偏移),调用方无需手工解析即可追踪增量更新链。
  • 无增量更新或仅单次 revision 的 PDF 保持字节级稳定:新行仅在找到一个以上 startxref 标记时输出。
  • JSON 输出中为新字段添加 ISO 32000-1 sec 7.5.6 规范引用。

2026-05-25 Version 2.124.0

  • 新增 THotPDF.EmbedPreflightReportInPDF,含两个 overload:一个带可选 Password 参数支持加密 PDF。该方法把源 PDF 复制到目标文件,并在最后一个 %%EOF 之后以 PDF 风格的注释行追加 preflight 报告。
  • 报告随文档一同归档:文本编辑器、grep 和审计工具在文件离开原始工作站后很久还能查阅。
  • PDF 阅读器继续按原样渲染文档,因为追加的字节落在对象图之外;交叉引用表与 trailer 字典未变。
  • Delphi PreflightReport demo 新增 embed CLI 参数,在生成独立报告的同时写出嵌入版本。

2026-05-25 Version 2.123.1

  • 新增 regression 测试:手工构造包含重复对象编号、错误 xref 行和不平衡 stream/endstream 对的 PDF,验证 Phase F 的三个根因诊断行(DuplicateObjectListFirstMalformedXRefOffsetStreamEndStreamDelta)按预期触发。
  • 库行为无变化:本次仅填补 regression 套件之前只覆盖"干净"与"非 PDF"两种情况的盲区。

2026-05-25 Version 2.123.0

  • THotPDF.CreatePreflightReportTHotPDF.SavePreflightReport 新增 HTML 输出格式。THPDFPreflightFormat 枚举新增第三个值 pfHTML,与 pfTextpfJSON 并列。
  • HTML 输出是自包含的 dashboard 样式文档:内联 CSS、severity 着色(PASS 绿、FAIL 红、WARN 黄)、checks / hints / info / warnings 分表 + summary 卡片。无需外部 CSS、JavaScript 或字体。
  • 每行表格包含 ISO 32000-1 / ISO 19005 / ISO 15930 / ISO 14289 规范引用,与 v2.122.1 在 JSON 输出加入的 spec 字段同源。
  • HTML 输出做了字节安全转义:& < > " ' 都转义,UTF-8 字节序列保真。
  • Delphi PreflightReport demo 接受 html 作为第 4 位 CLI 参数请求 HTML 输出。

2026-05-25 Version 2.122.4

  • 新增 PDF/X subset 检测。检测到 PDF/X 声明时,新的 Hint PDFXVersion 行给出具体子集(X-1a:2001X-1a:2003X-3:2002X-3:2003X-4X-4pX-5gX-5pgX-5nX-5)。
  • 新增 PDF/UA part 提取。检测到 PDF/UA 声明时,新的 Hint PDFUAPart 行从 XMP 元数据中的 pdfuaid:part 报告值。
  • JSON 输出中为新 hint 条目添加 ISO 15930 和 ISO 14289 规范交叉引用。
  • 无 PDF/X / PDF/UA 声明的干净 PDF 保持字节级稳定:新 hint 仅在底层声明被检测到时输出。

2026-05-25 Version 2.122.3

  • THotPDF.CreatePreflightReport 新增根因诊断行。当损坏的 PDF 触发缺陷计数时,报告现在给出可操作的细节。
  • DuplicateObjectList 仅当 DuplicateObjectNumbers > 0 时出现,列出最多 5 个重复的对象编号,便于调用方定位冲突。
  • FirstMalformedXRefOffset 仅当 XRefMalformedEntries > 0 时出现,给出第一个错误交叉引用行的 1-based 字节偏移。
  • StreamEndStreamDelta 仅当 streamendstream 标记数量不等时出现,给出有符号差值,提示缺多少标记。
  • 干净的 PDF 保持字节级稳定:新诊断行仅在底层缺陷计数非零时输出。

2026-05-25 Version 2.122.2

  • 扩展 preflight action warning 覆盖 PDF 1.7 §12.6.4 全部动作类型。新增 15 个咨询性 warning:GoToRActionGoToEActionThreadActionURIActionSoundActionMovieActionHideActionNamedActionSubmitFormActionResetFormActionImportDataActionSetOCGStateActionRenditionActionTransActionGoTo3DViewAction
  • 仅当检测到对应 action token 时才输出 warning;不含任何 action 的干净 PDF 保持字节级稳定。
  • JSON 输出中的 ISO 32000-1 规范交叉引用现在同样覆盖这 15 个新 warning。

2026-05-25 Version 2.122.1

  • 扩展 THotPDF.CreatePreflightReport JSON 输出,新增 ISO 规范交叉引用。每个 checkshintswarnings entry 现在携带 spec 字段,指向 ISO 32000-1、ISO 19005、ISO 15930 或 ISO 14289 中对应的章节。
  • plain-text 报告保持不变,字节级稳定;spec 字段只出现在 pfJSON 输出中。
  • 映射覆盖报告到 v2.122.0 为止 emit 的每个 check、hint 和 warning,JSON 消费者无需外部 lookup 表即可把诊断路由到对应规范章节。

2026-05-25 Version 2.122.0

  • THotPDF.CreatePreflightReportTHotPDF.SavePreflightReport 新增机器可读 JSON 输出。新增 THPDFPreflightFormat 枚举包含 pfText(默认 plain-text 路径)和 pfJSON(CI 友好的 JSON 文档)两个值。
  • 新增格式参数 overload 接受 Format 参数;既有 text overload 保持字节级稳定,现有应用无需任何代码改动。
  • JSON 输出分组:顶层字段 inputsizepdfVersionxrefStyle,加上 checksinfohintswarnings 数组和包含 failedwarningsresult 计数器的 summary 对象。
  • 内置 JSON encoder 处理 "\、控制字节转义,UTF-8 字节序列保真,无需外部 JSON 库。
  • Delphi PreflightReport demo 接受第四个 CLI 参数 json,对输入 PDF 请求 JSON 格式输出。

2026-05-25 Version 2.121.36

  • 增强 preflight 报告,新增合规标记 hint。报告现在输出 Hint PDFAClaimedHint PDFAPartHint PDFAConformanceHint PDFXClaimedHint PDFUAClaimedHint TaggedPDFHint HasTransparency 行,便于下游工具据此决策。
  • 新增 PDFAWithEncryptionPDFAWithJavaScriptPDFA1WithTransparency 互斥 warning,在文档同时声明 PDF/A 又携带加密、JavaScript 或 PDF/A-1 禁止的 transparency 标记时触发。
  • hint 行不计入 Failed 计数,所有 helper 保持咨询性质;不替代完整 PDF/A、PDF/X、PDF/UA 或 ISO 32000 合规验证引擎。

2026-05-25 Version 2.121.35

  • 增强 preflight 报告,新增内容资源完整性诊断。报告现在包含资源字典、字体、嵌入字体程序、图像 XObject、Form XObject、ColorSpace、注释、widget 和 link 计数。
  • 报告现在包含 FlateDecodeDCTDecodeCCITTFaxDecodeJBIG2DecodeLZWDecodeASCIIHexDecodeASCII85DecodeRunLengthDecodeJPXDecode 的 filter chain 使用计数。
  • 新增 FontsHaveEmbeddedPrograms pass/fail 检查,在声明字体资源时验证至少有一份 /FontFile/FontFile2/FontFile3 嵌入字体程序。
  • 新增 AnnotationCountConsistent pass/fail 检查,验证 widget 与 link 注释计数之和不超过整体注释计数。

2026-05-25 Version 2.121.34

  • 增强 preflight reports,新增 trailer root type diagnostics。报告现在验证 trailer /Root indirect reference 指向的对象是否声明为 /Type /Catalog

2026-05-25 Version 2.121.33

  • 增强 preflight reports,新增 trailer ID diagnostics。报告现在统计 trailer /ID array 中的 hex string entries,并在提供 ID array 时验证 file identifier 是否为有效的 two-entry pair。

2026-05-25 Version 2.121.32

  • 增强 preflight reports,新增 trailer indirect-reference diagnostics。报告现在显示 trailer /Root/Info object references,并验证这些引用对象是否在文件中定义。

2026-05-25 Version 2.121.31

  • 增强 preflight reports,新增 stream length coverage diagnostics。报告现在统计 stream /Length entries,并验证检测到的 streams 是否具备对应的 length entries。

2026-05-25 Version 2.121.30

  • 增强 preflight reports,新增 revision marker diagnostics。报告现在统计 %%EOF markers 和 startxref sections,验证二者数量是否平衡,并检查最后一个 startxref marker 是否位于最后一个 EOF marker 之前。

2026-05-25 Version 2.121.29

  • 增强 preflight reports,新增 page tree consistency diagnostics。报告现在包含 page tree 声明的 /Count,并增加 pass/fail 检查,用于比较该值以及统计到的 page objects 是否与 HotPDF 加载到的页数一致。

2026-05-25 Version 2.121.28

  • 增强 preflight reports,新增 xref table row diagnostics。报告现在包含 xref subsection、entry、free-entry、in-use-entry 和 malformed-row counts,并增加 xref row syntax 以及 xref 覆盖范围是否达到最高 object number 的 pass/fail 检查。

2026-05-25 Version 2.121.27

  • 增强 preflight reports,加入有边界的 PDF name-pair counting 和更多一致性检查。报告现在能区分 catalog、page tree 和 page objects,验证 object numbers 是否唯一,并检查 trailer /Size 是否覆盖最高 object number 以及 trailer /Root 是否存在。

2026-05-25 Version 2.121.26

  • 增强 preflight report 的 object 和 trailer diagnostics。报告现在包含 indirect object definition counts、最高 object number、重复 object number count、stream/endstream 平衡检查,以及 /Size/Root/Info/ID/Encrypt trailer details。

2026-05-25 Version 2.121.25

  • 增强 THotPDF.CreatePreflightReportTHotPDF.SavePreflightReport 的 cross-reference diagnostics。报告现在会验证最终 %%EOF marker 是否靠近文件尾部,解析最后一个 startxref offset,识别其目标是 xref table 还是 xref stream,并包含 xref table、xref stream、object stream、trailer、incremental update 和 linearization counts。

2026-05-25 Version 2.121.24

  • 扩展 preflight report helpers,新增 password-aware overloads 和更完整的诊断信息。报告现在包含 PDF header version、xref style、已加载页面解析出的 MediaBox、form field count、stream count、feature presence flags、JavaScript/action/media attachment warnings、warning totals,以及损坏文件的 pass/fail summary。Delphi PreflightReport 示例支持为受支持的加密 PDF 传入可选密码参数。

2026-05-25 Version 2.121.23

  • 新增 library-level preflight report helpers:THotPDF.CreatePreflightReportTHotPDF.SavePreflightReport。应用现在可以为输入 PDF 生成文本摘要,覆盖 header、EOF marker、startxref、trailer 或 XRef stream、可加载页数、加密状态、catalog、page tree、page object、page box、information dictionary、root reference、indirect object count 和整体 pass/fail 状态。Delphi PreflightReport 示例现在直接使用这些 API。

2026-05-25 Version 2.121.22

  • 新增 Delphi PreflightReport 控制台示例,用于创建样例 PDF,并写出包含轻量结构检查的文本报告,覆盖 PDF header、EOF marker、可加载页数、加密状态、catalog、page tree、page box 和 document information dictionary。该 workflow 保持 AutoLaunch 关闭,也支持从命令行对现有输入 PDF 生成报告。

2026-05-25 Version 2.121.21

  • 新增 Delphi SearchAndSelect 控制台示例,用于生成报表、搜索受控文本行,并用 PDF highlight annotations 和 /QuadPoints 标记每一个匹配词。示例保持 AutoLaunch 关闭,适合命令行和自动化验证。

2026-05-25 Version 2.121.20

  • 新增 RC4-40 和 RC4-128 Standard 加密 PDF 的密码加载能力。应用现在可以用带密码的 LoadFromFileLoadFromStream,也可以加载后调用 DecryptLoadedDocument,再通过 SaveLoadedDocument 保存未加密的已加载文档副本。新的 Delphi DecryptPDF 控制台示例会创建带密码的样例文件,并在不启动 PDF 查看器的情况下写出解密副本。

2026-05-25 Version 2.121.19

  • 新增 Delphi Printer 控制台示例,用于生成带打印预设的 PDF。示例会写入打印相关 /ViewerPreferences、页面 BleedBoxTrimBox、可打印注释标志以及 print-control ExtGState,同时不会自动打开 PDF 查看器,也不会向物理打印机发送任务。

2026-05-25 Version 2.121.18

  • 新增 Delphi ConvertTo 控制台示例,用于把支持的源图形转换为 PDF 输出文件。该 workflow 会分别从 JPEG、BMP、TIFF 和 EMF 输入创建 PDF 文件,并关闭 AutoLaunch,便于命令行和自动化检查使用。

2026-05-25 Version 2.121.17

  • 新增已加载页面操作 helper:InsertPagesFromDocumentExtractPagesToFileMovePage。应用现在可以从另一个已加载文档插入页面、把选定页面抽取到新 PDF,并在重排已加载页面后让后续保存和抽取继续使用更新后的页序。Delphi PageOperations demo 展示了插入、抽取和重排 workflow,且不会自动启动 PDF 查看器。

2026-05-25 Version 2.121.16

  • 扩展已加载 AcroForm 编辑能力,新增 RemoveFormFieldFlattenFormFields。应用现在可以按索引或名称删除已加载字段,或把已加载字段扁平化为静态页面内容,同时移除交互式 AcroForm 字段树和 widget annotations。Delphi FormFields 示例现在展示字段说明、字段删除和扁平化,并且不会自动启动 PDF 阅读器。

2026-05-25 Version 2.121.15

  • 新增已加载书签标题搜索能力:THotPDF.FindLoadedBookmarkPageIndex。应用现在可以加载现有 PDF,按书签标题搜索 outline 树,并把匹配到的 /Dest 或本地 GoTo action 目标解析为 0-based 页面索引。

2026-05-24 Version 2.121.14

  • 新增 THPDFPage.AddTextWatermark,用于在新建页面或已加载页面上绘制旋转透明文字水印。新的 Delphi Watermark 示例会创建源 PDF、加载它、给每页应用水印并保存编辑后的文档。

2026-05-24 Version 2.121.13

  • 新增已加载 AcroForm 字段说明检查能力:GetFormFieldDescription。应用现在可以按索引或名称读取已加载字段的 /TU 工具提示文本,并与字段值、选项、只读状态和重命名操作配套使用。

2026-05-24 Version 2.121.12

  • 新增已加载 choice 字段选项检查能力:GetFormFieldOptionCountGetFormFieldOptionValue。应用现在可以加载现有 combo box 或 list box 字段,枚举其允许的 /Opt 值,并继续使用同一套已加载表单更新和保存流程。

2026-05-24 Version 2.121.11

  • 扩展已加载 AcroForm 编辑能力,新增 IsFormFieldReadOnlyRenameFormField。应用现在可以检查已加载字段的只读标志,按索引或名称重命名字段,保存已加载文档,并在重新加载后保留更新后的字段名称。

2026-05-24 Version 2.121.10

  • 新增 THotPDF.GetLoadedPageBox,用于在 LoadFromFile 后检查页面边界。应用现在可以读取已加载页面的 MediaBoxCropBoxBleedBoxTrimBoxArtBox,并包含未直接声明页面框时的标准回退链。

2026-05-24 Version 2.121.9

  • 新增已加载 AcroForm 字段检查和更新 API。应用现在可以加载现有表单 PDF,枚举字段名称和类型,读取并更新字段值,切换字段的只读标志,并通过 SaveLoadedDocument 保存已加载文档。新的 Delphi FormFields 示例展示了完整的加载、编辑和保存流程。

2026-05-24 Version 2.121.8

  • 新增 THotPDF.RegisterLineGraphicsState,用于创建可复用的线条绘制 ExtGState 资源。应用现在可以一次性注册线宽、端点样式、连接样式和斜接限制,再在描边路径前通过 THPDFPage.SetGraphicsState 应用该命名状态。

2026-05-24 Version 2.121.7

  • 新增 annotation 交互辅助 API,可写入 annotation /F flags 和 link /H highlight modes。Link 辅助方法现在会更新 LastAnnotation,因此创建 link 后可以一致地链式设置 flags、highlight modes、border styles 和 popup helpers。

2026-05-24 Version 2.121.6

  • 新增可复用 Form XObject 生成 API 与页面级绘制辅助方法,支持按矩形放置、缩放和旋转。新增当前变换矩阵便捷方法,以及用于单行对齐、矩形内自动换行输出和行数计算的文本辅助方法。

2026-05-24 Version 2.121.5

  • 新增 HotPDF 可复用的 Type 1 PFB 与 CFF 解析单元,包含字节数组读取器、Type 1 字形轮廓命令提取、CFF dictionary、INDEX、charset 与 encoding 读取,以及 Type 1 PFB 元数据和字形查找辅助能力。Delphi 和 C++Builder 包工程已纳入这些解析单元。

2026-05-24 Version 2.121.4

  • PDFACompliance='2A'PDFACompliance='3A' 现在只自动启用 Tagged PDF 基础结构,不再隐式写入 PDF/UA-1 XMP 声明。PDF/A Level A 文件仍保留 /MarkInfo/StructTreeRoot、文档标题、语言和归档元数据;pdfuaid 元数据只在显式启用 PDFUACompliance 时输出。
  • PDF/A-2/3 smoke 覆盖现在包含 PDF/A-3A,并断言 Level A 归档输出不会在未显式请求时宣称 PDF/UA。生成的 PDF/A-2A 与 PDF/A-3A smoke 文件已通过 veraPDF 验证。

2026-05-24 Version 2.121.3

  • THPDFPage.UnicodeTextOut 现在会复用 RegisterUnicodeTTF 生成的 Type 0 字体资源输出补充平面 Unicode,让页面文本与 AcroForm 外观流共享同一套内部 CID 编码路径。
  • 共享的最终化步骤现在会通过 FUnicodeUsedCps/CIDToGIDMap、descendant font /W/ToUnicode 联动同步页面级 SMP 输出,不再保留旧的页面本地 SMP 映射表。

2026-05-24 Version 2.121.2

  • THPDFPage.UnicodeTextOut 现在会在 RegisterUnicodeTTF 已加载当前字体时,利用页面字体已注册的 cmap format 12 glyph 映射输出补充平面 Unicode 字符。emoji 和其他 U+10000+ scalar 会在页面文本流中写成单个 glyph CID,而不是两个 surrogate-half CID。
  • 页面字体的 /ToUnicode CMap 会把这些 SMP glyph CID 映射回原始 UTF-16BE surrogate pair,在保留现有 /CIDToGIDMap /Identity 页面字体路径的同时,保证文本提取和无障碍语义。

2026-05-24 Version 2.121.1

  • RegisterUnicodeTTF 支持的 AcroForm 外观流现在会把补充平面 Unicode 字符(例如 emoji)写成单个内部 CID,而不是把 UTF-16 surrogate pair 当作两个 Identity-H CID。生成的 /CIDToGIDMap 会把该 CID 指向真实 glyph,/ToUnicode 会把它映射回原始 UTF-16BE surrogate pair,供文本提取使用。
  • Unicode 字体的最终化步骤会在所有外观流完成内部 CID 注册后刷新 /CIDToGIDMap/W/ToUnicode,让 TrueType 子集化和生成的表单外观使用最终 CID 集合。

2026-05-23 Version 2.121.0

  • PDF 1.7 §12.6.4.13–15 多媒体动作(Rendition / Sound / Movie) 三组新 THPDFPage 按钮 helper 方法落地: AddPushButtonWithRenditionAction 实现 §12.6.4.13 (Screen 注释 + MediaRendition + MediaClipData 描述音视频片段 + /OP 操作 0..4);AddPushButtonWithSoundAction 实现 §12.6.4.14,两个 overload(文件路径 / 原始 TBytes) + 调用方提供的 THPDFSoundParams record (SampleRate / Channels / BitsPerSample / Encoding) + 可选 /Volume + /Repeat + /Synchronous; AddPushButtonWithMovieAction 实现 §12.6.4.15 (通过 /Annotation 引用 Movie 注释 + /Operation Play / Stop / Pause / Resume 四操作)。
  • AddScreenAnnotationAddMovieAnnotation 现在返回创建的 annotation 字典, 调用方可直接传入对应 action helper 完成链式接入。procedure 改 function 是 Delphi ABI 兼容变更 — 旧调用方忽略返回值的代码继续 编译并发出字节级一致的 PDF。
  • Standard 实施深度:每个 helper 发 spec 必填字段加常用可选 字段(Sound /Volume /Repeat /Synchronous;Rendition /JS)。 MediaPlayParameters / MediaScreenParameters / Selector Rendition / Sound /Mix 留待后续 — reader 默认行为覆盖 95% 场景,helper 保持字节最小。
  • PDF/A(全级别)+ PDF/X(全 profile)在三个 helper 入口立即 抛 ISO 引用诊断(ISO 19005-1 §6.6.1 / ISO 19005-2 §6.5.1 / ISO 19005-3 §6.5.1 / ISO 15930)。原有 AddScreenAnnotation / AddSoundAnnotation / AddMovieAnnotation annotation 路径的 gate 保持不变。
  • Rendition action 的 MediaClipData filespec 自动接 /P /TF (TEMPACCESS) MediaPermissions(§13.2.6.1 Table 280 最低权限级), 让 reader 把 payload 临时交给平台音视频 codec。
  • Sound action stream 的可选字段(Volume / Repeat / Synchronous) 仅在调用方传入非默认值时发出,默认 case 下 action dict 字节最小。
  • Sound stream metadata(/R 采样率 / /C 声道 / /B 位深 / /E 编码)通过新 THPDFSoundParams record 由调用方 显式提供。v2.16 时代的 AddSoundAnnotation 入口仍 hardcode 22050 / 16-bit / Stereo / Signed;新 Sound action 路径 避开此坑 — 调用方可正确播放 44.1 kHz mp3、8 kHz µ-law 电话音频等任意源。
  • Win32 + Win64 全平台 smoke_multimedia_actions(4 个 action variant + Screen / Movie 注释链接的正向路径全覆盖)与 smoke_multimedia_actions_pdfa_pdfx(8 个 gate raise 断言 = 3 actions × PDF/A + PDF/X)通过;回归 smoke_button_actions 双平台 跑通 — AddScreenAnnotation / AddMovieAnnotation procedure → function 改造不破坏既有 byte-level 输出。

2026-05-23 Version 2.120.15

  • Fix:EndDoc 先于两个 post-build mutator 序列化 PDF 对象图, 两个 mutator 的修改都进不了输出文件。受影响的是 v2.84.0 引入的 TrueType subsetter 与 v2.94.0 引入的 PDF/UA-1 §7.18.3 /Tabs /S 自动 stamping:使用 RegisterUnicodeTTF + AddTextField 的文档会嵌入完整字体文件(典型 Latin TTF 约 1 MB / CJK 约 14 MB)而非子集(约 150 KB / 500 KB), /BaseFont 缺失六字母 AAAAAA+ 子集前缀,FontDescriptor 上 PDF/A 强制要求的 /CIDSet stream 完全缺失(veraPDF 报 ISO 19005-1 §6.3.5 / -2 -3 §6.2.11 失败);PDF/UA-1 文档的 annotated page 缺失 §7.18.3 /Tabs /S 键,撞 Matterhorn 检查规则 21-001 与 PAC 3 "Tabs" 失败。
  • 两个 mutator 现在都在 SaveToStream / SaveToFile 遍历 IndirectObjects 写入 PDF 之前运行。对未注册 Unicode TTF 且未启用 PDFUACompliance 的文档,输出与 v2.120.14 字节级 一致 —— 这两类文档下两个 mutator 本就是 gated no-op, 重排仅修复了 v2.120.14 输出原本静默错误的情况。
  • Win32 smoke_unicode_ttf_subset 验证子集字节为系统 Arial 的 33.6%(676 KB 缩减)、Type 0 / CIDFontType2 / FontDescriptor.FontName 三处 /BaseFont 一致为 UYENHH+ArialMT;smoke_pdfua_annot_structparents 验证 annotated page dict 上 /Tabs /S 正确 stamp,全部五条 §7.18.3 + annotation-structure 接线断言通过。

2026-05-23 Version 2.120.14

  • PDF/UA-1 strict 结构 role 入口校验(ISO 14289-1 §7.7 + ISO 32000-1 §14.7.3):PDFUACompliance 启用时, AddStructureElement 拒绝任何不在 PDF 1.7 §14.8.4 Table 333 41 个标准 structure type(Document / Part / Art / Sect / Div / BlockQuote / Caption / TOC / TOCI / Index / NonStruct / Private / H / H1..H6 / P / L / LI / Lbl / LBody / Table / TR / TH / TD / THead / TBody / TFoot / Span / Quote / Note / Reference / BibEntry / Code / Link / Annot / Ruby / RB / RT / RP / Warichu / WT / WP / Figure / Formula / Form)也未经 AddStructRoleMap 注册到 StructTreeRoot /RoleMap 的 /S role。诊断异常含违规 role 字面、PDF/UA-1 §7.7 / Table 333 引用、AddStructRoleMap 修复建议。空 Role 单独分支抛。没有这个 gate 时,不合规 role 会无声落入结构树,下游 veraPDF / PAC tools 才报 "unknown structure type" 错误。
  • 新 gate 仅在 PDFUACompliance=True 时触发;非 PDFUA 调用方 保持 pre-v2.120.14 freeform-role 行为字节级稳定。四个 AddStructureElement overload(4-arg AnsiString base、 v2.88.0 6-arg /Alt + /ActualText、v2.95.0 7-arg /ID、 v2.119.41 THPDFStandardStructureType 枚举)都汇聚到同一 gated path,单点 guard 覆盖。枚举重载总是传 Table 333 标准名因此总通过校验 —— 调用方应优先用,编译期消除 'Para' vs 'P' / 'Lbody' vs 'LBody' 大小写笔误隐患。
  • v2.119.41 已交付 THPDFStandardStructureType 枚举 (41 个 spec 名按 5 个 PDF role bucket 分组: §14.8.4.1 Grouping / §14.8.4.2 Block-level / §14.8.4.3 Inline-level / §14.8.4.4 Ruby+Warichu / §14.8.4.5 Illustration)+ StandardStructureTypeToName(T) enum→spec名转换 + IsStandardStructureType(Name) 任意 AnsiString 验证;v2.120.14 把这些 capability 接入 AddStructureElement 入口让 strict 检查自动化。新增私有 helper _IsCustomRoleInRoleMap 用 byte-safe CompareMem 扫 FStructRoleMap,非 ASCII 自定义 role 在 CP_ACP=65001 host 正常 match。

2026-05-23 Version 2.120.13

  • 修复:v2.120.12 源码无法在 RAD Studio 编译 —— 两个 EnableShapingFeatureForSubset 重载落在 THotPDF 的 published 段,Delphi 抛 E2266("只允许一个重载成员声明为 published", RTTI 无法按名称消歧)。v2.120.13 在两个重载前后加 public / published 括号让公开 API 面与 v2.120.12 字节级一致, 可在 dcc32 / dcc64 干净编译;前后属性与 helper 保持 published 不动。运行时行为不变。

2026-05-23 Version 2.120.12

  • EnableShapingFeatureForSubset(Phase 8f 收口): THotPDF.EnableShapingFeatureForSubset(FeatureTag) / THPDFShapingFeature 枚举重载会在 v2.119.49 SetGSUBScript / SetGSUBLanguage 选择状态下遍历所请求 feature 的所有 GSUB lookup,列出 LookupType 1(Single — Format 1 delta 对 Coverage 内每个字形 + Format 2 substituteGlyphIDs 数组)/ LookupType 2(Multiple — Sequence.substituteGlyphIDs[])/ LookupType 3 (Alternate — AlternateSet.alternateGlyphIDs[])/ LookupType 4 (Ligature — Ligature.ligatureGlyph)/ LookupType 5/6 (Contextual / Chained Contextual — 递归 SequenceLookupRecord 嵌套 lookup,深度限制 8)/ LookupType 7(Extension — 自动解包到有效 lookup 类型)/ LookupType 8(Reverse Chained — substituteGlyphIDs[])的每个潜在替换字形 GID,OR 合并到 FUnicodeExtraUsedGlyphs 让 v2.84.0 EndDoc subsetter 拉入嵌入 字体子集。Phase 8f 与 v2.119.50 per-glyph MarkUnicodeGlyphUsed 互补 —— 用于 producer 管线在通用启用某 feature 时的 feature-level batch opt-in。
  • 枚举重载将每个 THPDFShapingFeature 值派发到其约定的 4-byte OpenType feature tag 集合:sfArabicGSUB → init / medi / fina / isol / rlig;sfStandardLigatures → liga; sfContextualLigatures → clig + rlig;sfStylisticAlternates → salt;sfIndicShaping → nukt / akhn / rphf / rkrf / pref / half / vatu / cjct / pres / blws / abvs / psts / haln / pstf; sfContextualAlternates → rclt + calt。字符串 tag 重载接受 任意 4-byte OpenType feature tag 实现枚举之外的精确目标。 子集体积警告:启用宽 feature(如 aalt Access All Alternates,某些字体把整个脚本所有字形挂上来)可能把字体一大 部分字形拉进子集,抵消 v2.84.0 体积优化 —— 知道精确 GID 的 调用方应优先用 Phase 9 MarkUnicodeGlyphUsed。默认关闭 / 显式 opt-in。收口 GSUB Engine Roadmap Phase 8 最后一个子 phase。
  • 防御契约镜像 v2.119.50:无字体注册、无 GSUB 表、非 4-byte tag、feature 不在 (script, language) 路径、空 lookup 链 —— 所有 "nothing to do" 都是静默 no-op(不抛异常)。重复 调用幂等(位图 OR 合并)。Contextual 子表 Format 1 / 2 的 嵌套 lookup 递归留待未来版本;Format 3 在真实字体中占绝大 多数,已实现。需要 Format 1 / 2 精确覆盖的调用方可 fallback 到 per-glyph MarkUnicodeGlyphUsed。

2026-05-23 Version 2.120.11

  • ToUnicode CMap 调用方反向映射注册(Phase 8d 收口): THotPDF 新增 3 个公开方法让调用方向 EndDoc 发射的 Adobe-Identity-UCS ToUnicode CMap 注册额外的 (substitute codepoint → 原始 Unicode 源 codepoint 序列) 反向映射条目。v2.119.61 / v2.119.62 / v2.119.65 系列已 hard-code 了 35 个 bfchar 内置条目,覆盖 HotPDF producer-side shaping pipeline 自动 emit 的 Arabic 合字 (LAM-ALEF / YEH-HAMZA / Allah / Bismillah)和 Latin Alphabetic Presentation Forms(fi / fl / ffi / ffl / ff / ſt / st);v2.120.11 通过让调用方为自行驱动的 v2.119.43-50 GSUB 引擎 API(GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst)输出的 substitute 字形注册反向映射来收口 Phase 8d。
  • RegisterToUnicodeReverseMapping(SubstCodepoint, SourceCodepoints) 注册一个 (substitute → 源序列) 条目。 典型用法:Multiple Substitution(1 GID → N GIDs)emit substitute 序列后,注册反向映射让消费 reader 的 copy / paste 回到原始 codepoint;Ligature Substitution (N GIDs → 1 合字 GID)emit 合字后,注册合字 codepoint 反向到 N 元素源序列(例如任意 CALT 上下文合字反向到组件 codepoints)。ClearToUnicodeReverseMappings 清空所有调用方 注册;ToUnicodeReverseMappingCount 报告当前注册数。
  • EndDoc emit 在内置 35 条目 block 之后追加调用方注册条目 作为额外 bfchar block,按 Adobe CMap and CIDFont Files Specification(Technical Note #5014)§1.4.2 bfchar 算子 上限自动拆分为 100 条目子块。BMP codepoints 发射 4 hex 位;SMP codepoints 发射 UTF-16BE surrogate pair(8 hex 位)符合 Adobe CMap spec。注册顺序保留,让 PDF reader 的 bfchar last-write-wins 语义给调用方对内置 35 条目的冲突 提供确定性覆盖行为(调用方注册 <FB01> "fi" 的自定义 映射会在 reader 端覆盖 v2.119.65 默认)。未调用 RegisterToUnicodeReverseMapping 的工作流 ToUnicode CMap 字节级与 v2.120.10 baseline 一致。SetFormUnicodeFontDict ('', nil) reset 路径清空调用方注册表让下次 Unicode 字体 注册从干净状态开始。

2026-05-23 Version 2.120.10

  • Myanmar 文字排版器(Phase 8f.10):把 Myanmar(OpenType tag mymr)注册为 HotPDF 第十个、也是最后一个 Indic 脚本, 同时覆盖 Myanmar 主块(U+1000-U+109F:Burmese / Mon / Sgaw Karen / Western Pwo / Shan / Rumai Palaung / Pa'o)与 Extended-A (U+AA60-U+AA7F)。新公开 ApplyMyanmarReorder 重排前置 pass 与 GetMyanmarCategory 码点查询,可通过 ApplyIndicReorder dispatcher 统一调用。
  • 本批次中最复杂的 syllable 结构:音节起始的 Kinzi 三码点前缀 (U+1004 + U+103A + U+1039)被 FSM 检测、由 reorder pre-pass 留住,输出最前面 emit (R8);pre-base 元音 E (U+1031) 移到 音节起始 (R10);四个 medial 辅音 Y / R / W / H (U+103B-U+103E) 按固定 Y → R → W → H 序输出,不论源序 (R9)。
  • ASAT (U+103A) 与 VIRAMA (U+1039) 都按 virama 处理 stacked consonant 合并。DOT BELOW (U+1037) 渲染在基底之下;其他声调 记号渲染在基底之上。ANUSVARA(Bindu)在上方;VISARGA 在尾部。 无 Repha。
  • IndicScripts 注册表加 **两个** entry(主块 + Extended-A),共享同一组 Myanmar reorder 函数,让 Extended-A 辅音与 Pa'o Karen 声调记号通过 dispatcher 透明排版。
  • 11-Phase 非 Devanagari Indic shaping 批次完成 (Phases 8f.0-8f.10):基础设施 + 10 个注册 Indic 脚本 —— Devanagari / Bengali / Gujarati / Tamil / Telugu / Kannada / Malayalam / Sinhala(Brahmic SIA 家族) / Khmer / Myanmar (东南亚)。
  • 遵循 Unicode 16.0 §16.3(Myanmar)与 OpenType Myanmar shaping 规范。

2026-05-23 Version 2.120.9

  • Khmer 文字排版器(Phase 8f.9):把 Khmer(OpenType tag khmr,Unicode 块 U+1780-U+17FF)注册为 HotPDF 第九个 Indic 脚本,也是首个东南亚脚本。新公开 ApplyKhmerReorder 重排前置 pass 与 GetKhmerCategory 码点查询,可通过 ApplyIndicReorder dispatcher 统一调用。
  • 独立 syllable 结构(不同于 Phase 8f.1-8f.8 用的 Brahmic R1-R5 规则):pre-base 元音 (E / AE / AI / OE / OO / AU) 移到音节起始; register shifter(MUUSIKATOAN、TRIISAP)与其他上标记号路由到 above-base 缓冲;Bindu(NIKAHIT)放上方;Visarga(REAHMUK、 YUUKALEAPINTU)放尾部。
  • COENG 下标处理:每个 COENG (U+17D2) + 辅音对形成 stacked-consonant cluster,在 base 缓冲中保持原始顺序,让字体的 'pres' / 'blws' GSUB feature 负责 下标定位。FSM 支持嵌套 coeng。
  • 无 Repha:Khmer 文字不形成 Repha 视觉,因此 Ra + COENG + Consonant 保留原始顺序,不会被旋转到 音节末尾。
  • 遵循 Unicode 16.0 §16.4(Khmer)与 OpenType Khmer shaping 规范。

2026-05-23 Version 2.120.8

  • PAdES adbe-revocationInfoArchival CMS signed attribute(PAdES Phase 9):emit Adobe 私有 OID 1.2.840.113583.1.1.8,把 CRL 与 OCSP 撤销值直接 塞进 CMS 签名内部。Adobe Acrobat 在做签名撤销检查时优先认这个 attribute,先于 DSS dictionary 查找与网络 OCSP / CRL 获取 —— 对离线可验证 PDF 与 Adobe Acrobat 最佳互操作至关重要。
  • 新增 THPDFCMSSignOptions 字段:
    • AdobeCRLsDER: array of TBytes —— 每个元素 一个 X.509 CertificateList (RFC 5280) DER, 与 DSS /CRLs 流的格式相同。
    • AdobeOCSPsDER: array of TBytes —— 每个元素 一个 BasicOCSPResponse SEQUENCE (RFC 6960 §4.2.1) DER。注意:不是 外层 OCSPResponse wrapper —— 持有完整 OCSPResponse 的调用方需要先剥出 responseBytes.response OCTET STRING 取出 BasicOCSPResponse
  • 启用条件:两个数组任一非空即 emit attribute。默认两个数组 都为空时与 v2.120.7 字节级一致。
  • ASN.1 DER 按 Adobe 规范: RevocationInfoArchival ::= SEQUENCE { crl [0] EXPLICIT SEQUENCE OF CRL OPTIONAL, ocsp [1] EXPLICIT SEQUENCE OF BasicOCSPResponse OPTIONAL, otherRevInfo [2] EXPLICIT SEQUENCE OF OtherRevInfo OPTIONAL }。HotPDF 接通 [0][1][2] (非 X.509 撤销系统)暂未实现。
  • Wire shape(两个分支都填): 30 <len> A0 <crllen> 30 <...> <CRL1> <CRL2> A1 <ocsplen> 30 <...> <OCSP1> <OCSP2>
  • 与 DSS dictionary 并存:本 attribute 是 PAdES-B-LT DSS dictionary(Catalog 级 /DSS /Certs /OCSPs /CRLs /VRI,v2.110.0 引入)的 并行 路径,不是替代。严格 PAdES 验证器(EU DSS、mTOM) 倾向认 DSS;Adobe Acrobat 认 adbe-revocationInfoArchival。最大互操作就同时填两层 —— 用 AddPAdESDSSCRL / AddPAdESDSSOCSP 喂 DSS,用 AdobeCRLsDER / AdobeOCSPsDER 喂 Adobe attribute。
  • 4-arg HPDFCMSBuildSignedData wrapper 把新字段 显式清零,legacy CMS bundle 字节级一致。
  • 对应 Adobe Acrobat PDF Reference + RFC 5280 (CRL) + RFC 6960 (OCSP)。

2026-05-23 Version 2.120.7

  • PAdES content-time-stamp CMS signed attribute(PAdES Phase 8):emit id-aa-ets-contentTimestamp(OID 1.2.840.113549.1.9.16.2.20,ETSI EN 319 122-1 §5.2.8 + RFC 5126 §5.11.4),携带签名 之前 由 TSA 盖章的 RFC 3161 TimeStampToken,证明文档内容在 TSA 声明时间点 已存在。区别于 signature-time-stamp(Phase 4 unsigned attribute,签名 之后)。
  • 新增 THPDFCMSSignOptions 字段 GetContentTimeStamp: THPDFCMSTimestampCallback。 与 v2.120.3 的 GetSignatureTimeStamp 完全对称: HotPDF 用 document SHA-256(与 messageDigest signed attribute 一致的 hash)调用回调,把返回的 TimeStampToken DER 作为 content-time-stamp attribute 的 value 塞进 SignedAttributes。
  • 默认 nil = 不 emit content-time-stamp。需要的 调用方赋值一个闭包通过 HTTP 驱动 RFC 3161 TSA。返回空 TBytes 静默跳过(TSA 不可达时优雅降级)。
  • 免费 TSA endpoint(无需付费 / 无需注册) 非合规场景可用: http://timestamp.digicert.comhttp://timestamp.sectigo.comhttp://timestamp.globalsign.com/tsa/r6advanced1https://freetsa.org/tsrhttp://timestamp.apple.com/ts01。eIDAS 合格签名 场景需要付费 QTSA;普通商业用途用免费 endpoint 即可(有速率 限制但够日常工作流)。
  • 回调契约:每次签名操作调用一次,时机在 SignedAttributes 封口 之前。TST 字节成为签名内容的一部分,所以签名本身 覆盖了 content-time-stamp —— 这就是为什么它是 signed attribute (与 signature-time-stamp 不同,后者是 unsigned,在签名之后塞进 unsignedAttrs)。
  • 4-arg HPDFCMSBuildSignedData wrapper 把 GetContentTimeStamp:=nil 显式清零,legacy CMS bundle 与 v2.119.27 字节级一致。
  • 调用方责任:PAdES 占位符需要 ContentsBytes 够容纳 signature 加 content-time-stamp TST(~4-8 KB 典型)。B-T 工作流同时用 content-time-stamp + signature-time-stamp 时需考虑两个 TST 的 合计大小 —— AddPAdESSignatureField(Profile='B-T') 默认 16 KB 一般够用。
  • 对应 ETSI EN 319 122-1 V1.2.1 §5.2.8 + RFC 3161 §6 + RFC 5126 §5.11.4。

2026-05-23 Version 2.120.6

  • PAdES signer-attributes-v2 signedAssertions [2] 分支(PAdES Phase 7):收口 SignerAttributeV2 第三个也是最后一个 OPTIONAL 字段。让签名者在 CMS 签名内附带 OID 标识的签名声明 (SAML 2.0 Assertion / JWT 紧凑形式 token / OAuth attestation / 组织自定义 attestation token)。
  • 新增 THPDFSignedAssertion record: SignedAssertionID(标识声明类型的 dotted-notation OID)+ AssertionBody: TBytes(按 OID schema 预编码 的 ANY 字节 —— 通常是包含 SAML XML 或 JWT compact form 的 OCTET STRING)。AssertionBody 为空时省略 ANY 字段, 只保留 signedAssertionID(spec 允许,适用于以 OID 注册的 boolean-style 声明)。
  • 新增 THPDFCMSSignOptions 字段 SignedAssertions: array of THPDFSignedAssertion。 支持多个声明;HotPDF 把 SEQUENCE OF 用 [2] EXPLICIT(tag 0xA2)包起来。
  • SignerAttributeV2 emit 条件改为三个 OPTIONAL 字段任一非空。 三者都填时按 spec 字段顺序输出: SignerAttributeV2 SEQUENCE { [0] claimed..., [1] certified..., [2] signedAssertions... }
  • ASN.1(ETSI EN 319 122-1 §5.2.6): SignedAssertion ::= SEQUENCE { signedAssertionID OBJECT IDENTIFIER, signedAssertion ANY DEFINED BY signedAssertionID OPTIONAL }。带 body 的单个声明 wire 形式: A2 <len> 30 <len2> 30 <len3> 06 <OIDlen> <OID> <ANYbytes>
  • 复用 v2.120.1 的 CMSEncodeOIDFromString 编码 assertion OID —— 接受任意 dotted-notation OID。
  • 4-arg HPDFCMSBuildSignedData wrapper 把新字段 清零,legacy CMS bundle 字节级一致。
  • signer-attributes-v2 收口:三个 OPTIONAL 字段 ([0] claimedAttributes / [1] certifiedAttributesV2 / [2] signedAssertions)经 Phase 5/6/7 全部接入。ETSI EN 319 122-1 §5.2.6 attribute producer 端完整覆盖。
  • 对应 ETSI EN 319 122-1 V1.2.1 §5.2.6 + ANNEX A.1。

2026-05-23 Version 2.120.5

  • PAdES signer-attributes-v2 certifiedAttributesV2 [1] 分支(PAdES Phase 6):在 SignerAttributeV2 SEQUENCE 里 emit X.509 attribute 证书(RFC 5755),与 v2.120.4 的 claimedAttributes [0] 并存或单独使用。让签名者附带 AA (Attribute Authority)签发的角色证书,从密码学上背书签名者 声明的身份 / 权限。
  • 新增 THPDFCMSSignOptions 字段 CertifiedAttributeCertsDER: array of TBytes。每个 元素是调用方从签发 AA 处取得的完整 DER 编码 AttributeCertificate(RFC 5755)。HotPDF 把它们 原样塞入 CertifiedAttributesV2 SEQUENCE OF 然后用 [1] EXPLICIT(tag 0xA1)包起来。
  • 现在只要 ClaimedRoles CertifiedAttributeCertsDER 任一非空, signer-attributes-v2 attribute 就会 emit。两者 都填时 SignerAttributeV2 SEQUENCE 里 [0][1] 依次出现,符合 spec 字段 顺序。
  • ASN.1 DER wire 形式(仅 CertifiedAttributesV2): A1 <len> 30 <len2> <AC1> <AC2> ...,每个 ACn 是调用方提供的 RFC 5755 AttributeCertificate SEQUENCE verbatim。 AttributeCertificateOtherAttributeCertificate 两个 CHOICE 分支由调用方 的 DER 输入决定(两者都以 SEQUENCE 0x30 起头;HotPDF 不解析)。
  • 4-arg HPDFCMSBuildSignedData wrapper 把新字段 清零,legacy CMS bundle 与 v2.119.27 字节级一致。
  • 范围:signedAssertions [2](SAML / JWT / 任意 OID token) 留待下一 Phase。
  • 对应 ETSI EN 319 122-1 V1.2.1 §5.2.6 + ANNEX A.1 + RFC 5755。

2026-05-23 Version 2.120.4

  • PAdES signer-attributes-v2 CMS signed attribute(PAdES Phase 5):emit id-aa-ets-signerAttrV2(OID 0.4.0.19122.1.1,ETSI EN 319 122-1 §5.2.6),让 签名者在 CMS bundle 内声明角色(如 "Chief Financial Officer"、 "Authorised Representative")。取代 RFC 5126 §5.10 已 deprecated 的 v1 signerAttr
  • 新增 THPDFClaimedRole record: RoleOID(attribute 类型 OID,dotted notation; 通常用组织内部角色 OID 或 1.3.6.1.5.5.7.20.1 id-id-aa-PERMrole)+ RoleValue(UTF-8 字符串,人类可读的角色标签)。
  • 新增 THPDFCMSSignOptions 字段 ClaimedRoles: array of THPDFClaimedRole。支持多个 角色;每个角色成为 claimedAttributes [0] SEQUENCE OF 内的一个 Attribute。空数组(默认)压制 attribute,未启用此 功能的 v2.120.3 调用方字节级一致。
  • ASN.1 DER 结构(ETSI EN 319 122-1 §5.2.6 + ANNEX A.1,模块 DEFINITIONS EXPLICIT TAGS): SignerAttributeV2 SEQUENCE { [0] EXPLICIT ClaimedAttributes }ClaimedAttributes ::= SEQUENCE OF Attribute。每个角色序列化为 Attribute { type OID, values SET OF UTF8String }。 EXPLICIT 标签即 0xA0 把内层 SEQUENCE OF 字节 verbatim 包起来(内层 0x30 tag 保留)。
  • OID body 手编:04 00 81 95 32 01 01(arc 0.4.0.19122.1.1;19122 的 base-128 编码 → 0x81 0x95 0x32 含 continuation bits)。未来自定义角色 OID 可走 v2.120.1 的 CMSEncodeOIDFromString
  • Scope 边界:本 Phase 仅实现 claimedAttributes [0]certifiedAttributesV2 [1](RFC 5755 X.509 attribute 证书)与 signedAssertions [2](SAML / JWT 风格 token)需要额外基础设施,等有具体需求再做。
  • 4-arg HPDFCMSBuildSignedData wrapper 把 ClaimedRoles 长度显式归零,legacy CMS bundle 与 v2.119.27 字节级一致。
  • 对应 ETSI EN 319 122-1 V1.2.1 §5.2.6 + EN 319 142-1 V1.2.1 §6.3 Table 1 行 signer-attributes-v2(MAY,0-or-1)。

2026-05-23 Version 2.120.3

  • PAdES signature-time-stamp CMS unsigned attribute(PAdES Phase 4):emit id-aa-signatureTimeStampToken(OID 1.2.840.113549.1.9.16.2.14,RFC 3161 §6 + ETSI EN 319 122-1 §5.3),放在 SignerInfo 的 [1] IMPLICIT unsignedAttrs SET 内。提供 PAdES-B-T trusted-time service 的 signature-time-stamp 路径 (Table 1:B-T shall be provided,可用 signature-time-stamp document-time-stamp)。
  • 新增 THPDFCMSTimestampCallback anonymous method 类型: reference to function(const SigValueSHA256: TBytes): TBytes。HotPDF 用 SignerInfo.signatureValue 的 SHA-256(RFC 3161 §2.4.2 定义的 messageImprint)调用回调,把返回的 TimeStampToken DER 作为 attribute value 嵌入。
  • 新增 THPDFCMSSignOptions 字段 GetSignatureTimeStamp。默认 nil 表示 不加时间戳;PAdES-B-T 调用方赋值一个闭包驱动 TSA HTTP / RFC 3161 通信并返回 TST 字节。返回空 TBytes 静默跳过(如 TSA 不可达但 签名仍要交付)。
  • 网络 / 身份验证 / TSA 账户处理留在调用方代码里;HotPDF 只把 结果接入 unsignedAttrs。与现有 AddPAdESSignatureField(Profile='B-T', ContentsBytes >= 16384) 占位符尺寸自然配套。
  • ASN.1 wire:SignerInfo SEQUENCE 现在可选携带尾部 [1] IMPLICIT SET OF Attribute(tag 0xA1),内含一个 signature-time-stamp Attribute,其 value 是调用方 提供的 TimeStampToken ContentInfo 字节。
  • 4-arg HPDFCMSBuildSignedData wrapper 把 GetSignatureTimeStamp:=nil 显式清零,legacy CMS bundle 字节级一致。
  • 对应 RFC 3161 §6 + ETSI EN 319 122-1 §5.3 + EN 319 142-1 V1.2.1 §6.3 Table 1 注 q(B-T 可用 signature-time-stamp 或 document-time-stamp 任一提供 trusted time)。

2026-05-23 Version 2.120.2

  • PAdES commitment-type-indication CMS SignedAttribute 支持(PAdES Phase 3):emit id-aa-ets-commitmentType attribute(OID 1.2.840.113549.1.9.16.2.16,ETSI EN 319 122-1 §5.2.3 + RFC 5126 §5.11),声明签名者通过本签名所做的承诺类型(如 proof of origin / receipt / delivery / sender / approval / creation)。
  • 新增 THPDFCommitmentType 枚举:ctNone (默认,压制 attribute)、ctProofOfOriginctProofOfReceiptctProofOfDeliveryctProofOfSenderctProofOfApprovalctProofOfCreation(六个标准 id-cti-ets-* OID 来自 RFC 5126 §5.11.1,arc 1.2.840.113549.1.9.16.6.{1..6}),以及 ctCustom(调用方通过新增 CommitmentTypeOID 字段提供任意 OID)。
  • 启用条件:THPDFCMSSignOptions.CommitmentTypectNone 时 emit。默认空值时 v2.120.1 调用方字节级 一致。
  • 调用方责任(PAdES Part 1 §6.3 Table 1 注 d): emit commitment-type-indication 时 Signature Dictionary /Reason 入口必须不存在(互斥)。HPDFCMS 看不到 Sig Dict 状态 —— 调用方需要选其一:要么 AddPAdESSignatureField(Reason='', ...) + CommitmentType,要么 CommitmentType=ctNone + 填 Reason
  • ASN.1 DER 结构: CommitmentTypeIndication SEQUENCE { commitmentTypeId OBJECT IDENTIFIER }。CommitmentTypeQualifier(RFC 5126 §5.11.2)省略,因为六个标准 id-cti-ets-* OID 没有 qualifier 定义。需要 qualifier 的自定义 commitment 可以扩展 helper。
  • 4-arg HPDFCMSBuildSignedData wrapper 把新增 CommitmentType 字段显式清零,v2.119.27 legacy CMS bundle 保持字节一致。
  • 对应 ETSI EN 319 122-1 §5.2.3 + RFC 5126 §5.11 + ETSI EN 319 142-1 V1.2.1 §6.3 Table 1 注 d。

2026-05-23 Version 2.120.1

  • PAdES signature-policy-identifier CMS SignedAttribute 支持(PAdES Phase 2):emit id-aa-ets-sigPolicyId attribute(OID 1.2.840.113549.1.9.16.2.15,ETSI EN 319 122-1 §5.2.9 + RFC 5126 §5.8),声明签名所采用的签名策略。PAdES-E-EPES (Part 2 V1.2.1 §5.4 Table 2)要求shall be present; 其他 PAdES 级别可选。
  • 新增 THPDFCMSSignOptions record 字段: SignaturePolicyOID(策略文档的 dotted-notation OID 字符串)、SignaturePolicyHash(策略文档摘要)、 SignaturePolicyHashAlgOID(摘要算法 OID,留空时 默认 SHA-256)、SignaturePolicyURI(可选 SPuri qualifier,按 RFC 5126 §5.8.1,OID 1.2.840.113549.1.9.16.5.1,指向策略文档 URL)。
  • 启用条件:调用方同时填了 SignaturePolicyOIDSignaturePolicyHash 时才 emit policy attribute; 默认空值时 attribute 被压制 —— baseline 签名调用方与 v2.120.0 字节级一致。
  • 新增辅助函数 CMSEncodeOIDFromString 把任意 dotted-notation OID 编码为 DER(X.690 §8.19:前两个 arc 合并为 40*arc1 + arc2,后续 arc 按 base-128 + 高位 continuation 编码)。策略 OID 由调用方业务决定,生产端无法 hard-code 为字节字面量。
  • ASN.1 DER 结构: SignaturePolicyId SEQUENCE { sigPolicyId OBJECT IDENTIFIER, sigPolicyHash OtherHashAlgAndValue [, sigPolicyQualifiers SEQUENCE OF SigPolicyQualifierInfo] }。 SPuri qualifier 把 URL 用 IA5String(tag 0x16)包在 SigPolicyQualifierInfo 里。
  • 兼容性:v2.120.0 调用方未填新增 policy 字段时输出字节级一致。 4-arg HPDFCMSBuildSignedData wrapper 把新字段 显式清零,legacy CMS bundle 保持不变。
  • 对应 ETSI EN 319 122-1 §5.2.9 + ETSI EN 319 142-2 V1.2.1 §5.4 Table 2 + RFC 5126 §5.8。

2026-05-23 Version 2.120.0

  • PAdES baseline CMS 属性合规修复(PAdES Phase 1): SignPDFWithPFX 现在默认 emit ESS signing-certificate-v2 signed attribute(RFC 5035 / OID 1.2.840.113549.1.9.16.2.47),保护签名证书身份不被替换。 ETSI EN 319 142-1 V1.2.1 §6.3 Table 1 把这条列为 所有 baseline 级别(B-B / B-T / B-LT / B-LTA)都 shall be provided
  • PAdES baseline 默认不再 emit CMS signing-time attribute。Table 1 明确要求 baseline 级别 cardinality 0(声明的 签名时间由 Signature Dictionary /M 入口承载,见 Table 1 注 g)。v2.119.27 路径无论 profile 都 emit signing-time,被严格 PAdES 验证器(EU DSS、Adobe Acrobat PAdES baseline 模式)拒收。
  • 新增公开 API:THPDFPAdESLevel 枚举(8 个值,覆盖 baseline B-B / B-T / B-LT / B-LTA、extended E-BES / E-EPES / E-LTV、legacy adbe.pkcs7), THPDFCMSSignOptions record(level + signingTime + signing-cert-v2 开关 + UTC 时间戳), HPDFCMSDefaultOptions(Level) 返回该 level 的规范默认值,HPDFCMSBuildSignedDataEx 接受 options record 的 CMS 构建器。
  • 向下兼容:v2.119.27 四参数版 HPDFCMSBuildSignedData 签名保留为 thin wrapper,emit 字节级一致的输出(不含 signing-cert-v2,按调用方请求开关 signingTime)。锁定 v2.119.27 CMS 字节的测试 fixture 保持不变。
  • 签名 PDF 输出的行为变化:调用 THotPDF.SignPDFWithPFX 处理 PAdES 占位符 (AddPAdESSignatureField,SubFilter ETSI.CAdES.detached)时,输出现在能通过严格 PAdES baseline 验证。需要 v2.119.27 旧属性集的调用方可以显式调用 HPDFCMSBuildSignedDataEx + HPDFCMSDefaultOptions(palLegacy_adbePkcs7)
  • ASN.1 DER 结构(RFC 5035 §3): SigningCertificateV2 SEQUENCE { certs SEQUENCE OF ESSCertIDv2 },省略 SHA-256 AlgorithmIdentifier(X.690 §11.5 DEFAULT 值不可编码),含 SHA-256 cert hash 与 IssuerSerial(复用现有 CMSExtractIssuerAndSerial 的 X.509 issuer + serialNumber 抽取)。
  • 对应 ETSI EN 319 142-1 V1.2.1 §6.3 Table 1 + RFC 5035 §3。

2026-05-23 Version 2.119.77

  • 新增 Sinhala shaper(Phase 8f.8)。Sinhala('sinh', U+0D80-U+0DFF)成为第八个注册的 Indic 脚本。 THotPDF 新增公开方法 ApplySinhalaReorderGetSinhalaCategory
  • Sinhala 特性:有 Repha(Ra=U+0DBB + Halant=U+0DCA AL-LAKUNA); 三个 pre-base matras(E=U+0DD9、EE=U+0DDA、AI=U+0DDB), Phase 8f 中 pre-base matra 数量最多的脚本;3 个 split matras (U+0DDC O / U+0DDD OO / U+0DDE AU)按 Unicode 16.0 canonical 分解; U+0DDD OO 是三部件 split(E + AA + AL-LAKUNA)。
  • ApplyIndicReorder 派发器自动覆盖 Sinhala(注册表扩为 array[0..7])。
  • 21 个新 DUnitX 测例。Win32 + Win64 全平台共 187/187 通过。
  • 对应 Unicode 16.0 §12.11(Sinhala)与 OpenType Sinhala shaping spec。

2026-05-23 Version 2.119.76

  • 新增 Malayalam shaper(Phase 8f.7)。Malayalam('mlym', U+0D00-U+0D7F)成为第七个注册的 Indic 脚本。 THotPDF 新增公开方法 ApplyMalayalamReorderGetMalayalamCategory
  • Malayalam 特性:有 Repha(Ra=U+0D30 + Halant=U+0D4D CHANDRAKKALA); I-matra(U+0D3F)是 POST-base,与 Tamil 一致, 在 Brahmic 中独特(不同于 Devanagari/Bengali/Gujarati 的 pre-base); E/EE/AI 是 pre-base;3 个 split matras(U+0D4A O / U+0D4B OO / U+0D4C AU)按 Unicode 16.0 canonical pre+post 分解;chillu 字母 (U+0D54-U+0D56、U+0D7A-U+0D7F)与 DOT REPH(U+0D4E)归类为辅音。
  • ApplyIndicReorder 派发器自动覆盖 Malayalam(注册表扩为 array[0..6])。
  • 21 个新 DUnitX 测例,含 chillu / DOT REPH 类别专项覆盖。 Win32 + Win64 全平台共 166/166 通过。
  • 对应 Unicode 16.0 §12.10(Malayalam)与 OpenType Malayalam shaping spec。

2026-05-23 Version 2.119.75

  • 新增 Kannada shaper(GSUB 引擎路线图 Phase 8f.6)。Kannada ('knda',U+0C80-U+0CFF)成为第六个注册的 Indic 脚本。 THotPDF 新增公开方法 ApplyKannadaReorderGetKannadaCategory
  • Kannada 与 Devanagari / Bengali / Gujarati / Telugu 一样有 Repha (Ra=U+0CB0 + Halant=U+0CCD)。与 Telugu 类似,Kannada 无 pre-base matras — 所有 Kannada matra 都是 above-base、below-base、 post-base 或 split。
  • 5 个 split matras 按 Unicode 16.0 canonical decomposition 自动展开: U+0CC0 II → U+0CBF(above)+ U+0CD5(post); U+0CC7 EE → U+0CC6(above)+ U+0CD5(post); U+0CC8 AI → U+0CC6 + U+0CD6(两个 above); U+0CCA O → U+0CC6(above)+ U+0CC2(post); U+0CCB OO 是三部件 split:U+0CC6(above)+ U+0CC2 (post)+ U+0CD5(post) — Phase 8f 家族首个三部件 split 路由。
  • Above-base matras (R3):I (U+0CBF)、E (U+0CC6)、AU (U+0CCC)、 AI 长度标记 (U+0CD6)。Below-base matras (R4):Vocalic R/RR (U+0CC3-U+0CC4)、Vocalic L/LL matras (U+0CE2-U+0CE3)。Post-base matras (R5):AA (U+0CBE)、U/UU (U+0CC1-U+0CC2)、post-base 长度标记 (U+0CD5)。
  • ApplyIndicReorder 派发器自动覆盖 Kannada(注册表扩为 array[0..5])。
  • 测试:21 个新 DUnitX 测例(含全部 5 个 split decomposition 与 三部件 OO 专项验证)。Win32 + Win64 全平台共 145/145 通过。
  • 对应 Unicode 16.0 §12.9(Kannada)与 OpenType Kannada shaping spec。

2026-05-23 Version 2.119.74

  • 新增 Telugu shaper(GSUB 引擎路线图 Phase 8f.5)。Telugu ('telu',U+0C00-U+0C7F)成为第五个注册的 Indic 脚本。 THotPDF 新增公开方法 ApplyTeluguReorderGetTeluguCategory
  • Telugu 与 Devanagari / Bengali / Gujarati 一样有 Repha(Ra=U+0C30 + Halant=U+0C4D)。与之前所有 Indic 脚本不同:Telugu 无 pre-base matras — 所有 Telugu matra 都是 above-base、below-base 或 split。
  • Above-base matras (R3):AA/I/II/E/EE/O/OO/AU 与长度标记 U+0C55。 Below-base matras (R4):U/UU/Vocalic R/RR (U+0C41-U+0C44)、 AI-length-mark (U+0C56)、Vocalic L/LL matras (U+0C62-U+0C63)。
  • 1 个 split matra:U+0C48 AI 在 reorder 时分解为 U+0C46(E above-base) + U+0C56(AI 长度标记 below-base)。
  • ApplyIndicReorder 派发器自动覆盖 Telugu(注册表扩为 array[0..4])。
  • 测试:17 个新 DUnitX 测例。Win32 + Win64 全平台共 124/124 通过。
  • 对应 Unicode 16.0 §12.8(Telugu)与 OpenType Telugu shaping spec。

2026-05-23 Version 2.119.73

  • 新增 Tamil shaper(GSUB 引擎路线图 Phase 8f.4)。Tamil ('taml',U+0B80-U+0BFF)成为第四个注册的 Indic 脚本。 THotPDF 新增公开方法 ApplyTamilReorderGetTamilCategory
  • Tamil 与其他 Brahmic 脚本的差异:无 Repha(Tamil 传统不形成 Repha 视觉);I-matra(U+0BBF)是 POST-base,在 Brahmic 脚本中独特; II(U+0BC0)above-base;E/EE/AI(U+0BC6-U+0BC8)pre-base; 3 个 split matras(U+0BCA O = U+0BC6+U+0BBE,U+0BCB OO = U+0BC7+U+0BBE, U+0BCC AU = U+0BC6+U+0BD7)。
  • Halant 在 Tamil 中称为 PULLI(U+0BCD)。ApplyIndicReorder 派发器自动覆盖 Tamil(注册表扩为 array[0..3])。
  • 测试:20 个新 DUnitX 测例。Win32 + Win64 全平台共 107/107 通过。
  • 对应 Unicode 16.0 §12.7(Tamil)与 OpenType Tamil shaping spec。

2026-05-23 Version 2.119.72

  • 新增 Gujarati shaper(GSUB 引擎路线图 Phase 8f.3)。Gujarati ('gujr',U+0A80-U+0AFF)成为 Devanagari 与 Bengali 之后 第三个注册的 Indic 脚本。THotPDF 新增公开方法 ApplyGujaratiReorderGetGujaratiCategory
  • Reorder 规则:R1 Repha(Ra=U+0AB0 + Halant=U+0ACD),R2 pre-base (U+0ABF I),R3 above-base(U+0AC5 CANDRA E、U+0AC7 E、U+0AC8 AI —— Gujarati 的 E/AI 是 above-base,与 Devanagari 一致,与 Bengali 的 pre-base 不同),R4 below-base(U+0AC1-U+0AC4、U+0AE2-U+0AE3), R5 post-base(U+0ABE AA、U+0AC0 II、U+0AC9 CANDRA O、U+0ACB-U+0ACC O/AU)。无 split matras。
  • ApplyIndicReorder 派发器自动将 Gujarati 码点路由到 Gujarati entry(IndicScripts 数组扩为 array[0..2])。 BuildUnicode*FieldContent helper 在 sfIndicShaping 启用时自动覆盖 Gujarati,集成点无需改动。
  • 测试:18 个新 DUnitX 测例,包含 R3 above-base 覆盖以区分 Gujarati 与 Bengali 的 E/AI 处理。Win32 + Win64 全平台共 87/87 通过。
  • 对应 Unicode 16.0 §12.6(Gujarati)与 OpenType Gujarati shaping spec。

2026-05-23 Version 2.119.71

  • 新增 Bengali shaper(GSUB 引擎路线图 Phase 8f.2)。Bengali ('beng',U+0980-U+09FF)成为 Devanagari 之后第二个注册的 Indic 脚本。THotPDF 新增公开方法 ApplyBengaliReorderGetBengaliCategory,与 Phase 8e Devanagari API 对称。
  • Bengali reorder 规则:R1 Repha(Ra=U+09B0 + Halant=U+09CD), R2 pre-base matras(I=U+09BF,E=U+09C7,AI=U+09C8 —— Bengali 的 E/AI 是 pre-base,与 Devanagari 的 above-base 不同),R4 below-base (U+09C1-U+09C4,U+09E2-U+09E3),R5 post-base(U+09BE,U+09C0,U+09D7)。 Bengali 主块无 above-base matra。
  • Split-matra 分解:U+09CB Oo 与 U+09CC AU 在 reorder 时分解为其可视组件(U+09C7 + U+09BE / U+09C7 + U+09D7), 使 GSUB 流水线按 canonical pre+post 位置处理。
  • ApplyIndicReorder 派发器自动将 Bengali 码点路由到 Bengali entry;BuildUnicode*FieldContent helper 在 sfIndicShaping 启用时自动覆盖 Bengali 与 Devanagari,集成点无需任何修改。
  • 测试:18 个新 DUnitX 测例覆盖 Bengali R1/R2/R4/R5、split 分解、conjunct 保留、混排穿过、idempotency、dispatcher 等价性。Win32 + Win64 全平台共 69/69 通过。
  • 对应 Unicode 16.0 §12.2(Bengali)与 OpenType Bengali shaping spec。

2026-05-23 Version 2.119.70

  • Devanagari complete-shaper 升级(GSUB 引擎路线图 Phase 8f.1): ApplyDevanagariReorder 现在应用完整 5 条 reorder 规则 (R1 Repha + R2 pre-base + R3 above-base + R4 below-base + R5 post-base), 取代 v2.119.55 仅有 R1 + R2 的子集。每个 syllable 的输出簇顺序: [pre-matras] + [base + halant + nukta + bindu/visarga/modifier] + [above-matras] + [below-matras] + [post-matras] + [Repha]
  • Conjunct 保留:consonant-halant-consonant 簇在 base 块中保持分组; reorder 只移动 matra 与 Repha。单 pass 完成、idempotent。
  • 对比 v2.119.69 的行为变化:syllable 包含 above/below/post-base matra (或多 matra 混合簇)时,PDF 字节流现在反映 canonical 重排版面。GSUB/ GPOS 之后的视觉渲染保持不变。仅含 R1 Repha 或 R2 pre-base I-matra (v2.119.55 子集)的输入仍字节一致。
  • 测试:8 个新 DUnitX 测例覆盖 R3 / R4 / R5 单独、多 matra 顺序、 matra 下 conjunct 保留、Repha + above + post 混合,以及多 matra idempotency。Win32 + Win64 全平台共 51/51 通过。
  • 对应 Unicode 16.0 §12.1(Devanagari)与 OpenType Devanagari shaping spec。

2026-05-23 Version 2.119.69

  • Indic shaping 基础设施(GSUB 引擎路线图 Phase 8f.0):将 v2.119.55 / v2.119.67 的 Devanagari reorder 前置预处理重构成 script-agnostic 的派发框架。新增类型 TIndicScriptInfo / TIndicCategoryFunc / TIndicFindSyllableFunc / TIndicReorderFunc,以及新的 IndicScripts 注册表,后续 Indic 脚本通过函数指针挂载即可接入。
  • 新公开方法 ApplyIndicReorder(Wide)Wide 的每个码点派发到对应已注册的 script,并调用该 script 的 syllable + reorder 回调。非 Indic 内容按字节穿过不变。
  • 三处 BuildUnicode*FieldContent helper 在 sfIndicShaping 包含在 FShapingFeatures 时改为调用 ApplyIndicReorder(取代仅覆盖 Devanagari 的旧 wrapper)。Phase 8f.0 仅注册 Devanagari,后续 Phase 8f.2 至 8f.10 将逐步添加 Bengali、Gujarati、Tamil、Telugu、Kannada、Malayalam、Sinhala、Khmer、Myanmar,集成点本身无需再改。
  • ApplyDevanagariReorder 保留为仅覆盖 Devanagari 的薄包装,向 v2.119.55 调用者提供向后兼容;输出与 v2.119.67 字节一致。
  • 对应 Unicode 16.0 §12(南亚脚本)与 ISO 32000-1 §9.10。

2026-05-22 Version 2.119.68

  • Phase 8c.6 — 通过 PUA 合成 codepoint 映射实现 producer 端 GID 级 emit:THotPDF 新增两个公开方法,让调用方为无自然 Unicode codepoint 的替换 GID 按需分配合成 PUA (U+E000-U+F8FF) codepoint。AssignSyntheticCodepointForGID (GID; out SyntheticCP): Boolean 分配下一个可用 PUA slot,把分配镜像到 FUnicodeCpToGid + FAcroFormUnicodeAdvances + 新的 FUnicodeSyntheticCpForGID 逐 GID 查询表,让现有 producer 端十六进制编码 pipeline 把合成 codepoint emit 为 4 hex-digit token,消费端 reader 通过 /CIDToGIDMap 解析到替换字形。GetSyntheticCodepointForGID (GID): Word 查询已有分配(未分配返回 0)。两个方法都幂等 —— 同一 GID 重复调用返回同一合成 codepoint。
  • 解锁无 Unicode Presentation Form codepoint 的字体特定 GSUB 替换的 producer 端 emit:Devanagari cluster 形态 (Indic 'akhn' / 'rphf' / 'pres' / 'blws' / 'psts' / 'haln' 输出通常落到字体内部 GID)、stylistic alternates ('salt' / 'ss01-20' 无 codepoint 替换)、discretionary ligatures ('dlig' / 'hlig' 字体特定字形)、CJK 表意文字变体序列替换。配合 v2.119.43-50 GSUB 引擎 API 和 v2.119.50 MarkUnicodeGlyphUsed 子集化闭包,调用方现在可构建完整的 producer 端 shaping pipeline,通过现有 codepoint 基础的 hex pipeline emit 任意替换 GID。
  • 状态生命期:合成映射在再次 RegisterUnicodeTTF 调用或 BeginDoc 触发前持续(两者都重置所有 per-font 状态含合成表)。PUA 范围 U+E000-U+F8FF 提供 6400 slot,足够任何实用字体的 GSUB 替换集合。ToUnicode CMap 反向映射是调用方责任 —— 合成 PUA codepoint 无源 codepoint 可反向映射;关心文本提取的调用方应在 PDF marked content 中包裹文本 emit 配 /ActualText 属性指定原源 codepoint。未调用 AssignSyntheticCodepointForGID 的调用方 PDF 输出与 v2.119.67 字节级一致(新方法是无状态查询 / 显式分配 helper;无自动副作用)。本 commit 完成 GSUB 引擎 roadmap Phase 8 capability 矩阵。

2026-05-22 Version 2.119.67

  • Devanagari reorder pre-pass 自动应用 (GSUB 引擎 roadmap Phase 8e):三个 BuildUnicode*FieldContent helper 现在在 sfIndicShaping 包含于 FShapingFeatures 时自动调用 v2.119.55 ApplyDevanagariReorder。pre-pass 走遍输入 Wide,应用两个 Devanagari 专属重排(音节起始 Ra-Halant 的 Repha 重定位 + 前置 I-matra U+093F 移到音节聚类起始),让 emit 的 codepoint 流匹配视觉读序。消费 reader 的 GSUB 引擎再对重排后的 codepoint 应用 Indic shaping 链('nukt' / 'akhn' / 'rphf' / 'rkrf' / 'pref' / 'half' / 'vatu' / 'cjct' / 'pres' / 'blws' / 'abvs' / 'psts' / 'haln')生成最终 cluster 字形。
  • Scope:Phase 8e 仅交付 producer 端 reorder。Producer 端 GSUB 链应用(producer 在 PDF emit 时提交 cluster shaping 而非依赖 reader)留待 Phase 8c.6,因为多数 Devanagari GSUB 替换落在字体特定 GID 无 Unicode Presentation Form codepoint —— 与 Arabic / Latin 的 Forms-A / Forms-B 块提供 codepoint 可达替换不同,Devanagari 在 Unicode 中没有对应的 Presentation Form 范围。Phase 8c.6 PUA synthetic codepoint mapping 会解锁 Devanagari 和其他替换 GID 字体特定的 Indic / 南亚脚本的 producer 端 GSUB emit。
  • 非 Devanagari 内容经 ApplyDevanagariReorder 字节级穿透(方法仅走 Devanagari 音节,其他 codepoint 范围原样 emit)。未设 sfIndicShaping (默认) 的调用方 PDF 输出与 v2.119.66 字节级一致。结合 v2.119.55 capability 层 (GetDevanagariCategory + ApplyDevanagariReorder 公开 method),本 commit 完成 Devanagari producer 端 reorder 集成:启用 sfIndicShaping 的调用方不再需要在 BuildUnicode*FieldContent 之前手动调 ApplyDevanagariReorder —— HotPDF 自动完成。

2026-05-22 Version 2.119.66

  • GSUB 'rclt' (Required Contextual Alternates) 自动集成:新方法 THotPDF.ApplyArabicGSUBContextualRefinement (Wide) 与 v2.119.63 ApplyArabicGSUBRefinement 平行,但走 contextual substitution (GSUB Type 5 / 6) 通过 v2.119.47 ApplyContextualSubst API,而非 ligature substitution (Type 4)。'rclt' 编码上下文相关的必需替换 —— 典型用法是 Arabic 字体的 v2.85.0 静态 walker Forms-B 映射之外的额外位置变体,以及南亚 / Indic 字体的 cluster 级依赖邻居上下文的重排形态。
  • 变长输出处理:与 ApplyLigatureSubstitution (N 输入 GID → 1 替换 GID) 不同,ApplyContextualSubst 可从 N 输入 GID 产生 M 替换 GID。上下文规则 fire 时,**所有** 替换 GID 必须可经 Unicode Presentation Form codepoint 反向 cmap 到达 (FB00-FDFF + FE70-FEFF 约 770 codepoint 扫描) 才提交替换;任一替换无反向映射时输入窗口原样穿透(部分 emit 会破坏序列)。Reverse cmap 范围扩到含 Latin / Armenian / Hebrew Presentation Forms (FB00-FB4F) 加完整 Arabic 范围,任意脚本的 'rclt' 替换都可 emit。
  • 新 THPDFShapingFeature 枚举成员 sfContextualAlternates(追加在末尾保后向兼容 —— 既有调用方走 v2.119.59 5 成员枚举的 FShapingFeatures 集合算术编译不变)。接入 3 个 BuildUnicode*FieldContent helper,gated on sfContextualAlternates in FShapingFeatures(opt-in,默认 off)。独立于 FAutoShapeArabic —— 'rclt' 可应用于非 Arabic 脚本 (Latin / Hebrew / Indic)。替换 GID 走 MarkUnicodeGlyphUsed 闭包入 v2.84.0 subsetter。字体无 'rclt' 规则或替换 GID 无 Presentation Form codepoint 时防御 no-op。未设 sfContextualAlternates 的调用方 PDF 输出与 v2.119.65 字节级一致。

2026-05-22 Version 2.119.65

  • Latin 'liga' Standard Ligatures emit (GSUB 引擎 roadmap Phase 8b):新方法 THotPDF.ApplyLatinLigatureRefinement (Wide) 与 v2.119.63 ApplyArabicGSUBRefinement 平行,但目标是 Latin / Armenian / Hebrew Alphabetic Presentation Forms U+FB00-U+FB4F。走遍输入 Wide,通过 FUnicodeCpToGid 构建并行 GID 数组,每位置调 v2.119.43-50 ApplyLigatureSubstitution API 查询字体 GSUB 'liga' feature。如果替换 fire AND 替换 GID 可经 Unicode Alphabetic Presentation Form codepoint 到达,源 codepoint 窗口替换为替换 codepoint。接入三个 BuildUnicode*FieldContent helper (单行、多行、comb AcroForm /AP),gated on sfStandardLigatures in FShapingFeatures (opt-in, 未置位时字节级稳定)。
  • 可选第二 pass 'clig' (Contextual Ligatures):当 sfContextualLigatures 也在 FShapingFeatures 中时,方法对 'liga' 结果再走一遍 'clig' pass 捕获字体特定的上下文 ligation(fi / fl / ffi / ffl 标准集之外)。每个 emit 替换 GID 都过 MarkUnicodeGlyphUsed 走 v2.84.0 TTF subsetter 闭包。Reverse cmap 是 ~80 codepoint (FB00-FB4F) 的线性扫描;替换稀疏,开销可忽略。
  • ToUnicode CMap bfchar 块从 28 条扩到 35 条:7 个新条目覆盖 Latin Alphabetic Presentation Forms FB00 (ff -> f + f)、FB01 (fi -> f + i)、FB02 (fl -> f + l)、FB03 (ffi -> f + f + i)、FB04 (ffl -> f + f + l)、FB05 (ſt -> long s + t)、FB06 (st -> s + t)。PDF 文本流出现 Latin ligature codepoint 时,消费端文本提取(复制 / 粘贴 / 无障碍)还原源 ASCII 字母序列。未设 sfStandardLigatures (默认) 的调用方 PDF 输出与 v2.119.64 字节级一致。Scope:仅 emit 经 Alphabetic Presentation Form codepoint 可达的替换 —— 无 Unicode codepoint 的装饰 / discretionary Latin ligature (字体特定 GID) 不能通过此路径 emit,留 Phase 8c.6 GID-level emit。

2026-05-22 Version 2.119.64

  • 公开 codepoint advance 查询 API (GSUB 引擎 roadmap Phase 8c.5):新方法 THotPDF.GetCodepointAdvance (CP: Cardinal): Single 暴露 v2.76.0 /W cache,让在 BuildUnicodeMultilineFieldContent helper 之外自建 word-wrap 计算的调用方能查询任意 codepoint 的真实 scaled em advance,源自字体 hmtx 表经缓存 cmap。v2.119.32 / v2.119.58 / v2.119.60 / v2.119.62 静态 ligature post-pass 链 + v2.119.63 GID-level GSUB refinement emit ligature codepoint (LAM-ALEF FEF5-FEFC、YEH-HAMZA FBEA-FBFB、Allah FDF2、Bismillah FDFD、GSUB 替换的 Presentation Forms in FB50-FDFF / FE70-FEFF) 之后,本方法返回真实 ligature 字形的 hmtx advance —— 通常比源 codepoint advance 之和窄 —— 让调用方的 wrap 预算与消费端渲染对齐。
  • BuildUnicodeMultilineFieldContent heuristic fallback 优化:v2.65 fallback 路径(/W cache 不可用时使用,例如走 RegisterUnicodeFontDict 而非 RegisterUnicodeTTF 时)此前把 codepoint >= U+2E80 全部当 wide (1.0 em) 处理。Arabic Presentation Forms (FB50-FBFF Forms-A 字母、FC00-FDFF Forms-A 合字 + Quranic FDF0-FDFD、FE70-FEFF Forms-B 基本形态 + LAM-ALEF) 实际上是 narrow (主流 Arabic 字体平均约 0.55 em),不是 wide。v2.119.64 把这三个 Arabic Presentation Form 范围在 heuristic fallback 中路由到 narrow 路径 (0.5 em),修复 v2.119.32 / v2.119.58 / v2.119.60 / v2.119.62 / v2.119.63 post-pass emit 的 Arabic 合字在 cache 未填充时获得错误 1.0 em advance 的边角情形。
  • 典型无信息路径(未注册字体、cache 未填充、codepoint 超 BMP、codepoint 无字体 cmap 字形)返回 0。纯函数无副作用,RegisterUnicodeTTF 成功后任何上下文都可安全调用。/W cache 已填充且未使用新查询方法的 PDF 输出与 v2.119.63 字节级一致。Phase 8c.5 在 capability 层收口 GSUB 引擎 roadmap §8c producer 端自动 Arabic GSUB shaping pipeline —— Phase 8c.1-8c.5 共同交付静态表 post-pass (4 合字家族) + ToUnicode 反向映射 + GID-level GSUB refinement + 公开 advance 查询,协同让 producer 端 Arabic 合字渲染与消费端显示字节级对齐。

2026-05-22 Version 2.119.63

  • GID-level GSUB 'rlig' refinement post-pass (GSUB 引擎 roadmap Phase 8c.2):新方法 THotPDF.ApplyArabicGSUBRefinement (Wide) 从左到右走遍输入 Wide,通过缓存 cmap (FUnicodeCpToGid) 构建并行 GID 数组,每个非零 GID 位置调用 v2.119.43-50 ApplyLigatureSubstitution API 查询字体的 GSUB 'rlig' (Required Ligatures) feature。如果替换 fire AND 替换 GID 可经过 Unicode Arabic Presentation Form codepoint 到达(reverse cmap 扫描 U+FB50-U+FDFF + U+FE70-U+FEFF 约 690 codepoint),源 codepoint 窗口被替换 codepoint 取代。替换字形通过 MarkUnicodeGlyphUsed 走 v2.84.0 TTF subsetter 闭包。
  • 接入三个 BuildUnicode*FieldContent helper,紧跟 v2.85.0 / v2.119.32 / v2.119.58 / v2.119.60 / v2.119.62 静态 codepoint 级 post-pass 链(LAM-ALEF / YEH-HAMZA / Allah / Bismillah)。补充静态表,捕获 4 个硬编码家族之外的字体特定 'rlig' 规则 —— 典型例子包括 Persian / Urdu / Sindhi / Kurdish 字体(Vazirmatn、Markazi Text、Lateef、Scheherazade、Amiri)中映射到 LAM-ALEF / YEH-HAMZA / Allah / Bismillah 集合之外 Unicode Presentation Forms 的上下文变体。仅在 FAutoShapeArabic = True AND sfArabicGSUB NOT in FShapingFeatures(mutex 保 v2.119.59 "调用方驱动" 路径)AND 加载字体有 GSUB 表时运行。
  • Scope 边界:仅 emit GID 可经 Presentation Form codepoint 到达的替换。替换 GID 落到任意字形 ID(无 codepoint 反向映射)的字体特定 GSUB 替换不能通过此路径 emit —— 需要 Phase 8c.5+ 范围的 producer pipeline 完整 GID 级 emit。Reverse cmap 是每次替换尝试约 690 codepoint 的线性扫描;实际替换稀疏,开销可忽略。加载字体无 GSUB 'rlig' 内容或替换 GID 无 Presentation Form codepoint 的 PDF 输出与 v2.119.62 字节级一致(方法在这些情况下是防御性 no-op)。本切片落地后,GSUB 引擎的 producer 端自动 Arabic shaping pipeline 获得超出硬编码静态表链的字体感知 ligature folding 能力。

2026-05-22 Version 2.119.62

  • Bismillah 短语级合字 post-pass (GSUB 引擎 roadmap Phase 8c.4):v2.85.0 producer 端 Arabic shaper 现在把 22 codepoint 的标准 Bismillah 短语 "بسم الله الرحمن الرحيم"("以最仁慈、至慈的真主之名")折叠为单一 U+FDFD ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM codepoint。在 post-pass 链的**最先**运行(在 LAM-ALEF / YEH-HAMZA / Allah 折叠之前),保证在 v2.119.60 Allah post-pass 折掉 الله 子短语前能检测完整短语。每个字母位置匹配原始 codepoint 或任一 v2.85.0 Forms-B shaped 变体(R-joining 字母每个 3 变体,D-joining 5 变体);3 个空格位置必须正好是 U+0020。标准未带 harakat 拼写的短语级检测。
  • ToUnicode CMap 加入第 28 个 bfchar 条目,把 U+FDFD 反向映射到完整 22-codepoint 源序列(BEH + SEEN + MEEM + SPACE + ALEF + LAM + LAM + HEH + SPACE + ALEF + LAM + REH + HAH + MEEM + NOON + SPACE + ALEF + LAM + REH + HAH + YEH + MEEM)。消费端文本提取(复制 / 粘贴 / 无障碍)现在还原原 22-codepoint 短语而非单一合字字形,完成 4 个静态表合字家族(LAM-ALEF / YEH-HAMZA / Allah / Bismillah)的 round-trip 行为收口。
  • 已知限制(故意的 scope 缩减):tatweel (U+0640) 在字母间不匹配;harakat / 变音符号在字母间不匹配(标准折叠针对未注音 Bismillah);非断行空格 U+00A0 或多空格不匹配(仅匹配单个 ASCII U+0020);不同拼写(الرحمٰن 含上标 ALEF)不匹配;短语必须正好出现无后接修饰。假阳率本质为零,因为标准 22-codepoint 序列在意图上唯一对应 Bismillah。其他 Quranic 敬辞(FDFA SALLALLAHOU ALAYHE WASALLAM、FDFB JALLAJALALOUHOU)和单词级合字(FDF3 AKBAR / FDF4 MOHAMMAD / FDF5 SALAM 等)不在范围内,因为这些源词在非宗教文本中也作为普通 Arabic 词出现,会产生假阳。

2026-05-22 Version 2.119.61

  • Arabic 合字 ToUnicode CMap 反向映射 (GSUB 引擎 roadmap Phase 8c.3):v2.83.0 RegisterUnicodeTTF 发射的 Adobe-Identity-UCS CMap 现在扩展 27 个 bfchar 条目,覆盖 v2.85.0 producer 端 Arabic shaper post-pass 链发射的每个 ligature codepoint,把 bfrange identity 映射 override 为正确的源 codepoint 序列。在消费端文本提取(复制 / 粘贴 / 无障碍)时还原源 Arabic 词,修复之前合字 codepoint 单字符提取的限制(例如 Allah 之前复制得到单字符 "ﷲ" 而非源词 "الله")。bfchar 按 Adobe CMap and CIDFont Files Specification 规定优先于 bfrange。
  • 覆盖范围:8 个 LAM-ALEF 条目 U+FEF5-U+FEFC(v2.119.32 强制合字,4 个 ALEF 变体 × 2 形态)→ LAM + ALEF / ALEF MADDA / ALEF HAMZA ABOVE / ALEF HAMZA BELOW 源对;18 个 YEH-HAMZA 家族条目 U+FBEA-U+FBFB(v2.119.58,含 auto post-pass 不产生但调用方可手动使用的起始形态 FBF8 和 FBFB)→ YEH-HAMZA + ALEF / AE / WAW / U / OE / YU / E / ALEF MAKSURA 源对;1 个 Allah 条目 U+FDF2(v2.119.60)→ ALEF + LAM + LAM + HEH 四 codepoint 源序列。本切片落地后,三个静态表合字家族的复制 / 粘贴 / 无障碍 round-trip 完整。
  • 非 Arabic 内容字节级稳定:bfchar 块在 CMap 流中加约 700 字节未压缩 PostScript 文本,FlateDecode 压缩后通常约 150-200 字节。不含 Arabic 内容的 PDF 仍发射相同的 /ToUnicode 流布局 —— 扩展在同一间接 FlateDecode 流对象内可加。主流消费 reader(Adobe Reader、Foxit、PDF.js、Apple Preview 等)都遵循 bfchar 优先于 bfrange 并正确应用反向映射。27 条目块在 CMap 文本中硬编码(无需逐 PDF 定制);后续 Phase 8c.4 将追加 U+FDFD Bismillah 合字的 bfchar 条目。

2026-05-22 Version 2.119.60

  • Allah 合字静态 post-pass:v2.85.0 producer 端 Arabic shaper 现在在 v2.119.32 LAM-ALEF 和 v2.119.58 YEH-HAMZA post-pass 完成之后再走一遍 post-pass,把 ALEF + LAM + LAM + HEH 四 codepoint 序列(الله 标准 Arabic 写法)折叠为单一 codepoint U+FDF2 ARABIC LIGATURE ALLAH ISOLATED FORM。模式与前两个 ligature post-pass 一致:扫描 raw 与各 shaped form 任意组合的源序列、输出单一 codepoint、依赖字体在 Unicode 定义的 cmap 项有该合字字形。这是 GSUB 引擎 roadmap Phase 8c (producer 端自动 Arabic GSUB shaping pipeline) 的第一刀 —— codepoint 级 ligature folding,覆盖最常用的 Arabic 宗教合字。
  • 源字母 form 匹配覆盖 v2.85.0 walker 成形之后的所有合法组合:ALEF 匹配 raw U+0627 或孤立形 FE8D 或末位形 FE8E(R-joining,无 init / medi);LAM 匹配 raw U+0644 或四种 shaped form FEDD-FEE0 之一(D-joining,4 个位置形态全集);HEH 匹配 raw U+0647 或四种 shaped form FEE9-FEEC 之一(D-joining)。一个典型独立 "الله" 单词在 walker 之后变成 FE8D + FEDF + FEE0 + FEEA(ALEF 孤立 + LAM 起 + LAM 中 + HEH 末),折叠为 FDF2。不含 Allah codepoint 序列的 PDF 输出与 v2.119.59 字节级一致。
  • codepoint 级 ligature folding 的已知限制:(1) 输出仅 isolated form FDF2 —— Unicode 没有为 Allah 合字定义 final / initial / medial form,但实际 Allah 几乎总是独立渲染,单 FDF2 codepoint 在视觉渲染上够用。(2) 消费端文本提取(复制 / 粘贴 / 无障碍)得到单字符 "ﷲ" 而非四字符词 "الله",因为 ToUnicode CMap 还没扩展为把 FDF2 反向映射到其 4 codepoint 源。后续 Phase 8c.3 安装件会通过 bfchar 反向映射解决。(3) 字体依赖:加载的 TTF / OTF 必须有 U+FDF2 → Allah 字形的 cmap 条目;主流 Arabic 字体(Noto Sans Arabic、Amiri、Scheherazade、Lateef、Markazi Text、Tahoma / Times New Roman Arabic)都含此字形。其他 Quranic 合字(FDF0 / FDF1 / FDF3-FDFB Allah 家族 honorifics、FDFD Bismillah)和 GID-level GSUB 集成(处理无 Unicode Presentation Forms 的字体特定替换)留待后续 Phase 8c.2-8c.4 安装件。

2026-05-22 Version 2.119.59

  • Producer 端 shaping pipeline opt-in 框架 (GSUB 引擎 roadmap Phase 8a):新枚举 THPDFShapingFeature + 集合类型 THPDFShapingFeatures 模型化 Phase 8a-8e producer 端文本成形集成的规划 feature flag。5 个枚举成员:sfArabicGSUB(本版本实现)、sfStandardLigatures(Phase 8b 留待)、sfContextualLigatures(Phase 8b 留待)、sfStylisticAlternates(Phase 8d 留待)、sfIndicShaping(Phase 8e 留待)。THotPDF 新增 ShapingFeatures 属性 (THPDFShapingFeatures 类型),默认值 [] (全部关闭);v2.119.32-58 调用方只要保持默认值,输出字节级一致。
  • sfArabicGSUB 是与 v2.85.0 静态表 Arabic shaper 互斥的开关。当 ShapingFeatures 含 sfArabicGSUB 时,三个 BuildUnicode*FieldContent helper(单行、多行、comb AcroForm /AP 外观流构造器)完全跳过 _ApplyArabicShaping —— 不做 v2.85.0 四位置 codepoint 改写、不做 v2.119.32 LAM-ALEF post-pass、不做 v2.119.58 YEH-HAMZA + 元音 post-pass。调用方负责通过 v2.119.43-50 GSUB 引擎 API 驱动 Arabic 成形 (SetGSUBScript ('arab') + GetSingleSubstituteGlyph 配 'init' / 'medi' / 'fina' / 'isol' feature tag + MarkUnicodeGlyphUsed 走 v2.84.0 子集化器闭包)。配合 sfArabicGSUB,使用 Noto Sans Arabic / Amiri / Scheherazade / Markazi Text 等 Arabic 字体的调用方现在可以在 producer 时驱动完整的字体侧 GSUB Arabic 成形,不依赖静态表 fallback。
  • 配套 Arabic capability API:两个新公开方法 GetArabicJoiningClass (CP) 和 GetArabicPosition (Wide, Index) 镜像 v2.119.53 GetSyriacJoiningClass / v2.119.54 GetMongolianJoiningClass 模式,暴露静态 shaper 内部使用的 joining-class 表和 4 位置上下文 walker。手动驱动 Arabic GSUB 成形的调用方不再需要逐 codepoint 重新推导 joining-class 数据 —— 可直接复用 v2.85.0 / v2.119.35 / v2.119.52 / v2.119.57 累积表(覆盖基本 Arabic U+0600-U+06FF + Arabic Supplement U+0750-U+077F + Arabic Extended-A U+08A0-U+08FF)。本切片落地后,用户的两点需求"Forms-A FBEA-FBFB 装饰类合字 (v2.119.58) + Producer 端自动 Arabic GSUB shaping pipeline (本版本)"在能力层 + opt-in 框架层完成。完整的 producer 端自动 GSUB 应用 (TextOut / BuildUnicode*FieldContent 内自动 cmap-to-GID + 逐位置 GSUB substitute + substitute GID emit, 调用方零代码) 留待更深的 Phase 8c 集成 commit。

2026-05-22 Version 2.119.58

  • YEH-HAMZA + 元音字母合字 post-pass:v2.85.0 producer 端 Arabic shaper 现在在 4 位置 walker 和 v2.119.32 LAM-ALEF post-pass 之后,再走一遍 post-pass 把 Arabic Presentation Forms-A 块 U+FBEA-U+FBFB 区域内 8 对 ligature pair 折叠为单一合字码点。模式与 LAM-ALEF 一致(静态表强制替换、每个 ligature 输出 2 形态)。覆盖的 pair:YEH-HAMZA + ALEF -> FBEA/FBEB(标准 Arabic / Persian / Urdu)、YEH-HAMZA + AE U+06D5 -> FBEC/FBED(Kashmiri / Uyghur)、YEH-HAMZA + WAW -> FBEE/FBEF(标准 Arabic)、YEH-HAMZA + U U+06C7 -> FBF0/FBF1(Uyghur / Kazakh / Kyrgyz)、YEH-HAMZA + OE U+06C6 -> FBF2/FBF3、YEH-HAMZA + YU U+06C8 -> FBF4/FBF5、YEH-HAMZA + E U+06D0 -> FBF6/FBF7、YEH-HAMZA + ALEF MAKSURA -> FBF9/FBFA(标准 Arabic)。
  • YEH-HAMZA 匹配原始码点 U+0626 或任一 v2.85.0 成形后形态(FE89 isol / FE8A final / FE8B init / FE8C medial);后跟元音字母匹配原始码点或任一 v2.119.57 Forms-A 成形变体(U 的 FBD7-FBD8、OE 的 FBD9-FBDA、YU 的 FBDB-FBDC、E 的 FBE4-FBE7)或 v2.85.0 Forms-B 成形变体(ALEF 的 FE8D-FE8E、WAW 的 FEED-FEEE、ALEF MAKSURA 的 FEEF-FEF0)。末位形态选择(base + 1)遵循 LAM-ALEF 规则:YEH-HAMZA 已被 walker 改写为 FE8A final 或 FE8C medial 形态时(即前面有 dual-joining 字母),合字 emit 末位形态;否则 emit 孤立形态。
  • Scope 边界:post-pass 只 emit 孤立和末位形态(2 形态输出)。Unicode 定义的起始形态 FBF8(YEH-HAMZA + E)和 FBFB(UIGHUR YEH-HAMZA + ALEF MAKSURA)不被产生 —— 需要 3 形态变体的调用方用 ApplyContextualSubst 驱动字体的 'rlig' / 'clig' 链式上下文 GSUB lookup。其他 Arabic 合字区(Allah U+FDFA / FDFB、装饰类合字 FC00-FDC7)需 GSUB 'rlig' / 'dlig' 驱动,静态表 shaper 不在 scope 内。不含上述 YEH-HAMZA + 8 元音字母 pair 的 PDF 输出与 v2.119.57 字节级一致 —— 新 post-pass 只在那 2 码点序列上触发。

2026-05-22 Version 2.119.57

  • Persian / Urdu / Sindhi / Kashmiri / Uyghur / Kazakh / Kyrgyz Arabic 扩展字母全覆盖:v2.85.0 producer 端 Arabic shaper 的 joining-class 表现在按 Unicode 16.0 ArabicShaping.txt 完整覆盖 U+0672-U+06D5 范围,关闭 v2.119.35(Persian/Urdu 核心 9 字符)+ v2.119.52(ALEF WASLA / NOON GHUNNA / HEH 变体)落地时留下的"Persian/Urdu Form-B 扩展"工作面。新增约 80 个字母的 joining-class 条目,覆盖 REH / DAL / SEEN / SAD / TAH / AIN / FEH / QAF / KAF / GAF / LAM / NOON / HEH / WAW / YEH 各种变体 —— 相邻字母现在不论紧挨哪个 Arabic Extended-A 字母都能正确选择位置形态。
  • 新分类字母中 26 个还有 U+FB52-U+FBFC 范围的静态 Presentation Forms-A 编码,现在被静态表 shaper 直接映射、无需字体 GSUB shaper 介入:15 个 D-joining 4 形态字母(TTEHEH U+067A → FB5E-FB61、BEEH U+067B → FB52-FB55、TEHEH U+067F → FB62-FB65、BEHEH U+0680 → FB5A-FB5D、NYEH U+0683 → FB76-FB79、DYEH U+0684 → FB72-FB75、TCHEHEH U+0687 → FB7E-FB81、VEH U+06A4 → FB6A-FB6D、PEHEH U+06A6 → FB6E-FB71、NG U+06AD → FBD3-FBD6、NGOEH U+06B1 → FB9A-FB9D、GUEH U+06B3 → FB96-FB99、RNOON U+06BB → FBA0-FBA3、HEH DOACHASHMEE U+06BE → FBAA-FBAD(Urdu 标准 'h' 字母)、E U+06D0 → FBE4-FBE7(Kazakh/Kyrgyz Arabic 用))和 11 个 R-joining 2 形态字母(DAHAL U+068C → FB84-FB85、DDAHAL U+068D → FB82-FB83、DUL U+068E → FB86-FB87、KIRGHIZ OE U+06C5 → FBE0-FBE1、OE U+06C6 → FBD9-FBDA、U U+06C7 → FBD7-FBD8、YU U+06C8 → FBDB-FBDC、KIRGHIZ YU U+06C9 → FBE2-FBE3、VE U+06CB → FBDE-FBDF、YEH BARREE U+06D2 → FBAE-FBAF(Urdu 词尾 yeh)、YEH BARREE WITH HAMZA ABOVE U+06D3 → FBB0-FBB1)。
  • 其中 HEH DOACHASHMEE 和 YEH BARREE 两个字母值得专门说明:它们的 Forms-A 槽位(FBAA-FBAD 和 FBAE-FBAF)正是 v2.119.52 错误地分配给 U+06C2 HEH GOAL WITH HAMZA ABOVE 和 U+06C3 TEH MARBUTA GOAL 的两组码点。v2.119.56 撤销错误映射 + v2.119.57 把槽位绑定到正确源字符之后,审计链路清晰,混淆彻底解决。U+0672-U+06D5 范围内 Unicode 16 没有 Forms-A 编码的字母(约 50 个,主要是 REH/DAL/SEEN/SAD/TAH/AIN/FEH/QAF/KAF 变体无预编码成形)仍参与 joining-class 分析让邻居正确成形;字母本身按原 codepoint 穿透,自身成形依赖字体 GSUB lookup(调用方用 v2.119.43-50 GSUB 引擎驱动)。编译 78018 行;不含这些新增字符的 PDF 输出与 v2.119.56 字节级一致。

2026-05-22 Version 2.119.56

  • Arabic shaping bug 修复:v2.119.52 引入了两个 Urdu / Sindhi 区域字符的错误 Presentation Forms-A 映射。U+06C2 HEH GOAL WITH HAMZA ABOVE 被错误映射到 FBAA-FBAD,而该码点范围实际属于 U+06BE HEH DOACHASHMEE;消费 PDF reader 会把 Urdu 的 Goal-Heh-with-Hamza 字形渲染成标准的 Urdu Heh-Doachashmee 字形。U+06C3 TEH MARBUTA GOAL 被错误映射到 FBAE-FBAF,而该码点范围实际属于 U+06D2 YEH BARREE;reader 会把 Teh-Marbuta-Goal 渲染成 Urdu 词尾 Yeh-Barree。按 Unicode 16.0,U+06C2 和 U+06C3 都没有预编码的 Presentation Forms-A 条目,必须保持原始 codepoint 不变穿透(消费 reader 通过字体 cmap 和字体 GSUB 驱动的 shaping 正确显示这两个字符)。
  • 修复:v2.85.0 producer 端 Arabic shaper 的 _ArabicShape Forms-A 查找表删除了两个错误 case 项;U+06C2 和 U+06C3 现在走 passthrough 路径,原 codepoint 不变 emit。v2.119.52 的 joining-class 条目(U+06C2 归 D / U+06C3 归 R)保留不变,让这两个字符在词中时邻居仍能正确选择位置形态。本修复仅影响在 AutoShapeArabic 开启时含 U+06C2 或 U+06C3 的 PDF;不含这两个码点的 PDF 与 v2.119.55 输出字节级一致。配套发布的 v2.119.57 把 Forms-A 覆盖扩到更多 Persian / Urdu / Sindhi / Kashmiri / Uyghur / Kazakh / Kyrgyz 字母,包含本次错误所混淆的真正具备 Forms-A 编码的 HEH DOACHASHMEE 和 YEH BARREE。

2026-05-22 Version 2.119.55

  • Devanagari Indic shaping capability:THotPDF 新增两个公开方法覆盖与 Arabic / Syriac / Mongolian 4 位置 joining 模型不同的 Indic 成形范式。GetDevanagariCategory (CP) 按 Unicode 16.0 §12.1 + IndicSyllabicCategory.txt 返回简化 Indic 音节类别 (0 = Other / 1 = Consonant / 2 = Independent Vowel / 3 = Matra / 4 = Virama / 5 = Nukta / 6 = Bindu / 7 = Visarga / 8 = Danda / 9 = Digit / 10 = ZWJ / 11 = ZWNJ / 12 = Modifier);表覆盖 Devanagari 块全部 U+0900-U+097F,含 Marwari / Sindhi / Vedic 扩展 U+0978-U+097F。
  • ApplyDevanagariReorder (Wide) 从左到右走遍输入串,对每个 Devanagari 音节应用 Indic reorder pre-pass,返回适合 cmap + GSUB 消费的重排 UnicodeString。非 Devanagari 内容(Latin、数字、标点、其他脚本)字节级穿透。实现两个主导 Devanagari 重排规则:(1) Repha —— 当音节以 Ra (U+0930) + Halant (U+094D) + 辅音开头时,把 (Ra, Halant) 对移到音节末尾,让字体的 'rphf' GSUB feature 替换为视觉上贴在音节基字符右上方的 Repha 字形;(2) 前置 I-matra —— U+093F 移到音节最前(如有 Repha 删除之后),让从左到右的 GSUB 处理在辅音聚合前看到 i-matra,与视觉渲染顺序对齐。
  • 调用方流水线:ApplyDevanagariReorder 对逻辑顺序文本预重排 → SetGSUBScript ('deva') → 用 GetSingleSubstituteGlyph + ApplyLigatureSubstitution + ApplyContextualSubst 应用 GSUB feature ('nukt' / 'akhn' / 'rphf' / 'rkrf' / 'half' / 'vatu' / 'cjct' / 'pres' / 'blws' / 'abvs' / 'psts' / 'haln') → emit 输出 GID 到 PDF 文本流,并对每个 emit GID 调用 MarkUnicodeGlyphUsed 走 v2.84.0 子集化器闭包。Phase A (v2.119.52 Arabic Extended-A 收尾) + Phase B (v2.119.53 Syriac capability) + Phase C (v2.119.54 Mongolian capability) + Phase D (v2.119.55 Devanagari Indic capability) 全部落地后,矩阵 gap "Mongolian / Syriac / Devanagari shaping" 完全关闭。Scope 边界:非 Devanagari Indic 脚本(Bengali / Tamil / Telugu / Gujarati 等各有自己的类别数据和重排规则)、Repha + 前置 I-matra 之外的重排规则,以及 TextOut / BuildUnicode*FieldContent 内自动 Devanagari 成形(留待 GSUB 引擎 roadmap Phase 8)均不在本版本范围。

2026-05-22 Version 2.119.54

  • Mongolian shaping capability:THotPDF 新增两个公开方法暴露 Mongolian (U+1800-U+18AF) joining-class 查询与 4 位置上下文分析,让调用方能在 producer 端驱动 Mongolian 文本成形。GetMongolianJoiningClass (CP) 按 Unicode 16.0 §13.5 返回 Mongolian joining 类 (0 = U 非连接、2 = D 双连接、4 = T 透明 / 变体选择器);表覆盖基本 Mongolian + Todo + Sibe + Manchu + Ali Gali 字母扩展 (U+1820-U+1878 + U+1887-U+18A8 + ALI GALI DAGALGA U+18A9 + MANCHU ALI GALI LHA U+18AA),并把 NIRUGU (U+180A)、三个 Free Variation Selectors FVS1-FVS3 (U+180B-U+180D) 和 Ali Gali BALUDA / THREE BALUDA 元音标记 (U+1885-U+1886) 归为透明类。GetMongolianPosition (Wide, Index) 在 0-based Index 处的字母上跳过透明标记走前后向,返回 0 = 孤立、1 = 末位、2 = 起位、3 = 中位。
  • 与 Syriac (v2.119.53) 一样,Mongolian 在 Unicode 中**没有**预编码的 Presentation Forms —— 每个支持 Mongolian 的字体(Mongolian Baiti、Noto Sans Mongolian、Noto Serif Mongolian 等)都通过其 'mong' script 标签下的 'init' / 'medi' / 'fina' / 'isol' OpenType GSUB feature 完成位置成形。所以 v2.119.54 继续走 v2.119.53 的能力层路径;调用方将位置输出与 v2.119.43-50 GSUB 引擎 API 组合(SetGSUBScript ('mong') → GetSingleSubstituteGlyph 配合对应位置 feature tag → MarkUnicodeGlyphUsed 让 v2.84.0 子集化器闭包)生成可直接 emit 到 PDF 文本流的成形 GID。
  • Mongolian 在原生书写方向上是竖排自上而下;walker 在**逻辑** codepoint 顺序上工作(codepoint 流中的前驱 / 后继),与视觉方向无关。实际的自上而下渲染在 PDF emit 时通过 /WMode 1 (竖排书写模式) 或字体矩阵旋转处理,而非 shaper 介入。Free Variation Selectors 归为透明类让 walker 在 FVS 跟在字母之后时仍能正确解算位置形态;字体的 GSUB 在 'fina' / 'medi' / 'init' / 'isol' feature 内的 lookup 在输入流含 FVS 时自动选取 FVS 限定变体。Devanagari (Indic) 成形留待后续版本,因为它需要按 UAX #38 做 Indic reorder pre-pass (virama / 辅音聚合 / pre-base 重排),与 Arabic / Syriac / Mongolian 的 4 位置 walker 模型不兼容。TextOut / BuildUnicode*FieldContent 内自动 Mongolian 成形留待 GSUB 引擎 roadmap Phase 8。

2026-05-22 Version 2.119.53

  • Syriac shaping capability:THotPDF 新增两个公开方法暴露 Syriac (U+0700-U+074F) joining-class 查询与 4 位置上下文分析,让调用方能在 producer 端驱动 Syriac 文本成形。GetSyriacJoiningClass (CP) 按 Unicode 16.0 SyriacShaping.txt 返回 joining 类 (0 = U 非连接、1 = R 右连接、2 = D 双连接、4 = T 透明 / NSM);表覆盖 Syriac 块全部 35 个字母(含 PERSIAN BHETH / GHAMAL / DHALATH 在 U+072D-U+072F、SOGDIAN ZHAIN / KHAPH / FE 在 U+074D-U+074F 等 Suriyani / Sogdian 扩展)并把 SUPERSCRIPT ALAPH (U+0711) 与 Syriac 组合标记 (U+0730-U+074A) 归为透明类。GetSyriacPosition (Wide, Index) 在 0-based Index 处的字母上跳过透明标记走前后向,返回 0 = 孤立、1 = 末位、2 = 起位、3 = 中位。
  • 与 Arabic 不同(Arabic 有预编码的 Presentation Forms-B U+FE70-U+FEFF 和 Forms-A U+FB50-U+FBFF 块,HotPDF 可直接把 codepoint 改写为成形变体),Syriac 块在 Unicode 中**没有**预编码的位置形态 —— 每个支持 Syriac 的字体(Estrangelo Edessa、Serto Jerusalem、East Syriac Adiabene、Noto Sans Syriac 等)都通过其 'init' / 'medi' / 'fina' / 'isol' OpenType GSUB feature 完成位置成形。所以 v2.119.53 仅提供能力层;调用方将位置输出与 v2.119.43-50 GSUB 引擎 API 组合(SetGSUBScript ('syrc') → GetSingleSubstituteGlyph 配合对应位置 feature tag → MarkUnicodeGlyphUsed 让 v2.84.0 子集化器闭包)生成可直接 emit 到 PDF 文本流的成形 GID。
  • Alaph 终态范围:Unicode §9.3 记载了两个上下文终态 feature ('fin2' 在 DALATH / RISH 之后应用,'fin3' 在 FINAL LAMADH 之后应用),Syriac 字体用它们切换 Alaph 末位字形。v2.119.53 把 Alaph (U+0710) 归为基本右连接,仅返回 0 (孤立) 或 1 (末位);需要 'fin2' / 'fin3' 区分的调用方用 ApplyContextualSubst 配以对应 feature tag 驱动字体的链式上下文 lookup 即可。Mongolian 和 Devanagari (Indic) 成形留待后续版本:Mongolian 用 Free Variation Selectors + vowel harmony,Devanagari 需 Indic reorder pre-pass per UAX #38,二者均不适用 Syriac 风格的 4 位置 walker。TextOut / BuildUnicode*FieldContent 内自动 Syriac 成形留待 GSUB 引擎 roadmap Phase 8。

2026-05-22 Version 2.119.52

  • Arabic 家族 shaping 扩展:v2.85.0 producer 端静态 Arabic shaper 现在覆盖 Arabic Extended-A 剩余字符 (ALEF WASLA U+0671、NOON GHUNNA U+06BA、HEH 变体 U+06C0-U+06C3)、Arabic Supplement 块 U+0750-U+077F (用于豪萨语、Wolofal 等非洲语言的非洲 Arabic 字母) 以及 Arabic Extended-A 新分配区 U+08A0-U+08FF (古兰经 Arabic + Wolofal/Hausa 扩展字母 + 古兰经组合标记)。Joining 类数据源 Unicode 16.0 ArabicShaping.txt,在 v2.85.0 四位置分析中应用,让相邻字母不论紧挨哪个 Arabic 变体都能正确选择 init / medi / fina / isol 位置形态。
  • 在 Arabic Presentation Forms-A 块 (U+FB50-U+FBFF) 有预编码静态 presentation form 的字母现在直接映射到对应形态,无需 OpenType GSUB shaper 介入:ALEF WASLA → FB50/FB51 (右连接,两形态)、NOON GHUNNA → FB9E/FB9F (按 Unicode 是双连接但只有两个预编码形态,init/medi 优雅降级为 isol)、HEH WITH YEH ABOVE → FBA4/FBA5 (右连接,两形态)、HEH GOAL → FBA6/FBA7/FBA8/FBA9 (双连接,完整四形态)、HEH GOAL WITH HAMZA ABOVE → FBAA/FBAB/FBAC/FBAD (双连接,完整四形态)、TEH MARBUTA GOAL → FBAE/FBAF (右连接,两形态)。合并 v2.119.32 LAM-ALEF 强制合字后处理与 v2.119.35 Persian/Urdu 核心 9 字母子集,静态 shaper 现在几乎覆盖全部现代 Arabic、Persian、Urdu、Sindhi、Pashto、Kurdish、Uyghur、古兰经及非洲 Arabic (Hausa、Wolofal 等) 文本,不依赖字体的 GSUB 表。
  • Arabic Supplement (U+0750-U+077F) 和较新的 Arabic Extended-A 字母段 (U+08A0-U+08C7) 没有预编码静态 presentation form;它们的 joining 类条目让相邻字母正确成形,但字母本身保持原始 codepoint,自身的位置成形依赖支持 GSUB 的字体 (或调用方驱动 v2.119.43-50 GSUB 引擎 API)。Arabic Extended-A 古兰经组合标记区 (U+08CA-U+08E1、U+08E3-U+08FF) 被归类为透明 (T 连接),让相邻位置分析自动跳过它们,与既有的 U+064B-U+065F harakat 处理一致。不含这些扩展字符的文本与 v2.119.51 输出字节级一致。Mongolian / Syriac / Devanagari shaping (与 Arabic 不同的 shaping 模型) 留待后续版本。

2026-05-22 Version 2.119.51

  • XFA (XML Forms Architecture) producer 端容器层支持:PDF 1.7 §12.7.6 + §12.7.8 允许 AcroForm 在静态 /Fields 数组之外(或替代)携带 /XFA 入口。新方法 THotPDF.AddXFAPacket(PacketName, XMLBytes) 注册一个命名 XFA packet —— 常用名称包括 'template'(表单布局 + 脚本)、'datasets'(数据绑定)、'config'(viewer 配置)、'connectionSet'、'localeSet'、'stylesheet'、'xfdf'、'xmpmeta'、'signature'、'sourceSet'。每个注册的 packet 在 EndDoc 时变成一个 FlateDecode 压缩的间接流,/XFA 数组按注册顺序交替排列 packet 名称 PDF 字符串和流引用,确保字节级可重现。THotPDF.ClearXFAPackets 和 THotPDF.XFAPacketCount 补齐 API 表面。
  • 仅容器层支持 —— HotPDF **不**解析 XFA XML、**不**实现 XFA 动态布局引擎、**不**执行 XFA template 内的 FormCalc / JavaScript、**不**验证 template 结构。调用方自行编写 XFA XML(通常用 Adobe LiveCycle Designer 或参照 XFA 3.3 规范手写),HotPDF 按字节原样发射。含 XFA 引擎的消费 reader(旧版 Acrobat / Reader、FormsCentral、某些 kiosk 产品)渲染表单;不含 XFA 引擎的 reader(Adobe Reader DC 2017 起、多数开源 reader)若文档是 AcroForm + XFA 混合工作流则显示 AcroForm 后备字段。
  • PDF/A 与 PDF/X 合规闸门:ISO 19005-1 §6.4.2 / ISO 19005-2 §6.4.2 / ISO 19005-3 §6.4.2 都明确禁止 XFA 表单(动态布局引擎无法确定性归档)。ISO 15930 印刷工作流完全拒绝交互式表单。AddXFAPacket 在任意非空 PDFACompliance 或 PDFXCompliance 下立即抛带 spec 引用的异常。重复 packet 名抛异常。空 PacketName 或空 XMLBytes 抛异常。RequirePDFVersion(pdf15) 自动 bump 文档版本。AcroForm 字典现在在 /Fields 至少一个条目或至少一个 XFA packet 已注册时发射;未注册 packet 的 pre-v2.119.51 调用方 /AcroForm 输出字节级一致。

2026-05-22 Version 2.119.50

  • TTF 子集化器对 GSUB substitute 字形的闭包:新方法 THotPDF.MarkUnicodeGlyphUsed(GID: Word) 不管 cmap 是否有 codepoint 能到达,都把该字形 opt-in 到 v2.84.0 EndDoc 子集化器。v2.84.0 closure 通过 cmap 从 FUnicodeUsedCps 派生 used-glyph 集合,所以所有 GSUB 引入的替换字形 —— stylistic alternates、合字、contextual 变体,Phase 1-6 查询 API 的全部输出 —— 之前都对子集化器不可见,消费 PDF reader 在它们位置渲染 .notdef。在 emit GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst 返回的任何 GID 到 PDF 文本流后,调用方现在为每个 emit 的 GID 调用一次 MarkUnicodeGlyphUsed(GID) 把它拉入嵌入子集。
  • helper 幂等(重复调用同一 GID 是 no-op)、防御(GID >= FUnicodeNumGlyphs 静默丢弃;任何 RegisterUnicodeTTF 成功之前的调用是 no-op)、并与 v2.84.0 _BuildSubsetTTF 内的复合字形闭包 pass 集成:调用方只需 mark 顶层 substitute GID —— 组件由现有 _TTFWalkCompositeClosure walk 自动拉入。集合在首次调用时懒分配,并在每个 RegisterUnicodeTTF('', nil) 时与其余子集状态一并重置为空。Phase 9 落地后,Phase 1-7 的每个 GSUB 查询 API 都能与子集 emission 安全配对,不会出现 substitute 字形从嵌入字体中掉队的问题。

2026-05-22 Version 2.119.49

  • OpenType GSUB Script / LangSys 选择 API:THotPDF 新增 5 个公开方法,让调用方把 GSUB 查询固定到特定 script 和 language,而非历史的 DFLT-script / 默认 LangSys fallback。SetGSUBScript ('latn' / 'arab' / 'cyrl' / 'hani' / 'kana' / 'deva' / 'beng' / 'taml' 等) 选择 OpenType script 标签;默认空字符串维持 v2.119.43-48 baseline(优先 'DFLT',否则首个 script)。SetGSUBLanguage ('ENG ' / 'TUR ' / 'AZE ' / 'JAN ' / 'KOR ' / 'ARA ' 等,调用方需空格填充到 4 字节) 选择 OpenType 语言标签;空字符串走 script 的默认 LangSys。两个设置跨 GSUB 查询持久存在,在 RegisterUnicodeTTF('', nil) 时复位为空。
  • 3 个新枚举 helper 暴露字体公布的内容:GetGSUBScripts: TGSUBStringArray 按 ScriptList 顺序返回每个 script 标签;GetGSUBLanguages(ScriptTag) 返回该 script 下的所有 language 标签(当 script 有默认 LangSys 时 index 0 是 '' 占位符);GetGSUBFeatures(ScriptTag, LangTag) 按 LangSys featureIndices 顺序返回 (script, language) 路径下的所有 feature 标签(如 'liga' / 'salt' / 'aalt' / 'ss01' 到 'ss20')。空 ScriptTag / LangTag 走与替换 API 相同的 DFLT-first / 默认 LangSys fallback,调用方可直接 GetGSUBFeatures('', '') 枚举默认路径下的 feature,不需要先看 script 列表。
  • 严格匹配 vs 回退语义:SetGSUBScript 设置为字体不公布的标签时,后续 GSUB 查询返回空 / no-op —— 引擎**不会**静默 fallback 到 DFLT,让调用方清楚知道选定 script 不可用。SetGSUBLanguage 设置为不认识的标签时**会**按 OpenType spec 建议 fallback 到 script 默认 LangSys。已有 7 个 GSUB 替换方法 (GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyphCount / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst) 公开签名字节级稳定,仅内部 `_GSUBFindFeatureLookups` helper 增加 2 个尾部参数,所有 7 个调用点同步路由。Script / LangSys API + LookupType 1-8 矩阵全部就位后,GSUB 引擎对 capability-only 使用已经 feature-complete;剩余 roadmap phase(自动 shaping pipeline + TTF subsetter 闭包)面向 producer-side 集成,不增加新查询面。

2026-05-22 Version 2.119.48

  • OpenType GSUB Reverse Chained Contextual Single Substitution (LookupType 8):新方法 THotPDF.ApplyReverseChainedContextualSubst(const InputGIDs; StartIndex; FeatureTag; out OutGID): Boolean —— GSUB 最后一个 LookupType 落地。Type 8 做上下文相关的 1:1 单字替换,替换字形由输入字形的 Coverage 索引选定,特点是调用方必须以"反向"顺序(从末尾向起始)扫描多字形序列 —— 因为每个替换字形可能依赖未来 lookahead 上下文,这些位置必须尚未被替换。helper 是单点 applier,调用方负责驱动反向 scan loop。Coverage Format 1 + 2、LookupFlag honor(input + backtrack + lookahead 全部 skip-aware)、Extension 包装(LookupType 7)都支持。
  • 典型用户:某些 Arabic / Syriac / N'Ko / Indic 上下文替代形态,其终态依赖后续字形的具体形状。一些字体也用于 Latin 序列消歧。与 LookupType 5/6 不同,Type 8 没有 nested-lookup 机制 —— 替换本质就是 1:1。未命中返回 False、OutGID = InputGIDs[StartIndex](best-effort 透传,与 v2.119.43 GetSingleSubstituteGlyph 契约一致)。
  • LookupType 矩阵收口:v2.119.48 起,OpenType GSUB 全部 8 个 LookupType (1-8) 都在 THotPDF 上有专用公开 capability。Script / LangSys 选择 API、自动 shaping pipeline 接入、TTF 子集化器自动拉 substitute 字形 留待 Phase 7-9;调用方仍需手动把选中的替换字形 mark 进字体子集,且自行驱动反向 scan loop。

2026-05-22 Version 2.119.47

  • OpenType GSUB Contextual + Chained Contextual Substitution (LookupType 5 + 6):新方法 THotPDF.ApplyContextualSubst(const InputGIDs; StartIndex; FeatureTag; var OutGIDs; out ConsumedLen): Boolean 是统一入口,覆盖 LookupType 5(仅 input 上下文)和 LookupType 6(backtrack / input / lookahead 三段上下文),并支持全部 3 个子表 Format —— Format 1(字面 glyph ID 序列)、Format 2(ClassDef 类序列)、Format 3(Coverage 表序列,现代字体最常用)。典型用户:'rclt' (Required Contextual Alternates — 字体通过 GSUB 驱动的 Arabic init/medi/fina/isol 位置形态)、'clig' (Contextual Ligatures)、'calt' (Contextual Alternates)、Indic 系列 ('pres' / 'blws' / 'psts' / 'half' / 'pstf' / 'cjct')。
  • SequenceLookupRecord nested lookup 派发:contextual rule 命中后,SequenceLookupRecord 条目分别在匹配的 input 子序列特定位置触发 nested lookup。新 helper `_GSUBApplyNestedLookup` 重新进入 GSUB LookupList,透明解 Extension 包装,派发到 Single (Type 1) / Multiple (Type 2) / Alternate (Type 3,取 index 0) / Ligature (Type 4) applier。dispatcher 跟踪 live MatchPositions[],让后续 SequenceLookupRecord 在 Multiple Substitution 1→N 扩张或 Ligature Substitution N→1 折叠后仍能正确寻址 working position。Nested LookupType 5 / 6 / 8(递归 contextual)刻意延后;多数实际字体把 nested lookup 接 Type 1 / 4。
  • 命中时返回 True + 应替换 InputGIDs[StartIndex..StartIndex+ConsumedLen-1] 的输出字形序列。OutGIDs 长度可能与 ConsumedLen 不同(nested 1→N 或 N→1 lookup 触发时)。ConsumedLen 始终是 contextual rule 吸收的总 input 跨度(含 significant glyphs 之间被 LookupFlag 跳过的 marks);典型调用方按 ConsumedLen 推进 scan loop 并 emit OutGIDs。未命中返回 False、空 OutGIDs、ConsumedLen = 1 —— 与 v2.119.43-46 helper 一致的安全 no-op。LookupFlag honor (Phase 4) 贯穿 contextual match:backtrack / input / lookahead walk 全部跳过被 flag 标记应忽略的字形。本版本 Script / LangSys 仍固定为 DFLT default;选择 API 是 Phase 7 范围。

2026-05-22 Version 2.119.46

  • OpenType GSUB Extension Lookups (LookupType 7):GSUB 引擎现在透明解包 Extension Substitution 子表 —— OpenType spec 为体积过大、LookupList Offset16 不够触达实际子表的字体定义的纯转向层。v2.119.43-45 的所有公开 API (GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyphCount / GetAlternateGlyph / ApplyLigatureSubstitution) 自动跟随 32-bit Offset32 转向找到真正的 LookupType 1/2/3/4 子表。这是解锁 GSUB 表超过 64 KB 的重型 CJK / Indic OpenType 字体(Noto Sans CJK / Noto Sans Devanagari 等)的关键 binary-layout 改造 —— 这类字体长期把 lookup 包在 LookupType 7 后面,之前在 HotPDF 看起来 feature 表是空的。
  • OpenType GDEF (Glyph Definition) 表解析:RegisterUnicodeTTF 现在在字体含 GDEF 表时也缓存。三个 GDEF 子表驱动 LookupFlag honor 逻辑 —— GlyphClassDef 把每个 GID 分类为 base(1) / ligature(2) / mark(3) / component(4);MarkAttachClassDef 给每个 mark 字形标注 attachment 类;MarkGlyphSetsDef (v1.2+) 声明命名的 mark 集合。ClassDef Format 1 (startGlyphID + classValueArray) 和 Format 2 (排序的 classRangeRecords) 都实现;GDEF v1.0 / v1.1 / v1.2 header 都接受(v1.3 itemVariationStore 忽略)。无 GDEF 表的字体退回 v2.119.43-45 baseline 行为("不忽略任何字形"),保持字节级稳定。
  • OpenType GSUB LookupFlag honor:5 个 GSUB 公开 API 现在都读取每个 Lookup 表的 LookupFlag(以及 LookupFlag.useMarkFilteringSet 时尾部跟随的可选 markFilteringSet uint16),跳过被标记应忽略的 input 字形。spec 定义的 flag bits 都尊重:0x0002 ignoreBaseGlyphs (跳过 GDEF.GlyphClassDef class 1)、0x0004 ignoreLigatures (跳过 class 2)、0x0008 ignoreMarks (跳过 class 3)、0x0010 useMarkFilteringSet (只有命名 MarkGlyphSet 内的 mark 参与),以及高字节 markAttachmentType (只有指定 MarkAttachClassDef 类的 mark 参与)。对 Ligature Substitution (LookupType 4) 影响最大:含 mark 字形分隔的 Arabic 'rlig' / Indic 'akhn' 合字(如 LAM + Fatha + ALEF)现在在 LookupFlag.ignoreMarks 设置时能正确匹配,返回的 ConsumedCount 包含被跳过的 mark 字形,调用方扫描循环正确跨过所有被吸收的 input 字形。0x0001 (rightToLeft) 是 GPOS 专用,GSUB lookup 仍忽略。
  • 沿用同样的防御性契约:无 GSUB 表的字体、非 4 字节 tag、字体 DFLT script 未公布的 feature、无子表覆盖的 GID、以及现在新增的 LookupFlag-ignored 输入字形 都返回 v2.119.43-45 的安全 no-op 结果。5 个公开 API 签名字节级稳定,仅内部 lookup walk 接入 Extension dispatch + LookupFlag honor + GDEF classification。Script / LangSys 选择 API、自动 shaping pipeline 接入、TTF 子集化器自动拉 substitute 字形 仍留待后续版本。

2026-05-22 Version 2.119.45

  • OpenType GSUB Ligature Substitution (LookupType 4):新方法 THotPDF.ApplyLigatureSubstitution(const InputGIDs: array of Word; StartIndex; FeatureTag; out OutGID; out ConsumedCount): Boolean 把多字形分量序列折叠成单个合字字形。典型用户:'liga' (Standard Ligatures — fi / fl / ffi / ffl)、'clig' (Contextual Ligatures)、'dlig' (Discretionary Ligatures)、'hlig' (Historical Ligatures)、'rlig' (Required Ligatures — 字体通过 GSUB 驱动 shaping 时的 Arabic LAM-ALEF 及类似情形)、Indic 系列合字 feature ('akhn' / 'pres' / 'blws' / 'psts')。调用方传入 post-cmap 字形 ID 序列和 0-based 起始位置;完整命中时返回 True、OutGID = 替换合字字形、ConsumedCount = 消耗的分量总数(实际通常 ≥ 2)。未命中返回 False、OutGID = 0、ConsumedCount = 1,让调用方步进一格继续扫描。实现遵循 OpenType 惯例 —— LigatureSet 通常按"长度优先"排列,让 prefix 重叠的字体(例如三分量 'ffi' 合字与二分量 'ff' 合字共存)拿到更长的匹配。沿用 v2.119.43/.44 helper 的防御性契约:空 / 非 4 字节 tag、无 GSUB、feature 未公布、StartIndex 越界、该位置无 LookupType 4 子表命中 全部返回安全 no-op (False / OutGID = 0 / ConsumedCount = 1)。LookupFlag mark-skipping、GDEF GlyphClassDef 集成、自动在 text emit 路径应用合字、TTF 子集化器自动拉合字字形 都留待后续版本。

2026-05-22 Version 2.119.44

  • OpenType GSUB Multiple Substitution (LookupType 2):新方法 THotPDF.GetMultipleSubstituteGlyphs(InputGID, FeatureTag, var OutGIDs) 把单个输入字形拆分为一串替换字形。典型用户是 'ccmp' (Glyph Composition / Decomposition) feature,用于把预组合的带音标拉丁字母拆成基础字形 + 组合符号,配合下游的 mark positioning 使用。helper 走与 v2.119.43 GetSingleSubstituteGlyph 相同的 DFLT script / 默认 LangSys / FeatureList / LookupList 路径,对 FeatureTag 关联的每个 LookupType 2 子表应用 InputGID,第一个命中为准。OutGIDs 在入口被清空,调用方不需要预清。spec 允许 Sequence 长度为 0(Unicode-canonical 字形删除),此情况返回 Result = True 且 Length(OutGIDs) = 0,调用方据此从文本流里删除该输入字形。
  • OpenType GSUB Alternate Substitution (LookupType 3):两个新方法让风格特性的完整替代字形循环可被显式访问。THotPDF.GetAlternateGlyphCount(InputGID, FeatureTag) 返回字体为 InputGID 在 FeatureTag 下提供的替代字形数量;THotPDF.GetAlternateGlyph(InputGID, FeatureTag, AlternateIndex) 返回该 0-base 索引处的替代字形。典型用户是 'aalt' (Access All Alternates)、'salt' (Stylistic Alternates)、'titl' (Titling Alternates) 和 'ss01' 到 'ss20' (Stylistic Sets 1-20),这些 feature 用 LookupType 3 而非 LookupType 1 实现时,v2.119.43 的 GetSingleSubstituteGlyph 会静默跳过相应子表 —— 'aalt' 调用方之前只能看到 LookupType 1 的传递替换。这对方法让调用方显式遍历装饰字形循环(不同形态的 'a' / 'g' / '7')、Titling 大写变体、Stylistic Set 替代。AlternateIndex 越界 / 负值返回 InputGID 原值让调用方安全探测。
  • 两个 helper 沿用 v2.119.43 的防御性契约:无 GSUB 表的字体、非 4 字节 tag、字体 DFLT script 未公布的 feature、无 LookupType 2 / 3 子表覆盖的 GID 都返回安全 no-op(Multiple 返回 False + 空 OutGIDs;Alternate 返回 0 / InputGID)。feature 链路上遇到的 LookupType 1 / 4-8 子表静默跳过。LookupFlag mark-filtering、GDEF GlyphClassDef 集成、Script / LangSys 选择 API、自动 shaping pipeline 接入、TTF 子集化器自动拉所选 substitute 字形仍留待后续版本;调用方仍需要手动 mark 所选字形进字体子集。

2026-05-22 Version 2.119.43

  • OpenType GSUB stylistic-alternates 查询:新方法 THotPDF.GetSingleSubstituteGlyph(InputGID, FeatureTag) 走字体的 GSUB ScriptList / FeatureList / LookupList 链路(DFLT script 默认 LangSys),应用 FeatureTag 关联的每个 LookupType 1 (Single Substitution) 子表,返回替换后的字形 ID。最常用的 stylistic-alternates feature tag 包括 'salt' (Stylistic Alternates)、'ss01' 到 'ss20' (Stylistic Sets 1-20)、'aalt' (Access All Alternates)、'titl' (Titling Alternates)、'subs' / 'sups' (上标/下标)、'frac' (分数)、'ordn' (序数);任意 4 字节 OpenType feature tag 只要其 feature 链路含 LookupType 1 子表即可支持。Coverage Format 1(已排序字形数组,二分查找)和 Format 2(range records)均支持。Single Substitution Format 1(delta 增量)和 Format 2(替换字形数组)均实现。RegisterUnicodeTTF 现在与 cmap 一起缓存 GSUB 表偏移/长度,查询时不需重新扫表目录。无 GSUB 表的字体、传入字体 DFLT script 不公布的 4 字节 tag、GID 不在 lookup Coverage 内的情况都返回 InputGID 原值,让 API 作为"尽力替换或透传"安全调用。LookupType 2-8 子表在 feature 链路上遇到时静默跳过 —— Multiple / Alternate / Ligature Substitution、Contextual 链路、自动 shaping pipeline 集成留待后续版本。

2026-05-22 Version 2.119.42

  • AcroForm /DR Resources 现在支持多个 Unicode 字体。v2.56.0 SetFormUnicodeFontDict 仅追踪单个调用方提供的字体(作为 AcroForm /DA 默认字体),需要按字段切换脚本(阿拉伯 + CJK + 拉丁混合表单)的场景之前只能手工拼 /DR /Font 字典。新方法 THotPDF.RegisterAcroFormFont(LogicalName, FontDict) 按任意逻辑名注册附加 Unicode 字体,使其可在 per-field /DA 字符串中通过 '/ 12 Tf' 引用,并在 AcroForm /DR /Font 字典中与 v2.56.0 默认字体一起发射。注册顺序保留确保 /DR 字节级可重现。与 SetFormUnicodeFontDict 默认字体或先前注册的字体同名抛带冲突名称的异常。空 LogicalName / nil FontDict 也抛异常。SetFormUnicodeFontDict('', nil) 重置路径同时清空附加字体注册表,让全新 Unicode 工作流从已知干净状态开始。PDF/A / PDF/X 全级别允许 —— 组合 Unicode 字体属于结构性表单元数据,非交互脚本。

2026-05-22 Version 2.119.41

  • PDF 1.7 §14.8.4 Table 333 标准 structure type 枚举:新增 THPDFStandardStructureType 枚举覆盖 ~40 个 spec 规定的结构 role,按 PDF role 分类分组(Grouping: Document / Part / Art / Sect / Div / BlockQuote / Caption / TOC / TOCI / Index / NonStruct / Private;Block-level: H / H1-H6 / P / L / LI / Lbl / LBody / Table / TR / TH / TD / THead / TBody / TFoot;Inline-level: Span / Quote / Note / Reference / BibEntry / Code / Link / Annot;Ruby + Warichu: Ruby / RB / RT / RP / Warichu / WT / WP;Illustration: Figure / Formula / Form)。两个 unit-level helper 将枚举转换为 spec-精确的 PDF 名称——StandardStructureTypeToName(T) 返回作为结构元素 /S 键的大小写敏感名称,IsStandardStructureType(Name) 测试任意 AnsiString 是否在标准集合内,便于调用方预检自定义 role 名并按需通过 AddStructRoleMap 路由。新增 AddStructureElement 枚举重载直接接受 THPDFStandardStructureType,消除字符串重载的拼写 / 大小写敏感隐患(如 'Para' vs 'P'、'Lbody' vs 'LBody'),转发到相同的底层实现路径。既有 AnsiString 重载对使用自定义或 RoleMap 路由名称的调用方保持字节级一致。

2026-05-22 Version 2.119.40

  • 结构元素属性 setter 三件套:新增 3 个 helper 与 v2.88.0 /Alt + /ActualText、v2.95.0 /ID 一起补齐 structure element 级 PDF 文本字符串属性。THotPDF.SetStructureElementLanguage(Elem, Lang) 写 PDF 1.7 §14.9.2 /Lang BCP-47 自然语言代码,覆盖该元素及其子孙的文档默认语言直到下一次 /Lang override —— 典型用法是引用其他语言的段落或多脚本混排区段。THotPDF.SetStructureElementExpansionText(Elem, ExpansionText) 写 §14.9.5 /E 缩写展开文本,屏幕阅读器在朗读时用展开内容代替原始缩写(如 "NASA" 朗读为 "National Aeronautics and Space Administration"),同时给内容提取工具使用。THotPDF.SetStructureElementTitle(Elem, Title) 写 §14.7.5.2 /T 标题,区分同一 role 下多个 sibling 元素(Acrobat Tags 面板会在 role 名旁显示 /T)。三个 setter 均简单实现:空值 no-op、nil Elem 抛异常;属性属于 structural metadata,PDF/A 与 PDF/X 全部级别允许。

2026-05-22 Version 2.119.39

  • AcroForm form-field trigger 系列收口:新增 3 个 helper 与 v2.119.37 AttachFieldKeyStrokeAction 一起覆盖 PDF 1.7 §12.7.5.3 Table 246 完整四个 trigger。THotPDF.AttachFieldFormatAction 装 /AA /F(format 事件,字段提交后显示前格式化 — 典型用法是 AFNumber_Format / AFDate_FormatEx 货币 / 日期 / 千分位掩码);THotPDF.AttachFieldValidateAction 装 /AA /V(validate 事件,用户提交新值后触发 — 典型用法是 event.rc := false 范围 / 正则校验拒绝非法输入);THotPDF.AttachFieldCalculateAction 装 /AA /C(calculate 事件,依赖字段变化时触发,依赖集由 Catalog /AcroForm /CO 计算顺序数组定义 — 典型用法是合计求和、税额计算)。四个 wrapper 共享同一 FFormFields /T 查找、同一幂等 last-call-wins 替换、同一 PDF/A + PDF/X JavaScript 守卫。同一 /AA 字典上其他 trigger 键 (/E /X /D /U /Fo /Bl 注释触发器 + 三个 sibling form-field trigger) 保持不动。内部重构:AttachFieldKeyStrokeAction 现在委托给共享 _InstallFieldTriggerJSAction helper;v2.119.37 既有调用方看到的异常消息字节级一致。

2026-05-22 Version 2.119.38

  • RegisterUnicodeTTF 现在会从字体 cmap format 12 子表中捕获 Supplementary Multilingual Plane(SMP,U+10000-U+10FFFF)的 codepoint→glyph 映射。之前版本因为 codepoint→glyph 查找表是 BMP-sized 数组而静默丢弃 SMP 条目;v2.119.38 新增并行稀疏列表(FUnicodeCpToGidSMP)与 BMP 数组同步填充。新公开方法 GetUnicodeGlyphForCodepoint(Cp) 返回任意 U+10FFFF 以内 Unicode codepoint 的字形 ID(SMP 范围走二分查找),无映射时返回 0(.notdef)。这是 capability 第一刀:producer 端 hex 编码管线、/CIDToGIDMap 流、ToUnicode CMap 仍然 BMP-anchored,写入 SMP 字符到 PDF 文本流仍需调用方自行发射 UTF-16BE surrogate pair。完整 surrogate-aware 文本流 emit 留待后续版本。仅 format 4 子表的字体会得到空 SMP 列表。

2026-05-22 Version 2.119.37

  • 新增 AcroForm 键盘 trigger helper:THotPDF.AttachFieldKeyStrokeAction(FieldName, JavaScriptBody) 按 /T 查找命名表单字段,挂 /AA /K JavaScript action,PDF 阅读器在文本输入时每次按键触发执行;典型用途包括实时输入校验(数字 / 日期 / 正则)、输入掩码、其他字段编辑触发的计算字段连动。幂等设计:重复调用替换 /K 入口,同一 /AA 字典上其他 trigger 键(/F format / /V validate / /C recalc / /Bl blur / /Fo focus 等)保持不动。PDF/A 全级别(ISO 19005-1 §6.6.1、-2/-3 §6.5.1)+ PDF/X 全级别(ISO 15930 prepress)禁 JavaScript action,guard 启用时立即抛带 spec 引用的异常。底层对应 PDF 1.7 §12.6.3 trigger event 字典 + §12.7.5.3 Table 246 form-field /K 键。

2026-05-22 Version 2.119.36

  • BeginTaggedContent 新增 4 个可选参数,对接 PDF 1.7 §14.8.4 Table 322 marked-content sequence properties:Lang(BCP-47 自然语言代码)/ Alt(替代描述,非文本或装饰性内容用,屏幕阅读器消费)/ ActualText(实际文本,提供给文本提取 / 复制粘贴 / 屏幕阅读器替换视觉字形序列 —— 合字、装饰大写、风格化字形等场景必备)/ ExpansionText(/E 键,缩写展开文本)。4 个值与现有 MCID 一起发射到同一个 BDC inline properties dict;空值跳过对应 key,未传任何 attribute 的旧 2 参数调用方与 v2.119.35 字节级一致。PDF literal string 转义(圆括号 / 反斜杠)自动处理;含非 ASCII 内容(CJK ActualText、Indic Alt 等)调用方负责 pre-encode 为 UTF-16BE + U+FEFF BOM 字节并以 AnsiString 形式传入。配套低层 page-stream API BeginMarkedContentMCIDProps 同时暴露,便于不走高层 StructElem 通路、直接控制 BDC 的调用方使用。

2026-05-22 Version 2.119.35

  • Arabic Extended-A 块 Persian / Urdu 核心 9 字符子集接入 v2.85.0 producer-side 4-position shaper。AutoShapeArabic 启用时,以下字符按与基础 Arabic 块相同的 joining-context 算法映射到 U+FB50-U+FBFF Arabic Presentation Forms-A 块字形:PEH U+067E、TCHEH U+0686、JEH U+0698、KEHEH U+06A9、GAF U+06AF、FARSI YEH U+06CC(Persian 核心)+ TTEH U+0679、DDAL U+0688、RREH U+0691(Urdu retroflex 核心)。Dual-joining 字符提供完整 isolated / final / initial / medial 4 形态;right-joining 字符(JEH / DDAL / RREH)按 Arabic shaping 模型仅提供 isolated / final。Arabic Extended-A 其他字符(ALEF WASLA U+0671、NOON GHUNNA U+06BA、HEH 变体 U+06C0-U+06C3、完整 Arabic Supplement U+0750-U+077F)保持 pass-through,留待后续版本扩展。非 Persian / Urdu 阿拉伯文输出与 v2.119.34 字节级一致。

2026-05-22 Version 2.119.34

  • PDF name 处理现在会在导入或复制 PDF 对象时解码 ISO 32000-1 的 #XX 转义序列。资源名、专色名、结构 role 名,以及 /PANTONE#20216#20CVC 这类 name object 会以预期字节值在 HotPDF 内部往返,不再因为复制时把 # 再次转义而输出成双重转义名称。

2026-05-22 Version 2.119.33

  • PDF 日期处理更贴近 ISO 32000-1 date string 规则。文档信息、XMP 派生日期和签名时间戳现在使用调用方传入的 TDateTime,不再静默替换为当前系统时间;解析器也能接受标准的可选日期字段和时区后缀,同时保持本地日期/时间字段不被破坏。
  • PDF 导入过滤器处理新增显式 ASCIIHexDecode、ASCII85Decode 与 RunLengthDecode 辅助函数,LZWDecode 默认 EarlyChange 也调整为 PDF 默认值 1。导入或复制使用旧式 PDF stream filter 的页面时兼容性更好。

2026-05-21 Version 2.119.32

  • 阿拉伯文 LAM-ALEF 强制合字折叠落到 v2.85.0 producer-side shaper 内部。当 AutoShapeArabic 启用时,任何 U+0644 LAM(raw 或四种 shaped 形态 FEDD-FEE0 之一)跟随四种 ALEF 变体之一(MADDA U+0622、HAMZA-above U+0623、HAMZA-below U+0625、plain U+0627;raw 或 post-shape 形态)的序列,会被折叠为 U+FEF5-U+FEFC 块的单个合字字形。合字 connected 与 isolated 形态依据 LAM 是否已被 4-position walker shape 到 final (FEDE) 或 medial (FEE0) 形态来选择。其他阿拉伯文强制合字(Allah U+FDFB、各装饰性敬语合字)仍需完整 GSUB 引擎,不在 static-table shaping 模型范围内。不含 LAM-ALEF 的文本和未启用 AutoShapeArabic 的调用方与 v2.119.31 字节级一致。三个 AcroForm Unicode /AP 辅助函数(单行 / 多行 / comb 分格)通过共享 _ApplyArabicShaping 入口自动继承本变更。

2026-05-21 Version 2.119.31

  • 新增 DevExpress ExpressPrinting System 导出适配器:dxHotPDFExportReportLinkToFile / dxHotPDFExportReportLinkToStream 接收任何 TBasedxReportLink(桥接 cxGrid / cxRichEdit / cxScheduler / cxPivotGrid 等可打印组件到 TdxComponentPrinter 的抽象链路家族),把每页渲染输送进 HotPDF 而非 DevExpress 自带的 dxPSExportToPDF emitter。适配器镜像 DevExpress 内部导出循环:RebuildReport → for 每页 → PaintPage 到 TMetafileCanvas → ShowEnhancedMetafile 进 HotPDF。Options record 暴露 Title / Author / Subject / Keywords / PDFVersion / Compression / RenderDPI。与 FastReport / QuickReport / ReportBuilder 适配器相同的 metafile 桥限制:无 AcroForm 字段、无大纲、无 PDF/A / PDF/X 一致性闸门。适配器位于 Lib/Addons/DevExpress/,不进入主 HotPDF*.dpk。

2026-05-20 Version 2.119.30

  • 新增 ReportBuilder 设备类:TppHotPDFDevice 派生自 TppGraphicsDevice,挂载到 ReportBuilder 标准 publisher → device 链路(赋值给 TppReport.PrinterDevice 或 TppPublisher.Device)。每个页面渲染到一个临时 TMetafileCanvas(在 BeforeRenderPage 创建、AfterRenderPage finalize),捕获为 TMetafile 后通过 HotPDF EMF 导入(HPDFEmf.ShowEnhancedMetafile)输出。属性暴露 Title / Author / Subject / Keywords / PDFVersion / CompressionLevel / Compressed;SetOutputStream 把 PDF 字节路由到调用方提供的 TStream。与 FastReport / QuickReport 适配器相同的 metafile 桥限制:无 AcroForm 字段、无大纲、无 PDF/A / PDF/X 一致性闸门。适配器位于 Lib/Addons/ReportBuilder/,不进入主 HotPDF*.dpk。

2026-05-20 Version 2.119.29

  • 新增 QuickReport 导出过滤器:TQRHotPDFExportFilter 派生自 TQRExportFilter,无需修改 QuickReport 核心即可插入常规 MyReport.ExportToFilter(MyFilterInstance) 工作流。适配器收割 QuickReport 已经缓存在 QRPrinter.PageList 中的每页 TMetafile,逐个通过 HotPDF 的 EMF 导入(HPDFEmf.ShowEnhancedMetafile)写出。属性暴露 Title / Author / Subject / Keywords / PDFVersion / CompressionLevel / Compressed;SetOutputStream 把 PDF 字节路由到调用方提供的 TStream 而非磁盘。与 FastReport 适配器相同的 metafile 桥限制:无 AcroForm 字段、无大纲、无 PDF/A / PDF/X 一致性闸门。适配器位于 Lib/Addons/QuickReport/,不进入主 HotPDF*.dpk。

2026-05-20 Version 2.119.28

  • 新增 FastReport 4 / FastReport VCL 导出适配器:TfrxHotPDFExport 派生自 TfrxCustomExportFilter,无需修改 FastReport 核心即可插入常规 MyReport.Export(MyExportInstance) 工作流。适配器把每个 prepared page 通过 HotPDF 的 EMF 导入路径(FastReport TfrxPreviewPages.DrawPage → TMetafileCanvas → HotPDF.ShowEnhancedMetafile)渲染,因此 FastReport 标准布局引擎产生的矢量 + 位图页面内容无需逐对象 emit 代码即可保留。属性暴露 Title / Author / Subject / Keywords / PDFVersion / CompressionLevel / RenderDPI 控制最终 PDF。限制:无 AcroForm 字段、无大纲、无 PDF/A / PDF/X 一致性闸门(这些需要逐对象 emit 管道,未来版本规划)。适配器位于 Lib/Addons/FastReport4/,不进入主 HotPDF*.dpk;用户在自己工程中与 FastReport 对应包一起引用。

2026-05-20 Version 2.119.27

  • 新增端到端 PFX-based PDF 签名:THotPDF.SignPDFWithPFX(InputPDFPath, OutputPDFPath, PFXFilePath, Password) 加载一个已包含 AddSignedSignatureField 占位符的未签名 PDF,解析 PFX / PKCS#12 文件,对 /ByteRange 覆盖范围构造完整的 CMS SignedData (RFC 5652),并一次性写出已签名 PDF。同时提供 TStream-based 重载用于内存工作流。PDF /Contents 预留长度必须足够容纳 CMS DER 的 hex;默认 8192 字节即可覆盖 1024 / 2048-bit RSA。仅支持 PBES2 + AES-256-CBC 的 PFX 文件(与 v2.119.26 PKCS#12 解析器的限制一致)。
  • 新增 HPDFCMS 单元:CMS SignedData 构造器。HPDFCMSBuildSignedData 组装 ContentInfo → SignedData → SignerInfo,使用 id-data detached 内容、contentType + messageDigest + signingTime 签名属性(按 RFC 5652 §5.4 要求 DER 升序排序)、从 X.509 证书提取的 IssuerAndSerialNumber 签名者标识、RSA + SHA-256 签名算法,X.509 证书包装在 [0] IMPLICIT certificates 内。同时暴露 HPDFCMSSHA256ByteRanges 用于流式两段摘要、HPDFCMSBytesToHex 用于 /Contents 占位符注入。封闭了从 HPDFASN1(v2.119.23)+ HPDFRSA(v2.119.24)+ HPDFCrypt 扩展(v2.119.25)+ HPDFPFX(v2.119.26)起步的库内签名栈。

2026-05-20 Version 2.119.26

  • 新增 HPDFPFX 单元:PKCS#12 / PFX 容器解析器。读取 OpenSSL ≥ 3.0、Windows 11+ certutil 与 macOS Keychain Access 产生的 .pfx / .p12 文件,解密 PBES2(PBKDF2-HMAC-SHA-256 + AES-256-CBC)SafeBag 内容,提取 X.509 证书 DER 与 RSA 私钥参数。返回一个 THPDFPFXKeyMaterial 记录含 CertDER、Modulus、PublicExponent、PrivateExponent,下游 CMS / 签名代码可以直接消费。密码错误时通过错 AES key 之后的 PKCS#7 反填充失败被识别。Legacy PBE-SHA1-3DES 文件会抛带迁移提示的清晰错误(用 `-keypbe AES-256-CBC -certpbe AES-256-CBC` 重新导出)。

2026-05-20 Version 2.119.25

  • 新增密码学原语:AES-256 反向密码(AES256DecryptBlock + AES256CBCDecryptPKCS7 + AES256CBCDecryptNoPad,遵循 FIPS-197 §5.3,与既有的加密助手成对)、HMAC-SHA-256(RFC 2104)、PBKDF2-HMAC-SHA-256(RFC 8018 §5.2)。已通过 RFC 4231 HMAC 测试向量和标准 PBKDF2-SHA-256 参考输出验证。配合 v2.119.23 的 ASN.1 解码器和 v2.119.24 的 RSA 原语,完成了现代(OpenSSL ≥ 3.0 / Windows 11+)PKCS#12 / PFX 文件的 PBES2-AES-256 SafeBag 解密路径所需的全部密码学基础。

2026-05-20 Version 2.119.24

  • 新增 HPDFRSA 单元:多精度整数运算 + RSA 模幂 + PKCS#1 v1.5 EMSA 编码。大小足以签出真实世界使用的 2048 / 4096-bit RSA 密钥。配合 v2.119.23 的 ASN.1 解码器,完成了即将到来的库内 PFX 签名管道所需的密码学原语。还没有面向用户的 API 调用它 —— 这是内部基础设施,等待 PKCS#12 与 CMS 层。

2026-05-20 Version 2.119.23

  • 新增 HPDFASN1 单元:一个紧凑的 BER/DER 解码器,覆盖 HotPDF 读取 PKCS#12 密钥库、X.509 证书、CMS/PKCS#7 SignedData 容器所需的 ASN.1 子集。暴露 THPDFASN1Node(解析后的 Tag-Length-Value 描述符)、THPDFASN1ParseNode / ParseAt、Content / ToInteger / ToBigInt / ToOID / ToString 访问器,以及 FirstChild / NextSibling / Children 迭代器。显式拒绝 BER 的不定长形式和高 tag-number(≥ 31)编码,与 DER 严格子集保持一致。这是为后续版本中库内 PFX 签名管道准备的内部基础设施——还没有任何面向用户的 API 依赖它。

2026-05-20 Version 2.119.22

  • 新增 PolyLine + Polygon 注释扩展元数据重载:THPDFPage.AddPolylineAnnotation 与 AddPolygonAnnotation 增加两个新重载,接收一组 THPDFCurrPoint 顶点 + 可选的 THPDFPolyExtras 记录。Extras 暴露 ISO 32000-1 §12.5.6.9 全部可选条目——/IC 内部填色、/BE 云形边框(besSolid / besCloudy + /I 强度)、/IT 意图(PolyLineDimension / PolyLineFlight / PolygonCloud / PolygonDimension)、PolyLine 的 /LE 线端样式。用 HPDFDefaultPolyExtras() 拿零初始化的起点记录。新重载校验顶点数 ≥ 2,并按 /IT 取值动态 bump 文档版本(PDF 1.5 / 1.6 / 1.7)。原有的 interleaved-Single 数组重载继续编译保持不变。

2026-05-20 Version 2.119.21

  • 新增 Line 注释扩展元数据:AddLineAnnotation 增加第三个重载,接收 THPDFLineExtras 记录控制 PDF 1.6/1.7 §12.5.6.7 Table 175 全部可选条目——/IC 内部填色(实心箭头/菱形/方块)、/LL 引导线长度、/LLE 引导线延伸、/LLO 引导线偏移(PDF 1.7)、/Cap 标题标志 + /CP 标题位置(/Inline 或 /Top,PDF 1.7)+ /CO 标题偏移、/IT 意图(LineArrow / LineDimension)。用 HPDFDefaultLineExtras() 拿零初始化的记录基础上按需赋值。新重载按所需条目自动 bump 文档版本到 PDF 1.6(或 1.7 当 /LLO / /CP / /CO 被请求时)。原有两个重载继续发射字节级一致的输出,新重载只会写入调用方明确请求的条目。

2026-05-20 Version 2.119.20

  • 新增 baShow / baImportData 按钮动作:THPDFButtonAction 增加 baShow(baHide 的镜像,发射 /H false 重新显示已隐藏的控件)与 baImportData(/S /ImportData /F filespec,点击后载入 FDF 表单数据,ISO 32000-1 §12.7.5.4)。baShow 在 PDF/A 与 PDF/X 下都允许;baImportData 在 PDF/A(ISO 19005-1 §6.6.1)和 PDF/X(ISO 15930 印刷预检)下都拒绝。
  • 新增多字段 Hide/Show 重载:THPDFPage.AddPushButtonWithHideAction(FieldName, Caption, Targets, Hide, Rectangle, Flags) 接收一组目标字段名 + 一个显式 Hide 布尔,单个按钮可一次切换多个控件的可见性。空 Targets 数组会抛异常。PDF/A 与 PDF/X 都允许该重载(仅涉及控件可见性)。

2026-05-20 Version 2.119.19

  • 新增 SubmitForm /Flags 用户可控:THPDFPage.AddPushButtonWithSubmitAction(FieldName, Caption, URL, Rectangle, SubmitFlags) 通过新的 THPDFSubmitFormFlags 集合类型暴露 ISO 32000-1 §12.7.5.2 Table 237 全部位标志。调用方可以自由组合 sffExportFormatHTML / sffXFDF / sffSubmitPDF(提交格式)、sffGetMethod(HTTP GET 而非 POST)、sffIncludeAnnotations、sffSubmitCoordinates、sffCanonicalFormat、sffIncludeNoValueFields、sffIncludeAppendSaves、sffExclNonUserAnnots、sffEmbedForm 等位,替代旧 baSubmitURL 硬编码的 /Flags 0(FDF + POST)。已有的 AddPushButtonWithAction(.., baSubmitURL, ..) 仍发射 /Flags 0 保字节级兼容。PDF/A / PDF/X 守卫与 baSubmitURL 相同:PDF/A 允许,PDF/X 禁止。

2026-05-20 Version 2.119.18

  • 扩展按钮动作类型:THPDFButtonAction 增加 baNamed 与 baHide。baNamed 发射 /S /Named /N <name> 动作(ISO 32000-1 §12.6.4.11),适用于四个标准导航命令 NextPage / PrevPage / FirstPage / LastPage(PDF/A 按 ISO 19005-1 §6.6.1 全部允许)以及 Acrobat 扩展命令 Print、SaveAs。baHide 发射 /S /Hide /T <field> /H true 动作(§12.6.4.10),点击按钮时隐藏指定的表单字段。两种动作类型在 PDF/A 与 PDF/X 下都允许;只有 baJavaScript / baResetForm / baSubmitURL 保留之前的合规守卫。

2026-05-20 Version 2.119.17

  • 新增命名目的地注册表:THotPDF.RegisterNamedDestination(Name, PageIndex, FitMode, ...) 把一个符号化目的地写入 Catalog /Names /Dests 名字树(ISO 32000-1 §12.3.2.3)。GoTo / GoToR 动作和大纲 /Dest 条目此后可以按名字引用目的地,不用再写死页号,跨页插入/删除/调序时所有引用保持有效。新枚举 THPDFDestinationFitMode 覆盖 spec 全部 8 种 fit 模式(XYZ / Fit / FitH / FitV / FitR / FitB / FitBH / FitBV)。空 Name、越界 PageIndex、重复 Name 三类调用会抛异常;按需自动 bump 到 PDF 1.2。PDF/A / PDF/X 不 gate——导航辅助在所有 profile 都允许。

2026-05-20 Version 2.119.16

  • 新增 Line 注释 /LE 线端样式:THPDFPage.AddLineAnnotation 增加一个接受起点/终点线端样式的重载。新枚举 THPDFLineEndingStyle 暴露 ISO 32000-1 §12.5.6.7 Table 176 全集十个值:None / Square / Circle / Diamond / OpenArrow / ClosedArrow / Butt / ROpenArrow / RClosedArrow / Slash。Line 注释现在可以在 Adobe Acrobat / Foxit 里直接以箭头、尺寸标记或指向性提示符的形式渲染,调用方不再需要在页面上手画箭头。原四参数重载行为字节级保持不变。

2026-05-20 Version 2.119.15

  • 新增大纲(书签)样式支持:THPDFDocOutlineObject 增加 Color、Bold、Italic 三个属性(ISO 32000-1 §12.3.3 Table 153 /C 与 /F)。大纲面板现在可以用彩色和粗体/斜体强调书签,便于按章节分组、标注未完成或需注意的段落、以及匹配品牌样式。Color 默认 clNone(不发射 /C 条目,字节级兼容旧输出);Bold、Italic 默认 false。在 AddChild 后再设置任一属性会实时改写大纲字典;把 Color 改回 clNone 或者把 Bold/Italic 都改回 false 会移除已经写入的 /C / /F 条目。

2026-05-20 Version 2.119.14

  • 新增文档级命名 JavaScript 注册表:THotPDF 增加 RegisterDocumentJavaScript(Name, Body) 方法。每个注册项会被序列化到 Catalog /Names /JavaScript 名字树(ISO 32000-1 §7.7.4.4 + §12.6.4.16)作为一个独立的 /S /JavaScript /JS 字典。AcroForm 控件动作和文档打开动作可以通过阅读器(Acrobat、Foxit)的 JavaScript 引擎按名字调用注册好的函数。适用于让多个表单控件共享一组验证、格式化或计算函数,不必在每个 /A 动作里重复 JS 源码。该方法在任意 PDFACompliance 级别(ISO 19005-1 §6.6.1)和任意 PDFXCompliance 级别(ISO 15930 印刷预检)下抛异常,同时拒绝空 Name / Body 以及重复 Name 注册。

2026-05-20 Version 2.119.13

  • 新增页面缩略图支持:THPDFPage 增加 SetPageThumbnail(ImageIndex) / ClearPageThumbnail 两个方法。PDF 阅读器(Acrobat、Foxit、浏览器内 PDF 阅读器)会用页面字典的 /Thumb 条目(ISO 32000-1 §12.3.4)在导航面板里显示预先准备好的页面缩略图,省去阅读器自身光栅化整页内容的开销——对图文密集的归档文档尤其有用。该方法可直接复用通过 AddImage / AddImageFromFile 添加的任意图像,调用方准备好降采样的预览位图后一行就能挂上去;传 -1 或调用 ClearPageThumbnail 可移除已挂载的缩略图,越界的图像索引会抛出含明确信息的异常。

2026-05-20 Version 2.119.12

  • PDF/A 合规修复:当 PDFACompliance 在任意级别(PDF/A-1/2/3)启用时,AddPushButtonWithAction 现在会对 baJavaScript 和 baResetForm 动作类型抛出异常。ISO 19005-1 §6.6.1 / ISO 19005-2 §6.5.1 / ISO 19005-3 §6.5.1 在所有 PDF/A 级别下禁用 JavaScript、ResetForm 和 ImportData 动作。baSubmitURL(SubmitForm)、baURI 和 baNone 在 PDF/A 下仍然允许——SubmitForm 在 §6.6.1 允许的动作列表中。之前 v2.119.5 守卫只覆盖 Catalog /AA 和文档级 OpenAction 路径,按钮控件可以携带被禁动作而未被拦截。
  • PDF/X 合规修复:当 PDFXCompliance 在任意级别启用时,AddPushButtonWithAction 现在对 baJavaScript、baResetForm 和 baSubmitURL 动作类型抛出异常。ISO 15930 印刷预检工作流不允许交互式脚本、表单变更和向外部端点提交表单。

2026-05-20 Version 2.119.11

  • PDF/A 合规修复:PDF/A 注释类型守卫补全。AddScreenAnnotation、Add3DAnnotation、AddRichMediaAnnotation 现在在任意 PDF/A 级别(PDF/A-1/2/3)启用时抛出异常。ISO 19005-1 §6.5.2 / ISO 19005-2 §6.6.1 / ISO 19005-3 §6.6.1 在所有 PDF/A 级别下禁用 Screen、3D、RichMedia(多媒体 / Adobe Extension Level 3)注释子类型。同时镜像加 PDFXCompliance 守卫——ISO 15930 印刷预检工作流不支持多媒体子类型。
  • PDF/A-1 合规修复:当 PDFACompliance 设为 PDF/A-1 时,AddWatermarkAnnotation 和 AddRedactAnnotation 现在会抛出异常。Watermark(PDF 1.6)和 Redact(PDF 1.7)都是 PDF 1.4 之后引入的注释子类型,不兼容 PDF/A-1 的 PDF 1.4 基线(ISO 19005-1 §6.5.2)。PDF/A-2 和 PDF/A-3(PDF 1.7 基线)继续允许这两种注释类型。

2026-05-20 Version 2.119.10

  • PDF/A 合规修复:当 PDFACompliance 设为 PDF/A-1 时,AddImageWithSMask 和 AddImageWithSMask32 现在会抛出异常。ISO 19005-1:2005 §6.4 禁止所有透明效果;图像 /SMask 流激活软掩膜透明度合成,违反此规定。用户应将图像预先合成到背景色再传入 AddImage(),或改用 PDF/A-2 / PDF/A-3。PDF/A-2 和 PDF/A-3 继续支持图像软掩膜透明度。

2026-05-20 Version 2.119.9

  • PDF/A 合规修复:当 PDFACompliance 设为 PDF/A-1 时,SetTransparencyGroup 和 SetTransparencyGroupICC 现在会抛出异常。ISO 19005-1:2005 §6.4 禁止所有透明效果;页面级 /Group /S /Transparency 字典激活透明度合成模型,违反此规定。这两个方法在 v2.32.0 引入时没有加 PDF/A-1 守卫。PDF/A-2 和 PDF/A-3 支持透明度(ISO 19005-2/3 §6.3.8),继续允许。

2026-05-20 Version 2.119.8

  • PDF/A 合规修复:当 PDFACompliance 激活且提供了自定义半调字典时(即 HTDefaultName = false),RegisterHalftoneState 现在会抛出异常。ISO 19005-1:2005 §6.2.9 及 ISO 19005-2:2011 / ISO 19005-3:2012 §6.3.7 规定 ExtGState /HT 只允许使用 /Default 名字字面量;自定义半调字典(Type 1、5、6、10、16)在所有 PDF/A 级别下均被禁止。传入 HTDefaultName=True 以复位为默认半调在所有 PDF/A 级别下仍然允许。
  • PDF/A 合规修复:当 PDFACompliance 设为 PDF/A-1 且提供了自定义软掩膜字典时(SMaskDict 参数非 nil),RegisterSoftMaskState 现在会抛出异常。ISO 19005-1:2005 §6.4 禁止所有透明效果;非 None 的 /SMask 值会激活软掩膜透明度。传入 SMaskNone=True 以复位软掩膜仍然允许。PDF/A-2 和 PDF/A-3 继续支持软掩膜透明度。

2026-05-20 Version 2.119.7

  • PDF/A 合规修复:AddDocumentAttachment(通过 Catalog /Names /EmbeddedFiles 实现的文档级文件嵌入)在 PDF/A-1 或 PDF/A-3 模式下现在会抛出异常。ISO 19005-1:2005 §6.1.11 明确禁止 PDF/A-1 下在文档 Names 字典中出现 /EmbeddedFiles 键。对于 PDF/A-3,文档附件必须通过 AddPDFA3AssociatedFile() 注册,以包含 ISO 19005-3:2012 Annex E 要求的 Catalog /AF 数组和 /AFRelationship 键。PDF/A-2 继续允许文档级附件,但附件文件本身必须符合 PDF/A 规范。

2026-05-20 Version 2.119.6

  • PDF/A 合规修复:在 PDFACompliance 激活时使用 PDF 标准字体(Arial、Helvetica、Times New Roman、Courier New、Symbol、ZapfDingbats)且 StandardFontEmulation 开启时,现在会抛出描述性异常。ISO 19005-1:2005 §6.3 及 ISO 19005-2:2011 / ISO 19005-3:2012 §6.3 要求所有字体必须嵌入;标准字体模拟路径仅写出不嵌入的字体名引用,违反 §6.3。用户应将 StandardFontEmulation 设为 false,或使用 RegisterUnicodeTTF() 配合真实字体文件生成 PDF/A 文档。
  • PDF/A 合规修复:在 PDFACompliance 激活时,非标准字体现在总是强制嵌入,不受 FontEmbedding 属性设置影响。FontEmbedding = false 的性能优化在 PDF/A 模式下被静默覆盖,以满足 §6.3 的全量字体嵌入要求。

2026-05-20 Version 2.119.5

  • PDF/A 合规修复:当 PDFACompliance 激活时,JavaScript 动作及 Catalog 级 /AA(附加动作)字典现已被禁止。ISO 19005-1:2005 §6.6.1/§6.6.2 及 ISO 19005-2:2011 / ISO 19005-3:2012 §6.5.1/§6.6.2 禁止所有 JavaScript 动作类型,并禁止 Catalog 字典中出现 /AA 键。在 PDFACompliance 激活的情况下调用 SetActionScript 现在会抛出描述性异常。作为纵深防御,EndDoc 中的 OpenAction JavaScript 与 Catalog /AA 输出路径也加入了守卫,在 PDFACompliance 激活时静默跳过,确保即使通过内部路径也不会写入不合规的键。

2026-05-20 Version 2.119.4

  • PDF/A 合规修复:当 PDFACompliance 激活时,AcroForm /NeedAppearances 现在被强制为 false,不受 AutoFormAppearances 设置影响。ISO 19005-1:2005 §6.9 及 ISO 19005-2:2011 / ISO 19005-3:2012 §6.4 要求该键必须缺失或为 false。此前如果在 PDFACompliance 激活的同时禁用 AutoFormAppearances,/NeedAppearances 会被设为 true,导致生成的文件不合规。

2026-05-20 Version 2.119.3

  • PDF/A 合规修复:当 PDFACompliance 激活时,嵌入的 CIDFont 子集现在在其 FontDescriptor 上包含 /CIDSet 流。ISO 19005-1:2005 §6.3.5 及 ISO 19005-2:2011 / ISO 19005-3:2012 §6.2.11 要求 CIDFont 子集携带 /CIDSet 位流,指示嵌入字体程序中存在哪些 CID 值。HotPDF 使用 Identity-H 编码(CID 等于 Unicode 码点),因此 CIDSet 直接由文档中使用的码点集合构建。位流经 FlateDecode 压缩后作为 indirect 流对象挂到现有的 FontDescriptor 上。本次修复了所有 PDF/A 级别中此前未检测到的缺口(早期合规审计错误地标注此项已就位)。

2026-05-20 Version 2.119.2

  • PDF/A-2 与 PDF/A-3 合规修复:当 PDFACompliance 设为 PDF/A-2 或 PDF/A-3 级别时,非 Link / 非 Popup 注释类型(TextNotes、FreeText、Line、Square、Circle、Stamp、FileAttachment)现在自动发射 /AP /N 外观字典。ISO 19005-2:2011 §6.3.3 及 ISO 19005-3:2012 §6.3.3 要求每个此类注释必须至少有一个外观字典(/AP /N Form XObject)。当前发射一个最小空 Form XObject 满足结构要求;大多数 PDF 查看器对标准注释类型无论如何都使用内置渲染,不依赖 /AP 流内容。Link 和 Popup 注释按标准明确豁免,不受影响。此要求不适用于 PDF/A-1(ISO 19005-1 §6.5.3 仅在 /AP 存在时约束其格式,不强制要求存在)。

2026-05-20 Version 2.119.1

  • PDF/A 合规修复:所有非 Widget 注释类型(文本注释、印章、线条、矩形/椭圆、超链接、GoTo 跳转链接、GoToR 跨文档链接、URI 链接)在 PDFACompliance 激活时现在自动设置 /F Print 标志位(bit 3 = 1,值 4)。ISO 19005-1:2005 §6.5.3 及 ISO 19005-2:2011 / ISO 19005-3:2012 §6.3.2 要求每个注释字典必须包含 /F 键且 Print 位为 1。此前只有 Widget 注释(表单字段)设置了该标志,其余所有注释类型均未设置,导致生成文件不合规。
  • PDF/A-1 合规修复:可选内容组(图层)在 PDFACompliance 设为 PDF/A-1 级别('A' 或 'B')时现在被阻止。ISO 19005-1:2005 §6.1.13 禁止在 Catalog 字典中出现 /OCProperties 键;在 PDF/A-1 模式下调用 RegisterOptionalContentGroup 现在会抛出说明性异常,提示调用方在需要可选内容时改用 PDF/A-2 或更高版本。EndDoc 的 /OCProperties 写出也加了相应 gate 作为防御性保障。

2026-05-20 Version 2.119.0

  • XAdES-in-PDF 容器支持按 ETSI EN 319 142-2 V1.2.0 §6.2(B/C/D 系列收口 3 之 3)。把调用方提供的 XAdES 签名 XML 字节嵌入 PDF EmbeddedFile,把 Filespec 注册到 Catalog /AF 数组,按调用方指定的 PDF 2.0 枚举设 /AFRelationship。producer 端只做 wrapper——实际 XAdES 签名构造(XML-DSig + ETSI EN 319 132-1 QualifyingProperties + RFC 3161 时间戳嵌入 xades:UnsignedSignatureProperties)是调用方 XML 密码学库职责(Apache Santuario、.NET SignedXml、libxmlsec、带 XAdES 扩展的 Saxon 等)。
  • 新方法 `THotPDF.AddXAdESAssociatedFile(FileName, XMLBytes, Description, MimeType, Relationship)`。默认值:MimeType `'application/xml'`(常用 ASiC-E archive 用 `'application/vnd.etsi.asic-e+zip'`)、Relationship `'Source'`(XAdES 签名 XML 是文档主内容)。返回 indirect Filespec dict 供调用方挂额外元数据或自定义 Catalog 条目交叉引用。
  • 独立于 PDF/A:不像 v2.108 `AddPDFA3AssociatedFile`(需 PDFACompliance='3*'),本 helper 无 PDF/A gate——XAdES-in-PDF 按 PAdES Part 2 V1.2.0 §6 是独立 profile 族。
  • `_EscapePDFNameBytes` 关键 bug 修复(PDF 1.7 ISO 32000-1 §7.3.5 + Table 2)。之前 escape 只转换 [0x21..0x7E] 之外的字节 + `#`;PDF delimiter(`/ ( ) < > [ ] { } %`)spec 要求在 name 里 `#XX` 转义但被原样输出。`/Subtype /application/xml` 类名因此被严格阅读器解析为两个独立 name token(/application 后跟 /xml),破坏 EmbeddedFile /Subtype 识别。v2.119 按 spec 转义 10 个 delimiter;不含 delimiter 的 name(HotPDF 绝大多数 emit)保持字节相同。PDF/A-3 Annex E(v2.108)MIME 类型现在正确 emit(`/application#2Fxml` 替代损坏的 `/application/xml`)。
  • PAdES B/C/D 系列 3 commit 收口:v2.117(Extended profiles E-BES / E-EPES / E-LTV 按 Part 2 §5)+ v2.118(Seed Values 按 ISO 32000-1 §12.7.5.5 + Part 2 §4.2.6)+ v2.119(XAdES-in-PDF + delimiter escape 修复按 Part 2 §6.2)。加 v2.109 - v2.116,HotPDF 现在覆盖完整 PAdES producer 范围含 baseline profiles、extended profiles、document timestamp、DSS 验证信息、ESIC extensions、Seed Value 签名约束、XAdES-in-PDF 容器。
  • Win32 + Win64 clean compile;v2.108 - v2.118 baseline 全部重 compile 通过。新 smoke smoke_pades_xades 走 2 个正向路径(XAdES 签名 XML 作 /AFRelationship /Source + ASiC-E archive 作 /Data)加 4 个拒绝路径(空 FileName / 空 XMLBytes / 空 MimeType / 无效 Relationship)。Python verifier 走两 Filespec 端到端含 `/Subtype /application#2Fxml` 和 `/Subtype /application#2Fvnd.etsi.asic-e+zip` 转义形式。v2.108 PDF/A-3 / PAdES / PDF/X smokes 全部重跑通过。

2026-05-20 Version 2.118.0

  • PAdES Seed Values 按 ISO 32000-1 §12.7.5.5 + ETSI EN 319 142-1 V1.2.1 §5.5 + ETSI EN 319 142-2 V1.2.0 §4.2.6(B/C/D 系列第 2 刀,共 3 刀)。挂在签名字段上的 Seed Value (SV) 字典约束消费阅读器的签名工具在该特定字段可选什么——哪个 handler、哪个 SubFilter、哪个摘要算法、是否强制撤销信息、最低 PDF 版本。
  • 新 `THotPDF.AttachPAdESSeedValue(FieldName, SubFilters, DigestMethods, ForceSubFilter, ForceDigestMethod, AddRevInfo, MinPDFVersion, ForceMinPDFVersion)` 方法。在 AcroForm 字段列表查命名签名字段(必须已由 AddPAdESSignatureField 或 AddDocumentTimestampSignature 创建)并挂带调用方约束的 /SV 子字典。
  • Seed Value emit 条目(按 Table 234):
    • `/Type /SV`
    • `/SubFilter` Name 数组 — 例如 [/ETSI.CAdES.detached]
    • `/DigestMethod` Name 数组 — 例如 [/SHA256 /SHA384 /SHA512]
    • `/AddRevInfo` boolean — 签名工具必须在 CMS 里含 OCSP/CRL
    • `/V` 数字 — 最低 PDF 版本(例如 1.7)
    • `/Ff` 整数 bit 标志(Table 235)— 每个 Force* 参数置对应 bit 让签名工具**必须**遵守约束(bit 2 = force SubFilter, bit 3 = force V, bit 6 = force AddRevInfo, bit 7 = force DigestMethod)
  • 规范合规护栏:PAdES Part 2 V1.2.0 §4.2.6 禁止迫使签名工具违反 PAdES profile 的 seed value(例如指定 PKCS#1 SubFilter)。HotPDF producer 端不强制——调用方负责只传 spec 允许的值(SubFilter 用 `'adbe.pkcs7.detached'` 或 `'ETSI.CAdES.detached'`,DigestMethod 用 SHA-256+)。helper 文档注释明确该约束。
  • Win32 + Win64 clean compile;v2.108 - v2.117 baseline 全部重 compile 通过。新 smoke smoke_pades_seedvalue 创建 PAdES-B-T 字段然后挂完整 Seed Value 约束集(SubFilter 限定 ETSI.CAdES.detached、DigestMethod 限定 SHA256/384/512、AddRevInfo true、MinPDFVersion 1.7、全部 force bit 置位)。Python verifier 走 /SV 子字典断言每个条目形状;还走"未知 FieldName"拒绝路径。
  • 系列第 3 刀(v2.119):XAdES-in-PDF 容器按 Part 2 V1.2.0 §6 收口 B/C/D 系列。producer 端是个薄 wrapper 复用 HotPDF 既有 EmbeddedFile / Catalog /AF 管道(v2.108 PDF/A-3 helper)加上 PAdES Part 2 V1.2.0 §6.2.2 对 XAdES 签名 XML payload 期望的 /AFRelationship 语义。

2026-05-20 Version 2.117.0

  • PAdES 扩展 profile 按 ETSI EN 319 142-2 V1.2.0 §5(B/C/D 系列第 1 刀,共 3 刀)。Part 1 baseline (V1.2.1 §6 B-B / B-T / B-LT / B-LTA) 定义固定 CMS 属性组合;Part 2 §5 定义并行的"extended" profile 族(E-BES / E-EPES / E-LTV)含更高 CMS-属性可选性,供 baseline 之外需要灵活性的调用方(如多策略企业部署、PAdES 信封内的自定义 CAdES profile)。
  • `AddPAdESSignatureField` Profile 参数现在接受三个额外值,与四个 baseline 名并列:
    • `'E-BES'` — 基础 + 通过 signing-certificate ESS 属性的明确签名证书绑定(PAdES 信封内的 CAdES-BES 等价)。默认 16 KB /Contents 预算。
    • `'E-EPES'` — E-BES + 显式 signature-policy-identifier signed attribute。默认 16 KB;调用方通常传 18-20 KB 以容纳 policy OID + qualifier 字节。
    • `'E-LTV'` — 长期验证含 CMS-属性灵活的证书链 + 撤销信息嵌入。自动 upsize 到 20 KB 最低(镜像 B-LT)。
  • baseline 和 extended profile 的 producer 端 wire format 完全相同——七个全用 SubFilter `ETSI.CAdES.detached`、相同 /ByteRange + /Contents 哨兵模式、相同 DSS + ESIC 管道(v2.110 / v2.116)。级别差异完全在调用方密码学库产生的 CMS signed-attribute 组合里。Profile 字符串只影响校验诊断和默认 /Contents 预算;HotPDF 不在 PDF 字典本身里编码 CAdES-BES / CAdES-EPES / CAdES-T 之分。
  • PAdES helper 文档注释更新表面 Part 2 V1.2.0 §4.2 暴露的调用方责任:SHA-256+ 哈希选择(SHA-1 phase-out)、每个 Sig 字段一个 SignerInfo、不用 RFC 5755 attribute certificate、以及 E-EPES 要求包含 signature-policy-identifier signed attribute。caller-domain 项仍在 producer 范围外,但合约现在在 API 表面明确。
  • Win32 + Win64 clean compile;v2.108 - v2.116 baseline 全部重 compile 通过。smoke_pades_signature 扩展加 3 个新字段(SigEBES / SigEEPES / SigELTV)走每个新 Profile 值的 auto-upsize / 显式预算 / 默认预算路径。Python verifier 扩展走全部 7 个字段并断言 Catalog ESIC extension(v2.116 carry-forward)。
  • B/C/D 系列后续:v2.118 加 Seed Values (PDF 1.7 §12.7.5.5 + ETSI EN 319 142-1 V1.2.1 §5.5 + Part 2 V1.2.0 §4.2.6) 让 PAdES 签名字段声明 handler / SubFilter / digest-method 约束,消费阅读器签名时必须遵守。第 3 刀:v2.119 加 XAdES-in-PDF 容器支持按 Part 2 V1.2.0 §6(XAdES 签名 XML 作 EmbeddedFile 嵌入 + Catalog /AF 关联)。

2026-05-20 Version 2.116.0

  • PAdES 按最新 ETSI 规范 + eIDAS 实施决定刷新:ETSI EN 319 142-1 V1.2.1 (2024-01, 已发布) + ETSI EN 319 142-2 V1.2.0 (2025-03, draft 附加 profile + 扩展 profile 族 + XAdES-in-PDF) + ETSI TS 119 142-3 V1.1.1 (2016-12, PAdES-DTS 文档时间戳) + EU 2015/1506 (公共部门承认)。HotPDF v2.109 - v2.111 baseline(B-B / B-T / B-LT / B-LTA + standalone timestamp)继续满足同样的 wire format;本刀关闭新 spec 暴露的两个 producer 端缺口。
  • DSS 字典 `/Type "DSS"` 项 (EN 319 142-1 V1.2.1 §5.4.2.2)。/Type 项 optional 但存在时必须为 /DSS name。HotPDF 现在主动 emit 让先看 /Type 再走字典其余项的严格验证器立即识别。
  • ESIC extensions 字典 (TS 119 142-3 §5.1, recommended)。新 `EnsurePAdESESICExtensions` 方法在任意 PAdES helper 调用时幂等写入 Catalog:`<< /Extensions << /ESIC << /BaseVersion /1.7 /ExtensionLevel 1 >> >> >>`。验证器(Adobe Acrobat pre-flight / EU DSS / callas pdfaPilot)凭此项识别文档为 PAdES 容器并声明 PDF 1.7 + ESIC level 1 extensions。
  • `EnsurePAdESDSS` round-trip 路径关键 bug 修复。v2.116 之前 helper 每次调用通过 `Catalog.GetIndexedItem(FindValue('DSS'))` 重解析 DSS 字典,返回 Catalog 存储的 indirect-link THPDFLink 而不是真实 DSS dict。THPDFDictionaryObject 强转把指针类型搞错,导致第一次之后每个 `AddPAdESDSSCertificate` / `AddPAdESDSSOCSP` / `AddPAdESDSSCRL` / `AddPAdESDSSVRI` 调用都静默跳过数组 push(数组引用变 nil,FindValue 返回 -1,if-Idx 小于 0 early-exit 触发)。v2.116 修复用 `FPAdESDSSDict` 字段缓存真实 Dict 引用让后续调用直接命中缓存。smoke_pades_dss 现在正确 emit 2 证书 + 1 OCSP + 1 CRL + 1 VRI 条目(之前只有第一个证书进 /DSS,/OCSPs 和 /CRLs 都是空数组)。
  • PAdES helper 文档注释更新覆盖 Part 2 V1.2.0 §4.2 暴露的调用方责任:SHA-1 phase-out(调用方 CMS 应选 SHA-256+)、每个 PDF 签名只能一个 SignerInfo(Part 2 §4.2.1 h / Part 1 §4.1 a)、RFC 5755 attribute certificate 应避用(Part 2 §4.2 g)、PAdES-DTS 文档不应带 VRI 字典(TS 119 142-3 §6.3 h — DSS 足够)。HotPDF producer 端 wire format 通过结构本身已经满足这四条约束;注释表面告诉调用方他们的 CMS 构造库要做什么。
  • Win32 + Win64 clean compile;smoke_pades_signature / smoke_pades_dss / smoke_pades_doctimestamp 加 verifier 全部通过。Verifier 扩展:smoke_pades_dss 现在断言 `/Type /DSS` + Catalog `/Extensions /ESIC /BaseVersion /1.7 /ExtensionLevel 1`;smoke_pades_doctimestamp 断言同 ESIC extension 并接受 page-Y-flipped 零面积 /Rect(`[0 H 0 H]` H = 页高)。Audit doc + TechnicalNotes 刷新引用 EN 319 142-1 V1.2.1 / Part 2 V1.2.0 / TS 119 142-3 / EU 2015/1506。

2026-05-20 Version 2.115.0

  • PDF/X (ISO 15930) producer 系列收口 (4/4):禁用 annot + action 类型按 ISO 15930-1:2001 / ISO 15930-3:2002 / ISO 15930-7:2010 + ISO 32000-1 §12.5.6 / §12.6.4。PDF/X 印刷工作流只消费可见印刷内容——多媒体 annot、嵌入文件、交互 action 都不参与印刷,故消费 pre-flight 工具(Adobe Acrobat / Enfocus PitStop / callas pdfaPilot)会拒绝含这些功能的文档。
  • 禁用 annot 类型(任意 PDFXCompliance ∈ {X-1a, X-3, X-4} 下拒绝):
    • FileAttachment — 印刷压机不消费嵌入文件
    • Sound — 多媒体无印刷场景
    • Movie — 同上多媒体理由
    每个入口(AddFileAttachmentAnnotation / AddSoundAnnotation / AddMovieAnnotation)在任意 PDF/X profile 调用时抛带 spec 章节诊断 + PDFXCompliance 值。
  • 禁用 action 类型(任意 PDFXCompliance 下拒绝):Launch / JavaScript / SubmitForm / ImportData / Movie / Sound / ResetForm。印刷工作流不能触发任何外部程序——PDF 只被压机 / RIP 消费,从不执行。AddLaunchLink 在任意 PDF/X profile 下抛(其余 action HotPDF 无公开 API emit 路径自然满足)。
  • PDF/X 系列功能完整:v2.112(opt-in + XMP 身份 + Trapped /Name)+ v2.113(OutputIntent + ICC GTS_PDFX)+ v2.114(透明度 gate + PageBox 强制)+ v2.115(禁 annot/action)。三个业界主流 profile(X-1a:2001 / X-3:2002 / X-4:2010)全覆盖。调用方仍需自行编排页级印前内容(正确 CMYK 目标、v2.84 TTF subsetter 字体嵌入、TrimBox 与设计匹配、有出血时配 BleedBox)但结构性合规 gate 全部到位。
  • Win32 + Win64 clean compile;v2.108 - v2.114 baseline 全部重 compile 通过。smoke_pdfx_optin 扩展 4 个新拒绝测试(TestRejectFileAttachmentAnnot / TestRejectSoundAnnot / TestRejectMovieAnnot / TestRejectLaunchAction)——每个在 PDF/X 三 profile 之一下 emit 被禁类型确认干净拒绝。smoke 现共 11 个场景:3 个正向 emit + 8 个拒绝路径。
  • 本组完成两个并行多版本系列:PAdES(ETSI EN 319 142, v2.109 - v2.111, 3 commit, B-B / B-T / B-LT / B-LTA + standalone timestamp 全支持)和 PDF/X(ISO 15930, v2.112 - v2.115, 4 commit, X-1a / X-3 / X-4 producer 端结构合规 gate 全部到位)。

2026-05-20 Version 2.114.0

  • PDF/X (ISO 15930) producer 系列第三刀 (3/4):透明度 gate + PageBox 强制按 ISO 15930-1:2001 / ISO 15930-3:2002 / ISO 15930-7:2010 + ISO 32000-1 §14.11.2。本刀覆盖两个独立的页级限制;gate 拆分镜像 v2.103 PDF/A-1 透明 + 页特性模式。
  • 透明度 gate (§7.5):RegisterExtGState 在 PDFXCompliance 为 'X-1a' 或 'X-3' 时拒绝 fill alpha < 1 (/ca)、stroke alpha < 1 (/CA)、BlendMode 非 {Normal, Compatible}。PDF/X-1a (ISO 15930-1:2001) 和 PDF/X-3 (ISO 15930-3:2002) 强制 PDF 1.3 base,先于 PDF 1.4 透明度。PDF/X-4 (ISO 15930-7:2010, PDF 1.6 base) 明确允许透明度,故 X-4 下 gate 不开火。诊断解释成因 + 建议透明度需求时切到 X-4。
  • PageBox 强制 (§14.11.2):新方法 `EnsurePDFXPageBoxes` 走每页要求至少 /TrimBox 或 /ArtBox 其一。MediaBox 单独不够——它代表设计画布,而非印刷厂裁切所需的成品尺寸。缺 PageBox 抛带 1-base 页号、spec 章节号、推荐 SetTrimBox 调用签名的诊断。EndDoc PDFXCompliance gate 在 OutputIntent 检查之后自动调用 EnsurePDFXPageBoxes。
  • 典型调用方模式:设置 PDFXCompliance + Trapped + AddPDFXOutputIntent 后,文档每页必须包含 `CurrentPage.SetTrimBox(Left, Bottom, Right, Top)` 匹配成品尺寸(例如 US Letter 用 0 0 612 792,A4 用 0 0 595 842)。BleedBox 推荐但不强制。
  • Win32 + Win64 clean compile;v2.108 - v2.113 baseline 全部重 compile 通过。smoke_pdfx_optin 扩展:3 个 profile emit 中每个加 SetTrimBox 正向调用 + 缺 TrimBox 拒绝路径 + X-1a 透明度拒绝路径(RegisterExtGState ca=0.5 抛)+ X-4 透明度允许正向路径(同调用通过 + ExtGState 注册)。Python verifier 走每个 /Type /Page 字典断言至少含 /TrimBox 或 /ArtBox 其一。
  • 剩余 PDF/X slice:v2.115 收口禁用 action 类型(JavaScript / Launch / SubmitForm / ImportData / Movie / Sound)+ 禁用 annot 类型(Movie / Sound / FileAttachment / Screen)+ AcroForm /XFA 拒绝。印刷工作流不需要这些功能;消费 pre-flight 工具把它们视作硬失败。

2026-05-19 Version 2.113.0

  • PDF/X (ISO 15930) producer 系列第二刀 (2/4):OutputIntent + ICC profile 强制按 ISO 15930-1:2001 §6.2.2 / ISO 15930-3:2002 §6.2.2 / ISO 15930-7:2010 §6.2 + ISO 32000-1 §14.11.5。每个 PDF/X profile 强制至少一个 /Type /OutputIntent /S /GTS_PDFX 条目命名目标印刷条件 + 提供 /DestOutputProfile ICC profile 流——印刷厂用这个 ICC 把 PDF 与印刷机 / 纸 / 墨水组合做颜色匹配。
  • 新 helper `AddPDFXOutputIntent(OutputConditionIdentifier, Info, ICCProfileStream, NumComponents, AlternateCS)` 镜像 v2.102 PDF/A wrapper 但 emit GTS_PDFX 子类型而非 GTS_PDFA1。校验 ICCProfileStream 非 nil + OutputConditionIdentifier 非空;内调 RegisterICCProfile 嵌入 profile(自动按 NumComponents 配 /Alternate 备用色彩空间)+ AddOutputIntent 构建 OutputIntent 字典。
  • EndDoc 强制:PDFXCompliance 启用时, EnsurePDFXOutputIntent 走 OutputIntents 数组要求至少一个含 /S /GTS_PDFX + /DestOutputProfile 的条目。缺失抛带注册标识符建议(FOGRA39 用于单张胶印, CGATS TR 001 SWOP 用于北美卷筒, Japan Color 2001 Coated 用于亚洲市场)。
  • 典型 PDF/X 工作流:1) `Doc.PDFXCompliance := 'X-4'`,2) `Doc.Trapped := 'False'`,3) 把 CMYK ICC profile 流(FOGRA39 / SWOP 等)装入 TStream,4) `Doc.AddPDFXOutputIntent('FOGRA39 (ISO 12647-2:2004)', '', ICCStream, 4, 'DeviceCMYK')`,5) 设计内容,6) Doc.EndDoc emit OutputIntent 挂在 Catalog /OutputIntents 数组;消费 pre-flight 工具(Adobe Acrobat / Enfocus PitStop / callas pdfaPilot)校验色彩管理配对。
  • Win32 + Win64 clean compile;v2.108 / v2.109 / v2.110 / v2.111 / v2.112 baseline 全部重 compile 通过。smoke_pdfx_optin(原 v2.112)扩展:在 3 个 profile emit 中各注册一个假 ICC profile + AddPDFXOutputIntent(X-1a → FOGRA39 CMYK,X-3 → SWOP CMYK,X-4 → sRGB RGB)+ 加缺 OutputIntent 拒绝路径。Python verifier 扩展走每个字典确认 /Type /OutputIntent /S /GTS_PDFX + /DestOutputProfile 条目 emit 出来。
  • 剩余 PDF/X slice:v2.114 加透明度 gate(X-1a / X-3 拒绝 ExtGState fill/stroke alpha < 1 + BM 非 Normal;X-4 允许透明)+ PageBox 强制(每页需 TrimBox 或 ArtBox 按 §14.11.2);v2.115 收口禁用 action / annot / XFA gate(Movie / Sound / FileAttachment / Screen / JavaScript / Launch / SubmitForm / ImportData / XFA 都拒绝因为印刷工作流不需要)。

2026-05-19 Version 2.112.0

  • 启动 PDF/X (ISO 15930) 印刷预检 producer 系列。PDF/X 是把"press-ready"PDF 寄送商业印刷厂的 ISO 标准族;profile X-1a(严格 CMYK + 专色)、X-3(ICC 色彩管理)、X-4(PDF 1.6 透明度 + ICCN)三者业界主流。v2.112 是第一刀 (1/4):opt-in 属性 + 身份元数据。完整审计 + 4 版本路线图归档在 .superpowers/specs/2026-05-19-pdfx-compliance-audit.md。
  • 新 `PDFXCompliance: AnsiString` 属性在 THotPDF 上接受 `''`(禁用,默认)、`'X-1a'`(ISO 15930-1:2001)、`'X-3'`(ISO 15930-3:2002)、`'X-4'`(ISO 15930-7:2010)。设置非空值自动启用 HotPDF 能为该 profile 结构层满足的所有 producer 端要求。
  • 新 `Trapped: AnsiString` 属性(PDFXCompliance 启用时强制)接受 `'True'` / `'False'` / `'Unknown'`。PDF/X 全族 spec 强制此 DocInfo 项,让印刷厂知道 PDF 是否已 trap 或需印刷厂在印前处理 trap。按 ISO 32000-1 §14.11.6.1 emit 为 PDF /Name(不是字符串);例如 `<< /Trapped /False >>`。
  • XMP 元数据识别:packet 现在 advertise `xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/"` + `` 设为 spec 精确字符串(`PDF/X-1:2001`、`PDF/X-3:2002`、`PDF/X-4`)+(X-1a + X-4)`` 设为完整 profile 名(`PDF/X-1a:2001` 或 `PDF/X-4:2010`)。Adobe Acrobat pre-flight / Enfocus PitStop / callas pdfaPilot 识别文档为 PDF/X 候选。
  • EndDoc 校验:拒绝未知 profile 值、拒绝空 / 未识别 Trapped 值、要求 Title、自动 bump PDF 版本到 profile 强制最低(X-1a/X-3 → PDF 1.3,X-4 → PDF 1.6)。加密 + PDFXCompliance 硬冲突——调用 EnableEncrypt(或 `ActivateProtection := True`)抛带清晰诊断的异常说明 PDF/X 工作流必须能不带密码处理地审查每个对象。
  • 调用方仍需自行编排 OutputIntent + ICC profile + TrimBox/ArtBox + 有效印前色彩空间 + 正确字体嵌入(X-1a/X-3 避免透明度)才能完整 PDF/X 合规。v2.113 - v2.115 续刀关闭这些缺口:v2.113 加 `AddPDFXOutputIntent`(强制 /GTS_PDFX 意图),v2.114 加透明度 gate + PageBox 强制,v2.115 收口禁用 action/annot/XFA。
  • Win32 + Win64 clean compile;v2.108 / v2.109 / v2.110 / v2.111 baseline 全部重 compile 通过。新 smoke smoke_pdfx_optin emit 三个 PDF(每 profile 一个)并验证 XMP 命名空间 + GTS_PDFXVersion + GTS_PDFXConformance + DocInfo /Trapped /Name + 无效 profile / 空 Trapped / 未识别 Trapped / 加密冲突拒绝路径。
  • PAdES 系列注:v2.109 / v2.110 / v2.111 覆盖 B-B / B-T / B-LT / B-LTA + standalone timestamp 用例。原本计划的 v2.112(standalone ETSI.RFC3161 timestamp-only PDF)实际上是隐含的——v2.111 的 `AddDocumentTimestampSignature` 在没有内层签名的新文档上调用就是 timestamp-only PDF——故 PAdES 系列视为 3 commit 实质收口,v2.112 重新分配给 PDF/X。ETSI EN 319 142-1 baseline profile 都已支持。

2026-05-19 Version 2.111.0

  • PAdES producer 系列第三刀 (3/4):PAdES-B-LTA 文档时间戳签名支持,按 ISO 32000-1 §12.8.5 + ETSI EN 319 142-1 §5.7。文档时间戳是在长期验证签名完成**之后**叠加的第二个签名;它覆盖整个 LT 状态文件(原签名 + DSS),证明 LT 材料在权威时间存在。这是欧盟监管下超过原 CA 证书生命周期归档保存所需的最高级 profile。
  • 新 `THPDFPage.AddDocumentTimestampSignature(FieldName, ContentsBytes)` helper。与普通 PAdES 签名不同,文档时间戳:
    • SubFilter 是 `/ETSI.RFC3161`(RFC 3161 TimeStampToken,不是 CAdES)。
    • Widget 矩形零面积(时间戳无可见外观——纯加密元数据)。
    • 无 /Reason / /Location / /ContactName(这些信息在 TST signed-attrs 里,不在 sig dict)。
    • 默认 /Contents 字节预算 16 KB(调用方可传更小,1 KB 地板;含证书链的完整 RFC 3161 TST 通常 4-8 KB)。
  • Producer 范围止于 wire format:helper 创建 /Type /Sig 占位字典含时间戳专用 SubFilter + /ByteRange + /Contents 哨兵供 PreparePDFForSigning + InsertSignatureHex 后续修补。调用方从可信时间戳权威 (TSA, 例如 DigiCert / GlobalSign / FreeTSA HTTP POST) 获取真实 RFC 3161 TST 字节并注入。典型 PAdES-B-LTA 归档工作流叠加:1) emit PAdES-B-LT 基础签名 (v2.109),2) 填充 Catalog /DSS 含证书链 + OCSP/CRL (v2.110),3) 增量更新保存,4) 在 LT 状态字节上加文档时间戳字段 (v2.111),5) 按字节范围向 TSA 请求 TST,6) 注入 TST 字节。
  • Win32 + Win64 clean compile;v2.108 / v2.109 / v2.110 baseline 全部重 compile 通过。新 smoke smoke_pades_doctimestamp 通过新 helper emit PAdES-B-LT 'SigInner' 字段 + 'DocTS' 文档时间戳字段。Python verifier 断言内层签名 /SubFilter /ETSI.CAdES.detached、时间戳 widget /Rect 零面积、时间戳 /V 字典含 /SubFilter /ETSI.RFC3161 且无 /Reason / /Location / /ContactName、/Contents 是 16 KB 占位(32768 hex 字符)。
  • 剩余 PAdES slice:v2.112(可选)加独立 ETSI.RFC3161 timestamp-only PDF(无 CAdES 内层签名——纯时间戳文档容器)。

2026-05-19 Version 2.110.0

  • PAdES producer 系列第二刀 (2/4):Document Security Store (DSS) 字典支持,按 ISO 32000-2 §12.8.4.3 + ETSI EN 319 142-1 §5.6。PAdES-B-LT(和 B-LTA)要求验证信息——证书链、OCSP 响应、CRL——嵌入 PDF 本身,让签名在原签名时间之后多年仍可验证,无需再访问外部 PKI 服务器。
  • THotPDF 上的新 helper:
    • `EnsurePAdESDSS` — 懒创建 Catalog /DSS indirect dict 含其 /Certs、/OCSPs、/CRLs 数组和空 /VRI 子字典;后续调用返回既有字典。
    • `AddPAdESDSSCertificate(CertDERBytes)` — 把 DER 编码 X.509 证书作为 indirect 流附加到 /DSS /Certs;返回 indirect 流对象供 VRI 引用。
    • `AddPAdESDSSOCSP(OCSPDERBytes)` — 同上, OCSP BasicOCSPResponse 加到 /DSS /OCSPs。
    • `AddPAdESDSSCRL(CRLDERBytes)` — 同上, Certificate Revocation List 加到 /DSS /CRLs。
    • `AddPAdESDSSVRI(SigContentsHexSHA1, Certs, OCSPs, CRLs)` — 创建按签名的 VRI 条目,键是签名 /Contents 字节的 UPPERCASE hex SHA-1 digest (spec 强制形式);每条目含 /Cert / /OCSP / /CRL 数组引用上面创建的 indirect 流。
  • Producer 范围止于 wire format。调用方仍需计算最终签名 /Contents 的 SHA-1 并从 PKI 工具链提供 DER 编码证书 / OCSP 响应 / CRL。典型工作流:先构造 PAdES-B-T 基础签名 (v2.109),再向 DSS 加 certs / OCSPs / CRLs 支持长期验证,再附 VRI 条目将验证信息绑到具体签名。
  • Win32 + Win64 clean compile;v2.108 + v2.109 baseline 重 compile 通过。新 smoke smoke_pades_dss 创建 PAdES-B-LT 签名字段,向 DSS 加 2 个证书 + 1 个 OCSP + 1 个 CRL,并加一个带测试 hex digest 的 VRI 条目。Python verifier 走 DSS 字典结构:Catalog /DSS ref、/Certs / /OCSPs / /CRLs 数组数量、VRI 键匹配、VRI 条目的 cert/OCSP/CRL ref 匹配顶层 DSS 数组。
  • 剩余 PAdES slice:v2.111 实现 PAdES-B-LTA 文档时间戳签名(第二个 Sig 字段,SubFilter ETSI.RFC3161,覆盖整个 LT 文件);v2.112 (可选) 加 timestamp-only PDF。

2026-05-19 Version 2.109.0

  • 启动 PAdES (ETSI EN 319 142) 数字签名 producer 工作。v2.109 在既有 ISO 32000-1 §12.8 签名基础设施(v2 AddSignedSignatureField helper)之上加高层 wrapper,硬编码 PAdES 要求的 SubFilter 并校验合规 profile。完整审计 + 4 版本路线图归档在 .superpowers/specs/2026-05-19-pades-compliance-audit.md。
  • 新 `THPDFPage.AddPAdESSignatureField(FieldName, Rect, Profile, Reason, Location, ContactName, ContentsBytes, Flags)` helper。Profile 选择 ETSI EN 319 142-1 baseline 级别:'B-B'(基本 CAdES,无时间戳)、'B-T'(带 RFC 3161 可信时间戳,欧盟法律最低要求)、'B-LT'(带 DSS 验证信息,支持长期验证)、'B-LTA'(带文档时间戳签名,归档级)。四者都使用 SubFilter 'ETSI.CAdES.detached';profile 参数影响诊断和默认 /Contents 字节预算。其他 profile 字符串抛带 ETSI 枚举诊断。
  • 自动 upsize Contents 预算:PAdES-B-B + B-T 默认 16 KB(调用方可传更少,继承 v2.108 的 64 字节地板)。PAdES-B-LT 自动 upsize 到 20 KB 最小(CAdES + 证书链 + OCSP/CRL 数据)。PAdES-B-LTA 自动 upsize 到 24 KB 最小(B-LT + 文档时间戳)。调用方提供更大值直接通过;helper 只抬地板。
  • Producer 范围:wrapper 创建 /Type /Sig 占位字典含 /SubFilter /ETSI.CAdES.detached、/Reason / /Location / /ContactName / /M(签名日期),以及 /ByteRange + /Contents 哨兵供 PreparePDFForSigning + InsertSignatureHex 后续修补。实际 CMS / CAdES 字节、RFC 3161 时间戳获取、OCSP/CRL 收集、DSS 字典构造仍是调用方责任——需要 PDF producer 范围之外的外部加密库(OpenSSL、Bouncy Castle、Windows CAPI 等)。
  • Win32 + Win64 clean compile。新 smoke smoke_pades_signature emit 四个 PAdES 签名字段(B-B / B-T / B-LT / B-LTA)并确认无效 profile 值抛。Python verifier 断言 AcroForm /SigFlags 3、每个 widget 有 /FT /Sig + /V indirect ref 指向 /Type /Sig 字典含 /SubFilter /ETSI.CAdES.detached、/Contents 占位 hex 长度匹配预期(自动 upsize 后)字节预算。
  • 剩余 PAdES 路线图:v2.110 加 Catalog /DSS(Document Security Store)字典 builder 供 PAdES-B-LT 验证信息(证书链 + OCSP/CRL);v2.111 加独立文档时间戳签名字段供 PAdES-B-LTA 归档;v2.112(可选)加 ETSI.RFC3161 timestamp-only PDF。

2026-05-19 Version 2.108.0

  • PDF/A-2 + PDF/A-3 producer 系列收口 (4/4):ISO 19005-3 Annex E Associated Files。新高层 helper AddPDFA3AssociatedFile(FileName, MimeType, Description, Relationship, FileBytes) 把任意文件(XML 发票、源 spreadsheet、JSON 元数据等)附到 PDF/A-3 hybrid 容器并注册到 Catalog /AF 数组,带 §E.4 /AFRelationship 语义。
  • Hybrid 容器工作流:典型用例是 ZUGFeRD 风格的 PDF/A-3 发票——人类可读的发票渲染存在 PDF 页流,机器可读的 XML 源作为 Associated File 一起带。/AFRelationship 告诉消费者附件如何与可见内容关联:'Source'(原始源数据)、'Data'(PDF 可视化的原始数据)、'Alternative'(替代表示)、'Supplement'(补充材料)、'Unspecified'(其他)。
  • Spec gating:AddPDFA3AssociatedFile 在非 PDF/A-3 PDFACompliance 值下抛(Annex E 是 part-3 专有),诊断建议 PDF/A-2 用 AddFileAttachmentAnnotation,并提示 PDF/A-1 §6.1.11 完全禁止附件。空 FileName 或 MimeType 抛;无效 Relationship 值抛附 spec §E.4 枚举。
  • Emission 结构:indirect EmbeddedFile 流带 /Type /EmbeddedFile + /Subtype + /Params <> + 文件字节。Indirect Filespec 字典带 /Type /Filespec + /F + /UF(都 = FileName)+ 可选 /Desc + /EF <> + /AFRelationship(Annex E key)。Catalog /AF 数组首次附件时 lazy 创建,后续调用追加。返回的 Filespec 字典让调用方需要时加额外可选键(如 /CI 校验和信息)。
  • Win32 + Win64 clean compile。新 smoke smoke_pdfa3_associated 构造 PDF/A-3B hybrid 发票,附真实 XML 发票为 /AFRelationship Source,行使三个拒绝路径:PDF/A-1 拒绝、PDF/A-2 拒绝、无效 /AFRelationship 拒绝。Python verifier 断言 Filespec / EmbeddedFile 字典图(Type / F / UF / Desc / AFRelationship / EF F UF / EmbeddedFile Type / Subtype #2F 转义 / Params Size)。
  • PDF/A-2 + PDF/A-3 producer 面现已完全审计闭合(v2.105 wrapper opt-in + XMP、v2.106 禁用 annot/action、v2.107 §6.1.13 implementation limits、v2.108 Annex E Associated Files)。配合 PDF/A-1 (v2.101-104) 和 PDF/UA-1 (v2.94-v2.100) 已达 strict conformance,HotPDF 现在端到端覆盖 PDF/A-1/2/3 + PDF/UA-1 归档/无障碍 producer 完整矩阵。

2026-05-19 Version 2.107.0

  • PDF/A-2 + PDF/A-3 producer 系列第三刀 (3/4):ISO 19005-2 §6.1.13 + ISO 19005-3 §6.1.13 implementation limits 强制。PDF/A-1 §6.1.13 显著宽松(无硬字符串长度限制),所以新 gate 只在 PDFACompliance 为 PDF/A-2 或更高('2*' / '3*')时触发。
  • 新公开方法 EnsurePDFAImplementationLimits 校验文档调用方控制的值是否符合 spec 硬限制:Doc.Title / Author / Subject / Keywords / Lang 字符串 ≤ 32767 字节;每个 page MediaBox 维度落在 [3..14400] 单位内。EndDoc 在 PDFACompliance 为 PDF/A-2/3 时自动调用;调用方也可在 build 中段直接调获取早期诊断。
  • 审计报告记录自动满足的限制:integer 范围(-2³¹..2³¹-1)、real 范围(±3.403×10³⁸,近零 ≥1.175×10⁻³⁸)、name 长度 ≤127 字节、indirect 对象数 ≤8388607、q/Q 嵌套 ≤28、DeviceN colorant ≤32、CID ≤65535。HotPDF emission 路径不会超过这些限制因为它们绑定到 HotPDF 控制的 producer 数据结构。校验器聚焦调用方提供的字符串 + 页面边界,因为只有这些是现实世界调用方能突破的限制。
  • XFA 表单(§6.4.2):HotPDF 不 emit AcroForm /XFA 或 Catalog /NeedsRendering。跨 PDF/A-1/2/3 自动满足——审计报告标记,防止未来 producer 扩展重新引入。
  • .notdef glyph(PDF/A-2 §6.2.11.8):spec 禁止 text-showing 算子引用 .notdef。调用方使用注册的 Unicode TTF 字体(v2.74-v2.86 路径)时 HotPDF 字体处理自动避免——producer 把每个 codepoint 映射到真实 glyph,或拒绝注册。原始文本 emit 路径是调用方责任;审计报告记录。
  • XMP namespace(§6.6.2):spec 禁 XMP packet 头部的 bytes / encoding 属性。BuildXMPPacket emit 最小头部(id="W5M0MpCehiHzreSzNTczkc9d")不带任一属性——所有 PDF/A part 自动满足。
  • Win32 + Win64 clean compile。新 smoke smoke_pdfa2_limits 行使三个路径:PDF/A-2 限制内通过;PDF/A-1 含 32768 字节 Title 也通过(无 §6.1.13 gate);PDF/A-2 含 32768 字节 Title 被 spec 节诊断拒绝。所有 PDF/A-2/3 baseline 重 compile 通过。
  • 剩余 PDF/A-2/3 producer slice:v2.108 实现 PDF/A-3 Annex E Associated Files(PDF/A-3 专有 hybrid 容器特性:Catalog /AF 文件 specification 数组 + /AFRelationship key + EmbeddedFile /Params /CheckSum + /ModDate)。

2026-05-19 Version 2.106.0

  • PDF/A-2 + PDF/A-3 producer 系列第二刀 (2/4):覆盖 PDF/A-2 §6.3.1 + §6.5.1(与 PDF/A-3 §6.3.1 + §6.5.1 一致)。v2.104 PDF/A-1 禁用 annotation + action gate 本来就在任何 PDFACompliance 非空时触发,所以 PDF/A-2/3 下继续工作。v2.106 确认此行为、升级诊断消息引用所有 PDF/A part 共享 spec 节、并文档化 PDF/A-2 新增禁用类型中 HotPDF 自然满足的部分(无公开 emit 路径)。
  • 禁用 actions 表:PDF/A-1 禁 6 个(Launch / Sound / Movie / ResetForm / ImportData / JavaScript)。PDF/A-2/3 扩展到 13 个,新增 Hide / SetOCGState / Rendition / Trans / GoTo3DView + 已弃用的 set-state / no-op。AddLaunchLink 继续抛;HotPDF 无公开 emit 其他 12 个的入口,自动满足。
  • 禁用 annotation 类型表:PDF/A-1 禁 FileAttachment / Sound / Movie。PDF/A-2/3 解除 FileAttachment 禁(v2.105 已放宽)但新增 3D / Screen 禁。AddSoundAnnotation + AddMovieAnnotation 跨所有 PDF/A part 继续抛;HotPDF 无 3D / Screen emit 路径,自动满足。
  • 诊断消息升级:v2.104 三个 gate(AddSoundAnnotation、AddMovieAnnotation、AddLaunchLink)的异常文本现在引用三个相关 spec 节(如 "PDF/A-1 §6.5.2 / PDF/A-2 §6.3.1 / PDF/A-3 §6.3.1"),开发者直接看到统一禁用范围。AddLaunchLink 消息额外列出 PDF/A-2/3 扩展禁用 action 集。
  • Win32 + Win64 clean compile。新 smoke smoke_pdfa2_annot_action 在所有 6 个 PDF/A-2/3 level(2B、2U、2A、3B、3U、3A)下行使三个 gate;每次调用断言带 §6.3.1 / §6.5.1 诊断的预期异常。既有 PDF/A-1 baseline(smoke_pdfa1_compliance / smoke_pdfa1_forbidden / smoke_pdfa1_annot_action)和 v2.105 smoke_pdfa2_compliance 重 compile 通过。
  • 剩余 PDF/A-2/3 producer slice:v2.107 强制 ISO 19005-2 §6.1.13 implementation limits(integer / real / string / name 字节范围、q/Q 嵌套、DeviceN 颜色数、CID、page 边界)+ §6.4.2 XFA + §6.2.11 Type 3 / .notdef 严格;v2.108 实现 PDF/A-3 Annex E Associated Files(任意类型嵌入的 hybrid 容器 PDF)。

2026-05-19 Version 2.105.0

  • 启动 PDF/A-2 (ISO 19005-2:2011) + PDF/A-3 (ISO 19005-3:2012) producer 合规 4 版本系列。审计 + 路线图归档在 .superpowers/specs/2026-05-19-pdfa2-pdfa3-compliance-audit.md。v2.105 落地 wrapper 层 opt-in、XMP pdfaid namespace 扩展、PDF 版本自动 bump 至 1.7、以及对 v2.101-104 PDF/A-1 严格 gate 中 PDF/A-2/3 spec 明确允许的有选择性放宽。
  • PDFACompliance 属性在 PDF/A-1 的 '' / 'A' / 'B' 基础上新接受六个值:'2A' / '2B' / '2U'(PDF/A-2 A/B/U 级)和 '3A' / '3B' / '3U'(PDF/A-3 A/B/U 级)。Level U 是 PDF/A-2 引入的新等级"仅 Unicode 保留"(视觉 + Unicode 可提取文本,不要求 Tagged PDF)。
  • BuildXMPPacket 扩展: 元素现在反映解析的 part 号(1 / 2 / 3), 反映解析的级别字母(A / B / U)。veraPDF 等验证器按这对值识别文档的 PDF/A part。按 ISO 19005-2 §6.6.4 + ISO 19005-3 §6.6.4 Table 8。
  • 自动 bump:PDFACompliance '2*' / '3*' 自动 bump 文档版本到 PDF 1.7,因为 PDF/A-2/3 都基于 ISO 32000-1 (PDF 1.7)。PDF/A-1 保持锚定 PDF 1.4。
  • v2.103 透明度 gate 放宽:RegisterExtGState 仅在 PDFACompliance 为 PDF/A-1 strict('A' 或 'B')时对透明 alpha / 非 Normal blend mode 抛异常。PDF/A-2/3 §6.4 明确允许透明度(带 OutputIntent 必填 + 完整 PDF 1.4 命名 blend mode 等独立要求),'2*' / '3*' 让调用通过。
  • v2.104 FileAttachment gate 放宽:AddFileAttachmentAnnotation 现在仅在 PDF/A-1 strict 下抛。PDF/A-2 §6.3.1 仅禁 3D / Sound / Screen / Movie 注释类型;FileAttachment 被允许且是 PDF/A-3 Annex E Associated Files hybrid 工作流的基础。剩余 v2.104 gate(Sound、Movie 注释;Launch 操作)按共享 §6.3.1 + §6.5.1 禁用规则对所有 PDF/A part 仍然触发。
  • 新 THotPDF 方法 PDFAPart / PDFAConformanceLevel / IsPDFA1Strict / IsPDFA2OrLater 让分支判断 ergonomic。既有 v2.101-104 PDF/A gate 现在使用这些 helper,代码更清晰,错误信息建议合适时切换到 PDF/A-2/3。
  • Win32 + Win64 clean compile;v2.101-104 baseline(smoke_pdfa1_compliance / smoke_pdfa1_forbidden / smoke_pdfa1_annot_action)全部重 compile 通过。新 smoke smoke_pdfa2_compliance emit 四个 PDF(Levels 2B / 2U / 2A / 3U),行使 v2.103 透明度放宽、v2.104 FileAttachment 放宽,加两个验证路径(无效 '4Q' level 拒绝、PDF/A-1 透明度 gate 仍然触发)。Python verifier 断言 XMP pdfaid:part + conformance 匹配每个 level,Level 2A 额外有 Tagged-PDF 继承。
  • 剩余 PDF/A-2/3 producer slice:v2.106 把禁用 action 集从 6 个扩展到 13 个(新增 Hide / SetOCGState / Rendition / Trans / GoTo3DView + deprecated set-state / no-op),并调整 annot 类型禁用(加 3D / Screen,去 FileAttachment);v2.107 强制 ISO 19005-2 §6.1.13 implementation limits + XFA gate;v2.108 实现 PDF/A-3 Annex E Associated Files(hybrid container PDF)。

2026-05-19 Version 2.104.0

  • PDF/A-1 producer 系列收口(4/4 闭环):闭合 §6.5.2 禁用 annotation 类型(FileAttachment、Sound、Movie)与 §6.6.1 Launch action 子类型。v2.104 落地后,2026-05-19 PDF/A-1 审计报告中所有 17 个 producer-side 缺口全部闭合:四大重头(§5/§6.7.11 标识、§6.1.3 加密禁、§6.2.2 OutputIntent、§6.4 透明度)+ 其他 forbidden-feature gate。
  • §6.5.2 禁用 annotation 类型:AddFileAttachmentAnnotation、AddSoundAnnotation、AddMovieAnnotation 在 PDFACompliance 启用时抛带清晰 spec 节诊断。按 spec "FileAttachment、Sound、Movie 类型不被允许",避免归档文件依赖外部内容或包含多媒体。
  • §6.6.1 Launch action:AddLaunchLink 在 PDFACompliance 启用时抛 §6.6.1 诊断带 launch 目标。按 spec "Launch、Sound、Movie、ResetForm、ImportData、JavaScript actions 不被允许"。剩余五个禁用 action 类型(Sound/Movie/ResetForm/ImportData/JavaScript)HotPDF 无 emit 它们的公开入口,自动满足无需显式 gate。
  • PDFUACompliance + PDFACompliance 组合现在跨四个 PDF/A slice 完全正确工作。两个 opt-in 叠加:Level A 继承 PDFUACompliance 自动开启 Tagged PDF;两个 wrapper 各自应用 §-specific guard(PDF/UA 的 Suspects/Tabs/Lang;PDF/A 的 Encrypt/Transparency/Annot/Action)互不干扰。
  • 审计闭环总结:HotPDF 能在 producer 端结构性满足的所有 PDF/A-1 §6 文件格式要求全部闭合。剩余的调用方责任(§6.1.5 Info-XMP 等价、§6.2.3.3 颜色空间 ↔ OutputIntent 匹配、§6.3 字体细节其中 v2.74-v2.86 PDF/UA 工作已经处理大部分)与 PDF/UA-1 调用方责任对齐。
  • Win32 + Win64 clean compile;v2.103 smoke_pdfa1_forbidden 重 compile 通过。新 smoke smoke_pdfa1_annot_action 在 PDFACompliance 下行使四个 gated 路径(AddFileAttachmentAnnotation、AddSoundAnnotation、AddMovieAnnotation、AddLaunchLink)并断言每次调用都抛 §6.5.2 或 §6.6.1 诊断。
  • PDF/A-1 producer 面现已完全审计闭合。未来 PDF/A-2(ISO 19005-2:2011 基于 PDF 1.7,允许透明度 + layers)与 PDF/A-3(ISO 19005-3:2012,允许 hybrid 工作流嵌入文件)扩展是后续可能的候选,待调用方需求触发。

2026-05-19 Version 2.103.0

  • PDF/A-1 producer 系列第三刀 (3/4):闭合 §6.4 Transparency (CRITICAL 缺口 #15) + §6.2.8 ExtGState /TR transfer function (#14)。两个 §6 缺口对应的既有 HotPDF producer 路径自动满足无需代码改动:§6.1.10 LZW filter (HotPDF writer 路径只 emit FlateDecode) 和 §6.2.4 image /Interpolate (HotPDF 从不 emit /Interpolate)。审计报告里同步标记为 automatic。
  • §6.4 透明度禁非 1.0 alpha 和非 Normal/Compatible 混合模式。RegisterExtGState 在 PDFACompliance 启用时 gate:fill alpha < 1 抛 (PDF/A-1 要求 /ca = 1.0);stroke alpha < 1 抛 (/CA = 1.0);BM 非 `Normal` 或 `Compatible` 抛。三种诊断都指明 spec 节和具体违规值。默认 ExtGState(无 alpha keys、无 BM)和显式合法值(1.0 alphas、Normal / Compatible BM)一律放行。
  • §6.2.8 ExtGState 禁 /TR 传输函数键(已 deprecated;PDF/A-1 只允许 /TR2 值 = /Default,而且尽量避免使用)。RegisterTransferFunctionState 现在 PDFACompliance 启用时抛带清晰诊断。需要 PDF/X 工作流传输函数的调用方仍可在非 PDFACompliance 模式下用。
  • §6.1.10 LZW + §6.2.4 Interpolate:已在审计报告里记为 HotPDF 既有 producer 路径自动满足(FlateDecode-only writer、不 emit /Interpolate)。无代码改动;注释提示未来调用方不要重新引入。
  • Win32 + Win64 clean compile;v2.102 smoke_pdfa1_compliance 重 compile 通过。新 smoke smoke_pdfa1_forbidden 行使所有三个 gated 路径(ca<1、CA<1、BM=Multiply、/TR 注册)+ 验证允许的路径(ca=1.0、CA=1.0、BM=Normal/Compatible、无 alpha)仍正常通过。无 verifier — smoke 本身用 try/except 断言每个 gated 调用都抛。
  • 剩余 PDF/A-1 producer slice:v2.104 闭合 §6.5.2 禁用注释类型(FileAttachment、Sound、Movie)+ §6.5.3 annot /F /AP 规则 + §6.6.1 禁用 action 类型(Launch、Sound、Movie、ResetForm、ImportData、JavaScript)+ §6.9 Form 字段 /A /AA + /NeedAppearances。v2.104 落地后 PDF/A-1 producer 面完全审计闭合。

2026-05-19 Version 2.102.0

  • PDF/A-1 (ISO 19005-1:2005) producer 系列第二刀 (2/4):闭合 §6.2.2 OutputIntent + ICC profile 缺口。新增高层 helper `AddPDFAOutputIntent(OutputConditionIdentifier, Info, ICCProfileStream, NumComponents, AlternateCS)` 把 v2.51 RegisterICCProfile + AddOutputIntent('GTS_PDFA1', ...) 两步合并为一次调用。PDF/A-1 严格要求 `/Type /OutputIntent /S /GTS_PDFA1` 条目带有效 `/DestOutputProfile` ICC 流——没它符合标准的文件无法跨平台准确再现颜色。
  • EndDoc PDFACompliance gate 新增 `EnsurePDFAOutputIntent` 检查:遍历已注册 OutputIntent 列表,找到至少一个 `/S /GTS_PDFA1` 条目带 `/DestOutputProfile`。缺失则抛清晰诊断指明 spec 节并指向 AddPDFAOutputIntent。按 §6.2.2 + Annex A。
  • 典型归档工作流:传 sRGB IEC61966-2.1 ICC profile 用于屏幕 PDF/A、FOGRA39 / GRACoL 用于印刷、或对应已注册的 CMYK profile 用于印前。NumComponents 必须 1 (Gray)、3 (RGB / Lab) 或 4 (CMYK) 按 PDF 1.7 8.6.5.5 Table 66;helper 转发校验给 RegisterICCProfile。
  • Helper 校验:空 OutputConditionIdentifier 抛 (§6.2.2 要求 identifier);nil ICCProfileStream 抛 (DestOutputProfile 强制)。无返回值——OutputIntent 自动注册到 FOutputIntents 并通过既有 Catalog /OutputIntents 序列化路径发出。
  • Win32 + Win64 clean compile;v2.101 smoke_pdfa1_compliance 更新调用 AddPDFAOutputIntent(带 192 字节 fake ICC 占位;真实调用方应传完整 ICC binary)。第 4 个验证案例断言 PDFACompliance + 缺 OutputIntent 抛 §6.2.2 诊断。Python verifier 扩展检查 Catalog /OutputIntents 数组,遍历条目找 `/S /GTS_PDFA1` + `/DestOutputProfile` indirect ref。
  • 剩余 PDF/A-1 producer slice:v2.103 闭合 #15 透明度 (CRITICAL) + #5 LZW filter + #10 image /Interpolate + #14 ExtGState /TR;v2.104 闭合 #16 FileAttachment/Sound/Movie annot + #17 annot /F /AP 规则 + #18 Launch/Sound/Movie/ResetForm/ImportData/JavaScript actions + #20 Form 字段 /A /AA + /NeedAppearances。

2026-05-19 Version 2.101.0

  • 启动四版本 PDF/A-1 (ISO 19005-1:2005) producer-side 合规系列,首版落地 wrapper 层 opt-in。完整 17 缺口审计报告归档在 `.superpowers/specs/2026-05-19-pdfa1-compliance-audit.md`;v2.101 落基础(PDFACompliance 属性、XMP pdfaid 标识、Title/Lang 严格化、加密冲突 guard)。v2.102 - v2.104 闭合剩余颜色管理 / 透明度 / 字体 / 注释 + 操作缺口。
  • 新 `PDFACompliance: AnsiString` 属性接受 '' (禁用,默认)、'A' (Level A: 视觉 + Tagged PDF + 无障碍) 或 'B' (Level B: 仅视觉保真)。EndDoc 时无效值抛异常。按 ISO 19005-1:2005 §5 conformance levels。
  • 设 PDFACompliance:='A' 自动启用 PDFUACompliance(Level A 继承 §6.8 Tagged PDF 要求,与 PDF/UA-1 §7 logical structure 要求一致)。两个 wrapper 可同时启用无冲突;v2.94 PDF/UA-1 Lang/Title/Suspects gate 叠在 v2.101 PDF/A guard 之上。
  • 设任一级别自动启用 FEnableXMPMetadata 让文档携带必需的 /Metadata XMP 流。按 §6.7.2 Catalog Metadata 强制。
  • BuildXMPPacket 扩展:PDFACompliance 非空时 XMP packet 声明 `xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/"` 加 `1` 和 `A` (或 `B`)。veraPDF 等验证器靠这组三元组分类文档为 PDF/A-1A 或 1B 候选。按 §6.7.11 Table 6 PDF/A 标识 schema。
  • Title / Lang 严格化:PDFACompliance 启用 + Doc.Title='' 抛 (PDF/A-1 §6.7.3 要求 dc:title in XMP 与 Info /Title 等价)。Lang 严格化镜像 v2.94 PDFUACompliance 行为(§6.8.4 for Level A),调用方必须显式选有效 BCP-47 标签而非接受静默 'en' 回退。
  • 加密冲突 guard:按 §6.1.3 文件 trailer 不能含 /Encrypt。EnableEncrypt(OwnerPassword/UserPassword/Protection 设置触发的内部入口)现在 PDFACompliance 非空时抛清晰诊断。EndDoc 经同路径暴露冲突,让调用方在序列化之前看到失败。
  • 调用方仍需自负责:emit 有效 PDF/A-1 OutputIntent + ICC profile(v2.102 跟进)、避免透明度(v2.103)、避免禁用的 annotation/action 类型(v2.104)、Level A 真实语义结构树(由 v2.96-v2.99 Tagged PDF helper 覆盖)。
  • Win32 + Win64 clean compile;v2.100 baseline smoke_pdfua_link_contents 重 compile 通过。新 smoke smoke_pdfa1_compliance emit Level A 和 Level B PDF + 行使三个验证路径(无效 level Z、空 Title、加密 + PDFACompliance 冲突)。Python verifier 断言 xmlns:pdfaid + pdfaid:part=1 + 匹配 pdfaid:conformance、dc:title、Level A 另外含 /MarkInfo /Marked true + Catalog /Lang + /StructTreeRoot + xmlns:pdfuaid(自动继承)。

2026-05-19 Version 2.100.0

  • 闭合 2026-05-19 PDF/UA-1 审计中剩余的 §7.18.5 link-annotation /Contents 覆盖缺口。v2.95 给 AddURILink 加 Description;v2.100 把同样的替代描述支持带到剩余三个 link 入口:AddGoToLink(文档内跳转)、AddGoToRLink(跨文件跳转)、AddLaunchLink(外部文件启动)。
  • 每个 helper 增加可选末参 `const Description: AnsiString = ''`。非空时 annotation dict 携带 `/Contents (Description)` 条目;`PDFUACompliance` 模式下空 Description 抛异常并给出明确诊断说明出问题的 link 目标,缺失替代描述无法静默通过。默认空保 PDFUACompliance 模式之外的向后兼容。
  • 按 PDF/UA-1 §7.18.5 严格:"链接必须通过 Contents 键提供替代描述(按 ISO 32000-1:2008, 14.9.3)"。spec 适用于所有 link annotation 类型不只 URI link — helper 签名现在统一覆盖。
  • 典型用法:`Page.AddGoToLink(R, 1, 700, '跳到第 2 页:方法论'); Page.AddGoToRLink(R, 'companion.pdf', 0, -1, false, '打开伴随文档查看完整数据表'); Page.AddLaunchLink(R, 'readme.txt', false, '在默认文本编辑器打开 README');`
  • Win32 + Win64 clean compile;v2.99 baseline smoke_pdfua_figure 重 compile 通过。新 smoke smoke_pdfua_link_contents 构造 2 页文档在 page 0 加三个 link annotation(GoTo 跳 page 1、GoToR 跳 'companion.pdf'、Launch 启 'readme.txt')+ 三个 PDFUACompliance 空 Description 异常路径。Python verifier 断言每个 link annotation 带 /Subtype /Link、匹配的 /S /GoTo(或 /GoToR / /Launch)action 类型、与传入 Description 匹配的 /Contents 值。
  • PDF/UA-1 producer 面覆盖总结:v2.100 之后,四个 link annotation 入口全部满足 §7.18.5。配合 v2.94 审计闭环(Suspects、Tabs、Lang)、v2.95(URI Contents、Bit 10、ID)、v2.96-v2.99 Tagged PDF semantic helper 系列(Heading、List、Table、Figure),HotPDF 的 PDF/UA-1 producer-side 面在 spec-strict wrapper 层与 ergonomic API 层双闭环。

2026-05-19 Version 2.99.0

  • 新增 `BeginTaggedFigure(Parent, AltText, BBox): FigureElem` + `EndTaggedFigure` 作为 Tagged PDF semantic helper 系列第四刀也是最后一刀,对应 PDF/UA-1 §7.3(Graphics)。把调用方提供的绘图操作(图像 XObject、矢量路径、描边矩形)包在 Figure 结构元素里,带强制 /Alt 和可选 /BBox 布局属性。
  • /Alt 强制(无空字符串默认 fallback)。按 PDF/UA-1 §7.3 严格:"Figure 标签必须包含代表 Figure 内容的替代表示或替代文本"。空 AltText 抛异常并给出明确诊断指向 v2.88 6 参 AddStructureElement 重载(如果 /ActualText 而非 /Alt 才是适合的替代文本机制)。
  • 可选 BBox 是 4 元素 [llx, lly, urx, ury] 数组描述 Figure 在默认用户空间的边界框,按 ISO 32000-1 14.8.5.4.5 Layout 属性 owner 附为 /A << /O /Layout /BBox [...] >>。传空数组完全省略 BBox 属性。长度错(1/2/3/5+ 元素)抛异常并给出 spec 矩形布局指引。
  • 典型用法:`Fig := Doc.BeginTaggedFigure(Root, '公司 logo:指向右侧的箭头', [72, 600, 200, 720]); ... 绘 Figure 内容 ... Doc.EndTaggedFigure;` Figure 参与与其他 v2.90 BeginTaggedContent 路径相同的 MCID / ParentTree 接线,但 /Alt + /BBox 属性自动挂上。
  • 这闭合审计报告中 PDF/UA-1 §7.3 调用方责任面。Tagged PDF semantic helper 系列现在覆盖所有四种重型结构类型:§7.4(Heading,v2.96)、§7.6(List,v2.97)、§7.5(Table,v2.98)、§7.3(Figure,v2.99)。配合 v2.94 + v2.95 wrapper 层修复,HotPDF 的 PDF/UA-1 producer 面在 spec 级别既 strict-conformant 又在 API 级别 ergonomic。
  • Win32 + Win64 clean compile;v2.98 baseline smoke_pdfua_table 重 compile 通过。新 smoke smoke_pdfua_figure emit 两个 Figure 结构元素(一个带 /Alt + /BBox、一个仅 /Alt)+ 行使空 AltText 与畸形 BBox 异常路径。Python verifier 断言两个 Figure /S 类型、/Alt 文本匹配、BBox 数组值为 [72, 600, 200, 720]。

2026-05-19 Version 2.98.0

  • 新增 4 个 Tagged Table helper 对应 PDF/UA-1 §7.5(Tables)作为 Tagged PDF semantic helper 系列第三刀。`BeginTaggedTable(Parent): TableElem` 构造 Table 结构容器;`AddTaggedTableRow(Table): TRElem` 添加 TR 行;`EmitTaggedTableHeader(TR, X, Y, Text, Scope): THElem` emit 带强制 /Scope 属性的 TH 表头单元格;`EmitTaggedTableCell(TR, X, Y, Text): TDElem` emit TD 数据单元格。5 个标签类型(Table、TR、TH、TD 加属性对象)全部 spec 合规。
  • /Scope 属性(ISO 32000-1 14.8.5.7 Table 350)helper 强制(无默认),合法值 `Row` / `Col` / `Both`。无效 Scope 字符串抛异常并给出 spec 集合。PDF/UA-1 §7.5 严格:"TH 类型结构元素应有 Scope 属性。如果表格结构无法通过 Headers + IDs 判定,TH 结构元素必须有 Scope 属性"。helper 签名强制 Scope 阻止 missing-Scope 合规失败模式。
  • Scope 序列化按标准属性对象形式:`/A << /O /Table /Scope /<Row|Col|Both> >>`(按 ISO 32000-1 14.7.5.3 Class Map)让消费阅读器视其为 Table 命名空间属性。与 v2.97 List 的 `/A << /O /List /ListNumbering ... >>` 属性同模式。
  • Table 与 TR 是纯结构容器(不带页内容 marked-content),但 TH 和 TD 携带可见文本所以都走 BeginTaggedContent + TextOut + EndTaggedContent。每个 TH/TD 单元格分配自己的 MCID、页内容流 BDC 算子、ParentTree 入口。helper 返回 cell StructElem 让调用方挂更多属性(如复杂表的 /Headers list、/RowSpan、/ColSpan)。
  • 典型用法:`T := Doc.BeginTaggedTable(Root); R := Doc.AddTaggedTableRow(T); Doc.EmitTaggedTableHeader(R, 72, 750, WideString('Name'), 'Col'); Doc.EmitTaggedTableHeader(R, 200, 750, WideString('Age'), 'Col'); R2 := Doc.AddTaggedTableRow(T); Doc.EmitTaggedTableCell(R2, 72, 730, WideString('Alice')); ...` 替代 v2.90 每个单元格多步结构 + BDC/EMC 序列。
  • 这闭合审计报告中 PDF/UA-1 §7.5 调用方责任面。Tagged PDF semantic helper 系列现在覆盖 §7.4(Heading,v2.96)、§7.6(List,v2.97)、§7.5(Table,v2.98)。剩余 slice(v2.99+):Figure helper for §7.3 含 /BBox + /Alt + 调用方提供绘图块。
  • Win32 + Win64 clean compile;v2.97 baseline smoke_pdfua_list 重 compile 通过。新 smoke smoke_pdfua_table 构造 3 行表(1 表头 + 2 数据),3 个 TH 单元格(Scope=Col)+ 6 个 TD 单元格 + 行使无效 Scope 异常路径。Python verifier 断言完整 Table > TR > TH/TD 结构每个 TH 带 /Scope /Col。

2026-05-19 Version 2.97.0

  • 新增两个 PDF/UA-1 §7.6 List helper 作为 Tagged PDF semantic helper 系列第二刀:`BeginTaggedList(Parent, NumberingStyle): ListElem` 构造 L 结构元素并附 /A << /O /List /ListNumbering /<Style> >> 属性对象(按 ISO 32000-1 14.8.5.5);`EmitTaggedListItem(ListElem, LblX, LblY, LabelText, BodyX, BodyY, BodyText): LIElem` emit 完整 LI > {Lbl, LBody} 子结构,Lbl 与 LBody 都带 marked-content 包裹可见文本。
  • NumberingStyle 参数接受 8 个 PDF/UA-1 / ISO 32000-1 spec 名:`None`、`Decimal`、`UpperRoman`、`LowerRoman`、`UpperAlpha`、`LowerAlpha`、`Circle`、`Disc`。无效 style 名抛异常并给出完整 spec 集合。空字符串完全省略 /ListNumbering 让调用方自行附加自定义属性对象。
  • 按 PDF/UA-1 §7.6:有序列表必须显式 /ListNumbering 属性;无序列表可用 `None` 或 bullet glyph 之一。helper 不强制——调用方负责选与可见内容匹配的 style。
  • 典型用法:`Lst := Doc.BeginTaggedList(Root, 'Decimal'); Doc.EmitTaggedListItem(Lst, 72, 750, WideString('1.'), 96, 750, WideString('First item')); ...` 每个列表项一行替代 v2.90 六行 LI + Lbl-BDC-TextOut-EMC + LBody-BDC-TextOut-EMC 链。
  • Lbl 与 LBody 子结构都经过 BeginTaggedContent(各自分配 MCID 和 ParentTree 入口),EmitTaggedListItem 返回 LI 结构元素让调用方可附加嵌套 LI 子项、/Alt 或 /Lang。
  • 这闭合审计报告中 PDF/UA-1 §7.6 调用方责任面。剩余 Tagged PDF semantic helper slice(v2.98+):Table helper 覆盖 §7.5(Table / TR / TH / TD with /Scope)、Figure helper 覆盖 §7.3(Figure + /Alt + /BBox 属性)。
  • Win32 + Win64 clean compile;v2.96 baseline smoke_pdfua_heading 重 compile 通过。新 smoke smoke_pdfua_list 构造 3 项 Decimal 有序列表 + 2 项 None 无序列表 + 行使无效 style 异常路径。Python verifier 断言两个 /S /L StructElem 带不同 /ListNumbering 属性值、3 vs 2 LI 子项、有序列表第一个 LI 含 Lbl/LBody 子项。

2026-05-19 Version 2.96.0

  • 新增 `EmitTaggedHeading(Level, Parent, X, Y, Text)` 高层 helper 对应 PDF/UA-1 §7.4。一次调用 emit 完整 `/H1`..`/H6` 结构元素 + 当前页可见文本:helper 分配 per-page MCID、发 BDC 算子、跑 TextOut、再 EMC,构造对应 /H<Level> 角色的 StructElem 接进 /ParentTree。替代 v2.90 三行 BeginTaggedContent + TextOut + EndTaggedContent 模板的常见 heading 场景。
  • Level 参数接受 1..6(标准 heading 范围)。超范围 Level 抛 Exception 并给出明确诊断。PDF/UA-1 §7.4.3 允许用户定义 H7+ 标签但少见;需要的调用方仍可手工调 BeginTaggedContent('H7', ...)。
  • 返回 heading StructElem 让调用方能在 helper 返回后链式添加属性(/Lang、装饰性 heading 的 /Alt 等)。
  • 典型用法:`Doc.EmitTaggedHeading(1, RootElem, 72, 750, WideString('第一章'));` 现在完全代替之前 BDC + TextOut + EMC + AddStructureElement 多行 plumbing。
  • 这是 v2.95 PDF/UA-1 wrapper 闭环后规划的"Tagged PDF semantic helpers"系列第一刀。后续 slice(v2.97+)覆盖 List(L / LI / Lbl / LBody)和 Table(Table / TR / TH / TD with /Scope)helper,闭合审计报告中 §7.5 / §7.6 的调用方责任面。
  • Win32 + Win64 clean compile;v2.95 baseline 重 compile 通过。新 smoke smoke_pdfua_heading emit 三个 heading (H1/H2/H3) 加 Level=7 范围检查异常路径;Python verifier 断言三个 /S /H<n> StructElem 与解压后页内容流中的匹配 /H<n> BDC 算子。

2026-05-19 Version 2.95.0

  • PDF/UA-1 审计收口:处理 v2.94 审计中标记的剩余三个 producer-side 缺口(§7.18.5 Link Contents、§7.16 加密 Bit 10、§7.9 StructElem ID)。PDF/UA-1 producer 面现已 spec-complete;审计原始六个缺口在 v2.94 + v2.95 横跨闭合。
  • §7.18.5 Link Contents(HIGH):`AddURILink(Rect, URL, Description='')` 增加第三个 `Description` 参数,非空时填到 annotation 的 `/Contents` 键作为替代描述。PDF/UA-1 §7.18.5 强制要求 "链接必须通过 Contents 键提供替代描述"。PDFUACompliance 模式下空 Description 现在抛异常并给出清晰诊断。默认 Description='' 保 PDFUACompliance 之外的向后兼容。
  • §7.16 加密 Bit 10(MED):PDFUACompliance=True 且文档加密(通过 EnableEncrypt)时,HotPDF 自动 `ProtectFlags := ProtectFlags or $200`(Bit 10 = "提取文本和图形供辅助技术使用")。按 PDF/UA-1 §7.16:"加密的合规文件其加密字典必须含 P 键。P 键的第 10 位必须为 true"。没这个 bit,屏幕阅读器无法从加密 PDF 提取内容,无障碍性破坏。修复是条件性的——非 PDFUA 加密 caller-supplied flag 不变。
  • §7.9 StructElem ID(LOW):新 7 参 `AddStructureElement` 重载接受非空 `IDStr` 参数,写入结构元素的 `/ID` 键。helper 维护 issued ID 内部集合并在碰撞时抛异常,防止重复 Note ID 滑过去(这会混乱交叉引用 Link → Note 跳转和辅助技术)。按 spec:"每个 Note 标签必须在 ID 键有一个唯一条目"。
  • EnableEncrypt 重构:原 body 现归 `EnableEncryptInternal`,公开 `EnableEncrypt` 是 6 行包装:先做 PDF/UA-1 Bit 10 stamp 再转发。v2 / v4 / v5 加密分支字节级不变;既有加密 PDF 调用方除非显式启用 PDFUACompliance 保字节级一致。
  • 验证器覆盖:新 smoke_pdfua_gap_closure 行使 §7.18.5(带 Description 的 Link)+ §7.9(两个不同 ID 的 Note 结构元素 + 一次碰撞应抛)。Verifier 断言 `/Contents` 存在并匹配 Description、两个唯一 Note `/ID` 值存在。加密 Bit 10 修复在审计报告中记录;加密 PDF 的 smoke 覆盖推到更大型加密测试重写。
  • spec 覆盖闭环:v2.95 落地后,2026-05-19 PDF/UA-1 审计(`.superpowers/specs/2026-05-19-pdfua-compliance-audit.md`)的所有 6 个缺口全部闭合。HotPDF 的 PDF/UA-1 producer-side 面对 wrapper 层(§5 metadata、§6 conformance、§7.1 Catalog 要求、§7.2 Lang、§7.16 encryption、§7.18.3 Tabs、§7.18.5 Link Contents、§7.9 Note IDs)已 strict-conformant。语义结构树决策(标题层级、列表嵌套、表头 TH/Scope、Figure /Alt 质量)一如既往是调用方责任。

2026-05-19 Version 2.94.0

  • PDF/UA-1 (ISO 14289-1:2014) producer-side 合规审计后续:闭合 v2.87 高层 opt-in 没覆盖的三个真实合规缺口。审计逐项对照 HotPDF v2.93 输出与 §7 文件格式要求每个子节,识别出六个 producer 缺口;本版处理三个 EndDoc 阶段的修复(Suspects 键、annotated-page Tabs、Lang 严格化)。剩余三个缺口(Link Contents、加密 Bit 10、StructElem ID)落在 v2.95-v2.97。
  • §7.1 Suspects(HIGH):`PDFUACompliance` 启用时 Catalog → MarkInfo 字典现在发 `/Suspects false`。按 spec "声明合规的文件 Suspects 值必须为 false" — veraPDF 等验证器检查键的**显式存在**,不是缺席。v2.93 完全不发该键,spec-strict 验证会报 "Catalog MarkInfo does not contain a Suspects entry"。
  • §7.18.3 Tabs(MED):EndDoc 现在遍历每页,对所有含非空 `/Annots` 数组且未声明 `/Tabs` 的页自动盖 `/Tabs /S`。按 spec "任何带注释的页面其页字典必须含 Tabs 键 ... 值为 S"。仅在 `PDFUACompliance` 启用时触发;调用方手工设过 `/Tabs`(通过 `THPDFPage.SetTabsOrder`)的选择被尊重。
  • §7.2 Lang(MED):`PDFUACompliance=True` 时若 `Doc.Lang` 为空现在抛 Exception 给出明确诊断,不再静默填 `'en'`。v2.94 之前的 fallback 给非英文文档贴错标签(任何中文/阿拉伯/日韩等内容会静默 emit 错的 BCP-47 标签,屏幕阅读器据此切换发音)。调用方现在必须显式声明文档语言——典型用法:`Doc.Lang := 'en-US'`、`'zh-CN'`、`'ar-SA'` 等。
  • 向后兼容说明:既有 PDFUACompliance 调用方必须在 BeginDoc / EndDoc 之前加 `Doc.Lang := '...'`。四个示例 smoke(smoke_pdfua_compliance、smoke_pdfua_alt_actualtext、smoke_pdfua_annot_structparents、smoke_pdfua_tagged_content)已统一设 `Doc.Lang := 'en-US'`。不用 PDFUACompliance 的调用方不受影响。
  • 验证器覆盖:smoke_pdfua_compliance_verify.py 现在断言 Catalog MarkInfo 字典含 `/Suspects false`;smoke_pdfua_annot_structparents_verify.py 断言带注释的页面含 `/Tabs /S`。两者都在生成的 PDF 上端到端跑。
  • 审计报告:完整 PDF/UA-1 合规审计报告(覆盖 §7 全部 21 个文件格式子节)归档在 `.superpowers/specs/2026-05-19-pdfua-compliance-audit.md`(项目本地,不入仓)。报告把每个子节分类为"HotPDF 自动满足"、"调用方责任带 helper API"或"真实 producer 缺口",并按严重度给剩余缺口排好 v2.95+ 路线图。

2026-05-19 Version 2.93.0

  • 新增 `RegisterColorConversionLUT3D(GridR, GridG, GridB, OutputComponents, Samples, BitsPerSample=8, Order=1): THPDFStreamObject` 高层 wrapper,针对常见的 3 输入色彩管理 LUT 用例封装 v2.52 Function Type 0 原语。内部自动构建标准 /Domain=[0,1]*3 + /Range=[0,1]*OutputComponents + /Size=[GridR, GridG, GridB] 数组并转发给 RegisterSampledFunction。
  • 典型工作流:ICC profile 驱动的 sRGB → Lab、sRGB → CMYK 或 RGB → RGB device-link 转换表达为 3D LUT。wrapper 校验每轴 grid size 和 OutputComponents 范围;底层 RegisterSampledFunction 校验 BitsPerSample / Order / 总字节数与 Sample payload size 一致。长度对不上时从原语抛出明确诊断。
  • Sample 字节布局:3D 网格行优先(R 最快,G 次之,B 最外层);每个 cell BitsPerSample=8 时占 OutputComponents 字节。总数 = GridR * GridG * GridB * OutputComponents。更高比特深度(12、16)需要调用方按 spec MSB-first 字节打包规则手工封装好。
  • 用户可见影响:色彩管理工作流现在可以用一行 17x17x17 RGB → CMYK ICC 模拟 LUT,参数是预计算好的样本数组,不再需要手工建 /Domain / /Range / /Size。返回的 indirect THPDFStreamObject 插入任何接受 Function 的 API:Pattern Type 2 axial/radial shading 的 /Func 项、Separation tint transform(通过 RegisterSeparationLUT)、/TR 传输函数 ExtGState、/BG 黑色生成 ExtGState。
  • 向后兼容:既有 RegisterSampledFunction 原语不变;新 wrapper 只是薄薄一层。需要非默认 /Domain(如 Lab L* 在 [0..100]、a*/b* 在 [-128..127])或非矩形 Range 的调用方继续直接用 RegisterSampledFunction。
  • 候选 #4 状态:这是矩阵候选 "3D Function Type 0" 唯一的一刀。高层 wrapper 让既有的 N=3 原语在典型色彩管理用例中变得 ergonomic;候选 #4 完成。

2026-05-19 Version 2.92.0

  • 新增 `RegisterHalftoneType5(ColorantNames, Halftones, DefaultHalftone, HalftoneName)` 收口 ExtGState `/HT` halftone 家族。Type 5 是多 component 字典,按颜色名 key(如 /Cyan /Magenta /Yellow /Black /Red /Green /Blue /Gray 加任何已注册的 Separation/DeviceN spot color),值是指向 Type 1/6/10/16 sub-halftone 的 indirect ref。CMYK prepress 工作流现在可以在一个 ExtGState 里给每个 ink 单独设置屏蔽频率/角度/网点形状。
  • 并行数组签名:`ColorantNames` 和 `Halftones` 按下标配对,长度必须相同。空 colorant 名或 nil halftone 抛异常并指出问题下标。可选 `DefaultHalftone` 进 dict 的 /Default fallback,消费阅读器对不在显式列表里的 colorant 用它(文档含意外 spot color 时常用)。
  • 输出字典结构(PDF 1.7 ISO 32000-1 10.5.5.2):/Type /Halftone + /HalftoneType 5 + 可选 /HalftoneName + 每对一个 /ColorantName 条目 + 可选 /Default。返回 indirect THPDFDictionaryObject,调用方可以像其他 halftone 对象一样传给 v2.64 `RegisterHalftoneState`。
  • 校验:至少要有一个 component 或 DefaultHalftone(空 Type 5 没语义)。helper 不验证 sub-halftones 不是 Type 5(spec 禁止 Type 5 嵌套),因为调用方要主动构造自引用图才会触发,实际 prepress 工作流不会。
  • 用户可见影响:制作四色册子的 prepress 工程师现在可以一次设四个独立屏蔽(Cyan 105°/75 lpi、Magenta 75°/75 lpi、Yellow 90°/75 lpi、Black 45°/75 lpi 用 Type 1 spot 函数,或换 Type 6 threshold raster 做 FM 屏蔽)包进一个 ExtGState。同一个 ExtGState 由后续任何需要 per-channel 屏蔽的绘图调用激活。
  • 候选 #3 状态:这是 ExtGState `/HT` halftone 家族的第二刀,也是最后一刀。完整功能集(v2.64 Type 1 spot 函数 + v2.92 Type 5 多 component + v2.91 Type 6/10/16 threshold-stream)现在 PDF 1.7 spec 完整。候选 #3 完成。

2026-05-19 Version 2.91.0

  • 新增三个 threshold-stream halftone builder 收口 v2.64 起步的 ExtGState `/HT` halftone 家族:`RegisterHalftoneType6`(8-bit threshold raster,Width/Height 键)、`RegisterHalftoneType10`(8-bit threshold raster,平行四边形 cell 通过 Xsquare/Ysquare 表示带角度屏蔽)、`RegisterHalftoneType16`(16-bit threshold raster,65536 级精度用于高端 prepress)。
  • 每个 builder 返回 indirect /Type /Halftone 流对象,调用方可传给 v2.64 `RegisterHalftoneState` 包成 ExtGState /HT 项,与 Type 1 spot 函数 halftone 用法一致。可选 /HalftoneName、/TransferFunction 或 /TransferFunction /Identity 条目与 Type 1 模式相同。
  • 体积校验:Type 6 要求 ThresholdBytes 长度 = Width*Height;Type 10 要求 Xsquare^2 + Ysquare^2(PDF 1.7 10.5.5.4 stacked-squares 布局);Type 16 要求 Width*Height*2(big-endian uint16 对,调用方自己摆好)。长度对不上抛 Exception 同时列出实际和期望字节数。
  • 共享 `_HotPDFCreateHalftoneStream` 内部 helper 去重每个 Type 的构造逻辑:indirect 流分配、/Type /Halftone + /HalftoneType N 头部、可选 name + transfer function 条目、payload 写入 + /Length 标。每个对外函数只校验自己 type 特定的 size 键再转发。
  • 用户可见影响:需要用户自定义屏蔽 pattern 的 prepress 工作流(自定义网点形状、频调网点、角度网点)现在在 HotPDF 中有了入口。调用方提供 threshold raster(通常用屏蔽生成器离线生成),HotPDF 发 spec 合规的 /Type /Halftone 流对象并接进 ExtGState 资源链。
  • Spec 覆盖:PDF 1.7 ISO 32000-1 10.5.5.3 (Type 6) + 10.5.5.4 (Type 10) + 10.5.5.5 (Type 16)。自动 bump 到 PDF 1.2(与 Type 1 同 floor,halftone 在 1.2 一起进入)。
  • 候选 #3 状态:这是矩阵候选 "ExtGState /HT halftone 剩余类型" 的第一刀。Type 5 多 component halftone(按颜色组件名 key 的字典,每通道委托给 Type 1/6/10/16 sub-dict,CMYK prepress 用)是第二刀也是最后一刀,落在 v2.92。

2026-05-19 Version 2.90.0

  • 新增高层 marked-content 序列 helper:`THotPDF.BeginTaggedContent(Role, Parent): THPDFDictionaryObject` + `THotPDF.EndTaggedContent`。Helper 自动分配 per-page MCID、在当前页内容流发 BDC 算子、在 Parent 下构造 StructElem、把它接进 /ParentTree——一次调用完成。结果:PDF/UA-1 调用方可以标记可见内容段落而无需手工管理 MCID 计数器。
  • 典型用法:`Para := Doc.BeginTaggedContent('P', Root); Doc.CurrentPage.TextOut(...); Doc.EndTaggedContent;` 返回的 StructElem 可以链式传给 AddStructureElement('Span', Para, ...) 构造子结构,或通过 v2.88 属性路径携带 /Alt / /ActualText。
  • 新 THPDFPage.FNextMCID 字段跟踪每页 MCID 分配;每页重置(在 BeginDoc init 中 lazy 初始化为 0)。低层 BeginMarkedContentMCID / EndMarkedContent 算子(HotPDF 自 v2.11 起就有)仍可供需要显式 MCID 控制的调用方使用。
  • Pipeline 集成:BeginTaggedContent 在当前页内容流发 "/Role <> BDC",调 AddStructureElement(Role, Parent, PageIdx, MCID) 构造 StructElem 并跑 RegisterMCIDForPage(v2.11 路径,lazy 给页字典标 /StructParents 并扩 /ParentTree 的 per-page array)。EndTaggedContent 在当前页发 "EMC"。嵌套通过页内容流线性顺序隐式工作。
  • 用户可见影响:PDF/UA-1 候选文档现在可以在每个可见段落、标题、span 或图说前后两行就给它标语义结构。veraPDF 接受产生的页内容 marked-content 序列 + StructTree 联动;辅助技术沿文档大纲走进 marked content,朗读其角色 + 文本。
  • 候选 #1 状态:第四刀也是最后一刀。PDF/UA-1 producer-side 面现在覆盖 wrapper opt-in (v2.87) + /Alt / /ActualText 属性 (v2.88) + 注释 /StructParent + OBJR 反向接入 (v2.89) + 页内容 BDC/EMC 自动包裹 (v2.90)。调用方仍需自负责结构树拓扑决策(标题层级、列表嵌套、表头),但机械接线全部自动化。候选 #1 完成。

2026-05-19 Version 2.89.0

  • 新增注释到结构树接入,按 PDF 1.7 14.7.4.4。新 `THotPDF.RegisterAnnotForStructure(AnnotDict, StructElem)` helper 分配新的 /ParentTree key、给 annotation 标 /StructParent、把 StructElem 直接存到 /ParentTree[Key](注释用单 ref 形式,区别于页面 MCID 用的数组形式)、并把 <</Type /OBJR /Obj <annotRef>>> 追加到 StructElem 的 /K 数组。屏幕阅读器现在可以从任何 tagged 注释向上导航到父结构元素,反向也通。
  • 新增 `THPDFPage.AddURILink(Rect, URL): THPDFDictionaryObject` 让 PDF/UA-1 调用方有个返回值的 URI 注释入口。既有 AddGoToLink / AddGoToRLink / AddLaunchLink 方法不返回 annot 字典(早期 API 局限);新 AddURILink 返回 indirect 注释字典,可直接传给 RegisterAnnotForStructure。
  • 用户可见影响:PDF/UA-1 候选文档现在用三行代码就能给超链接打语义结构标签:`LinkAnnot := Doc.CurrentPage.AddURILink(R, 'https://example.org/'); LinkElem := Doc.AddStructureElement('Link', RootElem, -1, -1); Doc.RegisterAnnotForStructure(LinkAnnot, LinkElem);` 跟随文档大纲的辅助技术现在可以达到链接、朗读其用途、并以可激活元素方式呈现。
  • 实现说明:v2.11 EmitTaggedPDFRoot 发 /ParentTree /Nums 条目的循环原本用硬强转 `THPDFArrayObject(FParentTree.Items[I])`。v2.89 把强转放宽到 THPDFObject 因为注释条目存的是单个字典而非数组;THPDFArrayObject.AddObject 本来就靠运行时 IsIndirect 探测分派,所以强转其实一直是名义上的。v2.11+ 在 FParentTree 里存数组的 smoke 字节级稳定。
  • Spec 说明:PDF 1.7 14.7.4 允许 /ParentTree 值是数组(marked-content 序列按 MCID 索引)或直接 StructElem ref(注释、beads 等非 MCID 结构元素)。v2.89 是第一刀用单 ref 形式;数组形式继续通过 v2.11 AddStructureElement-带-MCID 路径工作。
  • 候选 #1 状态:PDF/UA producer-side 充实的第三刀。还剩一刀:页面内容 BDC/EMC 自动包裹,让 AddTextField + CurrentPage.TextOut 路径自动发 marked-content 序列而不需要手工算子调用。之后 PDF/UA producer 面落到满意的基线。

2026-05-19 Version 2.88.0

  • 给 `AddStructureElement` 增加重载签名,接受可选的 `/Alt` 和 `/ActualText` 属性字符串。PDF/UA-1 (ISO 14289-1) 要求非文本内容(图形、公式、装饰字形)必须提供屏幕阅读器友好的替代文本;新重载让一行调用就把它们挂到 StructElem 上。
  • `/Alt`(替代描述)是辅助技术为非文本内容朗读的文本——典型用法:包裹图像或图表的 Figure 结构元素。`/ActualText`(实际文本)是复制粘贴时返回的内容(当可见字形与预期 Unicode 不同时使用)——典型用法:装饰连字、首字下沉、难以提取的书法字形。
  • 两个属性都作为 PDF 文本字符串通过标准 byte-safe SaveStringObject 转义路径发出;空字符串跳过对应条目,让既有四参数调用方保持字节级输出。原四参 `AddStructureElement(Role, Parent, PageIndex, MCID)` 签名通过 Pascal `overload` 指令保留。
  • 用户可见影响:PDF/UA-1 候选文档现在可以用 `AddStructureElement('Figure', Root, 0, 0, '公司 logo', '')` 标记 logo 图形,用 `AddStructureElement('Span', Root, 0, 1, '', '公交站')` 标记装饰字形 span;veraPDF 和辅助技术直接消费产生的 /Alt 和 /ActualText 条目,无需更多设置。
  • Pipeline 位置:v2.88 重载转发到 v2.11 base 实现(StructTreeRoot 接线 + /K 数组追加 + /Pg 反向链接 + MCID/ParentTree 注册都按原路跑),然后只追加两个属性条目。不动既有调用点;v2.74-v2.87 字节稳定性保留。
  • 候选 #1 状态:这是 PDF/UA producer-side 充实的第二刀。后续:页面内容 BDC/EMC 自动包裹(AddTextField + CurrentPage.TextOut 路径无需手工调算子即可发射 marked-content 序列);注释 /StructParents 接入(让屏幕阅读器导航也覆盖 link / 表单 widget)。

2026-05-19 Version 2.87.0

  • 新增 PDF/UA-1 (ISO 14289-1) 高层 opt-in,通过新 `PDFUACompliance` 属性开启。在 BeginDoc / EndDoc 之前设为 True 自动启用所有 HotPDF 能在不需要调用方配合的情况下满足的 PDF/UA-1 producer-side 包装要求:/MarkInfo /Marked = true、/StructTreeRoot stub、/Lang(空时默认 'en')、/ViewerPreferences /DisplayDocTitle = true、/Metadata XMP 流、以及 XMP 包里 AIIM 注册的 pdfuaid:part = 1 标识。
  • 一个属性代替六个开关:之前要做 PDF/UA-1 合规的调用方需要逐个设 EnableTaggedPDF + EnableXMPMetadata + Lang + ViewerPreferences := [vpDisplayDocTitle] + Title,再手工拼一个自定义 XMP packet 加 pdfuaid namespace。v2.87 把这些折叠成 PDFUACompliance:=True;各个单独的 flag 仍然可用,调用方显式设了的话会被尊重。
  • XMP packet 更新:BuildXMPPacket 现在在 PDFUACompliance 为 True 时发射 xmlns:pdfuaid="http://www.aiim.org/pdfua/ns/id/" 加 <pdfuaid:part>1</pdfuaid:part>。veraPDF 等 PDF/UA-1 验证器靠这组 namespace + 标识符三元组识别文档为 PDF/UA-1 候选。
  • 用户可见影响:用 `Doc.Title := '...'; Doc.PDFUACompliance := True; Doc.BeginDoc; ...; Doc.EndDoc;` 写出来的 PDF 在 veraPDF 里验证为 PDF/UA-1 候选,不需要进一步设置——前提是文档正文通过 AddStructureElement + marked-content 序列真实构造了结构树。政府/教育/无障碍法规驱动的发布流现在有了一个开关。
  • 调用方仍需自担的工作:PDF/UA-1 同时要求结构树完整性(每个 glyph 属于某个被标记的结构元素)、图形/公式内容的 /Alt 或 /ActualText、合理的 tab 顺序、有效的标题层级等。opt-in 覆盖的是机器可检 validator 首先检查的 Catalog/metadata 层封装;完整语义合规仍靠调用方诚实地通过 AddStructureElement 和 marked-content BDC / EMC 算子构造结构树。
  • 向后兼容:PDFUACompliance 默认 False,v2.74-v2.86 调用方保持字节级输出。既有 EnableTaggedPDF / EnableXMPMetadata flag 在新 opt-in 关闭时仍独立工作。
  • 这是矩阵候选 #1(Tagged PDF / PDF/UA producer-side 充实)的第一刀。后续(v2.88+):AddStructureElement 加 /Alt 和 /ActualText 支持、AddTextField & CurrentPage.TextOut 路径加页面内容 BDC / EMC helper、注释 /StructParents 接入。

2026-05-19 Version 2.86.0

  • 扩展 RegisterUnicodeTTF 的 cmap 解析器支持 OpenType format 12(分段全 Unicode 覆盖)。v2.75-v2.85 只解析 format 4(传统 BMP 段映射);含 format 12 的现代字体现在使用更丰富的表。子表选择优先 format 12 因为它对全 codepoint 范围都权威。
  • 新 `_TTFParseCmapFormat12` walker:读 16 字节头(format / reserved / length / language / numGroups),然后迭代 numGroups 个 12 字节组(startCharCode / endCharCode / startGlyphID)。对范围内每个 codepoint C,glyph ID = startGlyphID + (C - startCharCode)。每个多字节读取都做边界检查;截断或畸形的组静默跳过。
  • 子表打分更新:
    • platform 3 (Microsoft) + encoding 10 (Unicode full) + format 12: 120(最高)
    • platform 0 (Unicode) + encoding 4 或 6 + format 12: 110
    • 其他 format 12: 105
    • platform 3 + encoding 1 (Unicode BMP) + format 4: 100(传统 BMP 回退)
    • platform 0 + encoding 3..4 + format 4: 80
    • platform 3 + encoding 10 + format 4: 70
    • 其他 format 4: 50
  • SMP 范围:CpToGid 输出数组保持 BMP 大小($10000 项 × 2 字节 = 128 KB),因为我们的 Identity-H 编码使用 2 字节 CID(0..65535)。format 12 SMP 条目(U+10000+)被解析但不存储;format 12 walker 迭代前把 endCharCode 截到 $FFFF。/CIDToGIDMap 全 SMP 覆盖需要不同编码(Identity-V + surrogate-pair CMap 或 Adobe-Japan1),不在本刀范围。
  • 用户可见影响:只附 format 12 的字体(某些现代 CJK 字体、Segoe UI Emoji / Symbol)现在能正确填充 /W、/CIDToGIDMap、/FontFile2 和 v2.84 subsetter。v2.86 之前这些字体走 cmap 解析的静默回退、跳过全部四个流的发射,回到 v2.74 仅 metric 的结果。
  • 字节级稳定:只有 format 4 的字体(Arial、Tahoma)仍走同一代码路径产生相同的 CpToGid 输出。同时含 format 4 和 format 12 的字体现在用 format 12 处理 BMP,通常产生相同的 BMP 映射(format 12 是严格超集)。v2.74-v2.85 既有 smoke 保持字节级稳定。
  • 本刀完成 v2.4.0-v2.86.0 密集 PDF 规范合规阶段。剩余的 v2.86+ 工作(通过 surrogate-pair CMap 的 SMP 编码、字体特定 GSUB 风格变体、更高级排版)超出 "基础 Type 0 / Identity-H + AcroForm Unicode" 范围,无限期推迟到出现真实用户需求。

2026-05-19 Version 2.85.0

  • 新增 opt-in Arabic Presentation Form-B 上下文 shaping,通过新 `AutoShapeArabic` 属性开启。设为 True 时,3 个 BuildUnicode*FieldContent helper 在 bidi reversal 之前把基本阿拉伯字母(U+0621..U+064A)替换为位置对应的 presentation form(U+FE70..U+FEFF)。消费 PDF 阅读器不再需要 in-process text shaper(HarfBuzz / FreeType / GSUB binary lookup)在渲染时挑选正确的位置形式。
  • Shaping 算法:扫 logical-order UTF-16 buffer;对每个阿拉伯字母找前一个和下一个非 Transparent 字母(NSM / harakat U+064B..U+065F + alef khanjareeya U+0670 透明)。从 joining-class 上下文判定位置:D(dual-joining:BEH、TEH、SEEN、LAM、MEEM、NOON 等)按周围 strong 字母是否允许在各自侧连接,支持 isolated / initial / medial / final;R(right-joining:ALEF、DAL、ZAL、REH、ZAIN、WAW 等)只支持 isolated / final。
  • 直接用户影响(AutoShapeArabic=True):输入 "بسم"(BEH + SEEN + MEEM)emit Tj 携带 presentation form FE91(BEH initial)、FEB4(SEEN medial)、FEE2(MEEM final)。RTL reversal 后 Tj operand 是 FEE2 FEB4 FE91,任何 PDF 阅读器即使没内置 shaper 也能正确渲染连写阿拉伯文。
  • 覆盖范围:35 个基本阿拉伯字母(HAMZA 变体、ALEF 变体、28 字母阿拉伯字母表、YEH MAKSURA)。静态 Unicode 标准查表代替字体特定 GSUB binary 解析,所以 shaping 对任何 U+FE70..U+FEFF 块里有字形的字体都有效(Arial、Tahoma、Segoe UI Arabic、Noto Sans Arabic、Amiri 等)。
  • 本刀不含:波斯/乌尔都扩展字母(U+0671..U+06D3);LAM-ALEF 连写字形(FEFB / FEFC);蒙古/叙利亚(不同 shaping 模型);超出 4 位置基本模型的字体特定 GSUB 风格变体。
  • 字节级稳定:AutoShapeArabic 默认 False,所有 v2.74-v2.84 调用方保持 byte-identical /AP 输出。已经上游 pre-shape 的调用方(或用消费端 shaper)不受影响。
  • Pipeline 位置:shaping 在每个 Build*UnicodeFieldContent helper 里 UTF-8 解码之后、bidi reversal 之前跑,所以 shape 后的 presentation form 在 AutoDetectFormBidi / FormUnicodeRTL 启用时也能正确 RTL-reverse。
  • 剩余 UAX #9 工作:SMP cmap format 12(U+10000+ codepoint 在 RegisterUnicodeTTF 中的覆盖)仍待。

2026-05-18 Version 2.84.0

  • 新增 file-based TrueType subsetter 在 EndDoc 时触发。RegisterUnicodeTTF + AddTextField 之后,文档在 BuildUnicode*FieldContent emission 过程中累积每 codepoint 的使用集;EndDoc 把已用 codepoint 转为 glyph ID,走 composite glyph 闭包,重建嵌入字体程序只保留这些 glyph。v2.82 raw embed 被替换;其余表结构保持有效因为 subsetter 保持 glyph ID 不变(只压缩 glyf/loca)。
  • 直接用户影响:嵌入 Unicode TTF 的 PDF 体积显著缩小。"Hello World" 文档嵌入 Arial 在 v2.82 是 ~350 KB 压缩;v2.84 降到 ~10-15 KB。CJK 字体典型 1000-3000 glyph 子集从 ~14 MB 压缩到 ~500 KB-2 MB。
  • Subsetter 算法:保持 glyph ID 不变,cmap、hmtx、/CIDToGIDMap、/W 都仍有效。对每个 glyph index 0..numGlyphs-1,新 glyf 表要么拷贝原字节(已用)要么通过 loca[i+1] = loca[i] 发出空条目(未用)。loca 格式(短/长 by head.indexToLocFormat)保留。每表 checksum + head.checksumAdjustment 都按 spec 重算。
  • Composite glyph 闭包:当已用 glyph 的 numContours == -1(composite),subsetter 解析其 component 列表(每 component 的 flags + glyphIndex + 可变 args/transform 跳过),标记 components 为已用,迭代直到无新 glyph 添加。带边界检查防止畸形 composite 链导致死循环。
  • Spec 合规:Type 0 dict + CIDFontType2 descendant 的 /BaseFont 和 FontDescriptor 的 /FontName 全部加 6 字母 AAAAAA+ subset 前缀,从已用 glyph 集的确定性 FNV-1a 哈希派生。Spec 9.6.4 要求前缀表明字体程序已 subset;没有它 spec-strict 阅读器会警告嵌入字体被修改。
  • 使用跟踪基础设施:65536 项 FUnicodeUsedCps[] Boolean 数组在首次 RegisterUnicodeTTF 时 lazy 分配。3 个 BuildUnicode*FieldContent helper(单行/多行/comb)在发射 UTF-16BE hex Tj operand 时标记 code unit。SetFormUnicodeFontDict('', nil) 和后续 BeginDoc 复位时清空。
  • 失败安全:subsetter 任何一步无法完成(缺表、loca 畸形、composite 闭包失败、压缩失败)就保留 v2.82 raw embed。PDF 仍有效;只是体积没减。这保留了 v2.82 的可靠性同时在 happy path 上提供 subset 收益。
  • 本刀不含:OpenType GSUB Arabic/Syriac/Devanagari 上下文 joining;SMP cmap format 12(仍仅 BMP)。推到 v2.85+。

2026-05-18 Version 2.83.0

  • 给 RegisterUnicodeTTF 增加 PDF 1.7 ISO 32000-1 9.10.3 /ToUnicode CMap。Type 0 字体字典现在带 /ToUnicode 间接引用,指向 Adobe-Identity-UCS CMap 流,消费阅读器能从 PDF 中提取 Unicode 文本。v2.74-v2.82 的 PDF 没 /ToUnicode 时复制粘贴出来的是原始 CID 索引而不是 Unicode codepoint;屏幕阅读器读不出 AcroForm 内容;PDF/A 合规失败。
  • CMap 结构:最小 Adobe-Identity-UCS CMap,包含 /CIDSystemInfo (Adobe / UCS / 0)、/CMapName /Adobe-Identity-UCS、/CMapType 2、一个 codespacerange 覆盖 <0000>..<FFFF>、一个 bfrange 把 <0000> <FFFF> 映射到 <0000>(identity)。PostScript CMap 文本通过 FlateDecode 压缩到 ~150 字节,嵌入开销可忽略。
  • Identity 映射的理由:v2.74-v2.82 配置是 Identity-H + Adobe-Identity-0 + CID = Unicode codepoint,所以 ToUnicode 反向映射在整个 BMP 范围内本就是 identity。一个 bfrange 条目覆盖全部映射;不需要每 codepoint 的 bfchar 列表。
  • 用户可见影响:从 PDF 阅读器(Adobe、Foxit、Chrome PDF 阅览)选择/复制粘贴文本现在产出原始 Unicode codepoint 而不是 CID 乱码。屏幕阅读器(NVDA、JAWS、VoiceOver)能朗读 AcroForm 文本内容用于无障碍场景。PDF/A-1/2/3 合规现在可达成(PDF/A 要求所有 Type 0 字体必须带 /ToUnicode)。
  • Pipeline 位置:/ToUnicode 发射在 WArrayValid try/except 块之外,因为 identity 映射不依赖 cmap 解析成功。即使 /W / /CIDToGIDMap / /FontFile2 因字体表损坏被静默跳过,/ToUnicode CMap 仍然被发射且语义正确。
  • 流对象:indirect THPDFStreamObject + /Filter /FlateDecode(无 /Length1,因为 ToUnicode 是文本流不是字体程序)。通过 Result.AddValue('ToUnicode', ...) 挂到 Type 0 wrapper 字典。与 v2.77 /CIDToGIDMap 和 v2.82 /FontFile2 共享 zlib 压缩模式。
  • 本刀不包含:每 codepoint bfchar 条目(可支持非 identity 映射如 fi → f+i 多字符序列,但我们 Identity 配置从不需要);超出 BMP 的 SMP 覆盖(仍是 0x0000..0xFFFF 范围)。这些推到 v2.84+。

2026-05-18 Version 2.82.0

  • 给 RegisterUnicodeTTF 增加 PDF 1.7 ISO 32000-1 9.6.4 + 9.9 /FontFile2 原始字体嵌入。实际字体程序(TTF / OTF 二进制字节)现在以 /FontFile2 流挂在 FontDescriptor 下随 PDF 一起发出去,消费阅读器不再需要根据 /BaseFont 名字去 OS / 应用字体缓存里找对应字体。结果:PDF 自包含,在没装该字体的系统上也能正确渲染。
  • 流结构:indirect THPDFStreamObject + /Filter /FlateDecode + /Length1 = 原始字体文件未压缩字节数(spec Table 124)。压缩用 TZCompressionStream (zcDefault, windowBits 15) 沿 v2.77 /CIDToGIDMap 模式。典型 Latin TTF 压缩率 30-50%(例如 700 KB Arial → 350 KB 压缩嵌入)。
  • Pipeline 集成:嵌入与 v2.75 /W 数组和 v2.77 /CIDToGIDMap 在同一 WArrayValid 分支里发出,cmap 解析失败(corrupt subtable / 不支持的 format)三个特性流一起跳过,平滑回退到 v2.74 仅 metric 的结果。
  • 用户可见影响:v2.74-v2.81 的 PDF 技术上是 Type 0 / CIDFontType2 复合字体,但字体程序缺失。消费阅读器需要按 /BaseFont (PostScript 名) 在自己字体缓存里查。装了同名字体的系统上恰好渲染正确;没装的系统上要么替换为别的字体(错字形 / 错度量),要么渲染占位方框。v2.82 把字体程序随 PDF 一起发——同一个 producer 在 RegisterUnicodeTTF 解析过的字体程序——消费渲染与 OS 字体可用性无关,确定性输出。
  • 权衡:典型 Latin TTF 嵌入后 PDF 增长 ~150-500 KB(压缩后体积);CJK 字体增长 2-20 MB 压缩。这正是消费端字体替换原本规避的体积代价,但同时也避免了不确定的渲染。子集化(v2.83+ 续刀)会通过裁掉未用 glyph 减少体积。
  • spec 备注:v2.82 嵌入时 /BaseFont 保留原 PostScript 名(无 "AAAAAA+" subset 前缀),因为我们嵌入完整字体。Spec 9.6.4 的 6 字母前缀仅在字体程序被子集化时使用;v2.83+ 接子集化时会加前缀。
  • 本刀不包含:字体子集化(单个嵌入字体把整个文件体积加进 PDF)、ToUnicode CMap(PDF/A copy-paste)、SMP cmap format 12(U+10000+ codepoint 仍未映射)。这些推到 v2.83+。

2026-05-18 Version 2.81.0

  • 把 UAX #9 W4(数字间分隔符)和 W5(数字邻接终结符)移植到 LTR 段重排路径。v2.72.0 / v2.73.0 起这两个规则只在 RTL 路径里 inline 跑;v2.81 抽出共享 helper `_ApplyUAX9W4Rules` 和 `_ApplyUAX9W5Rules`,两个段落方向都调用同一套弱规则变换。
  • 重构:RTL 路径 Step 1b (W4) 与 Step 1c (W5) inline 代码替换为单行 helper 调用。Helper 接 Wide 字符串 + Classes[] 数组 in-place 修改 Classes[];逻辑与 v2.72/v2.73 inline 实现完全一致,因此 RTL 输出在 refactor 后字节级稳定。
  • LTR 路径在 W1 NSM 继承 (Step 1b) 与 W7 (Step 1d) 之间插 Step 1c W4 + Step 1c.2 W5。后续 N 规则 (Step 1e) 与 I 规则 (Step 1f) 重新编号保持 pipeline 显式有序。
  • LTR 端可见影响:含嵌入 RTL 的混排 LTR 段中,RTL strong 在数字/ET 之前时,W5 现在把 ET 提升为 EN,避免 N1 把它卷进 R run。例如 "Hi shin $1"(LTR 段含 shin Hebrew 字母后跟 "$1")之前输出为 "Hi $ shin 1" —— $ 被卷进 R run 与 shin 换位。v2.81 输出 "Hi shin $1" —— $ 保持挨着它的数字。W4 同理:"Hi shin $1,2 widgets" 保留 "$1,2" 作为逻辑整块,不在逗号处切开。
  • LTR 路径字节级稳定场景:纯 LTR 无 R 前置(如 "12,345 widgets")无可见变化——W7 sor=L 已经把所有 EN 吸收为 L;LTR 段只有 L + RTL 无数字 ET 的输入也不受影响。既有 v2.79 LTR smoke(smoke_bidi_n_rules_ltr、smoke_bidi_ltr_reorder)继续字节级通过。
  • Pipeline 顺序(RTL 不变):W1 -> W4 -> W5 -> W7 -> N0 -> N1 -> N2 -> I2 -> L2。LTR 现:W1 -> W4 -> W5 -> W7 -> N0 -> N1 -> N2 -> I 规则 -> L2。两条路径共享 W4/W5/W7/N rules helper;仅分类(Step 1a)与最终 L2 走法在 RTL 和 LTR 之间不同。
  • 剩余 UAX #9 工作:OpenType GSUB Arabic/Syriac/Devanagari 上下文 joining、RegisterUnicodeTTF 后续(ToUnicode CMap、SMP cmap format 12、/FontFile2 嵌入 + subsetting)仍待。

2026-05-18 Version 2.80.0

  • 加 Unicode UAX #9 W7 EN 桥接:往回扫到第一个 strong type(L/R/AL)若是 L(或扫到 buffer 起点且 sor=L),则该 EN 类改为 L。W7 在 W4(数字间分隔符)和 W5(数字邻接终结符)之后跑,覆盖 W4 把 separator 升级为 EN 后还需被 L 桥接的边角。
  • RTL 段可见影响:输入 "Hello 123 Arabic" 在 RTL 段,v2.79 输出视觉布局 "Arabic SP 123 SP Hello"——EN run 夹在 L (Hello) 与 AL (Arabic) 之间,N 规则把 LTR phrase 切成两段独立 run(中间 SP 停在嵌入 level)。v2.80 W7 把 EN 转 L(因为 L 在前面),N1 接着吸收 Hello 与 123 间的 SP 进 L run;整个 "Hello 123" 成一个 level-2 子串整体反转。最终视觉 "Arabic SP Hello SP 123",对齐 UAX #9 严格行为。
  • W7 不开火的情况:EN 最近的 strong 是 R 或 AL(Arabic letters 在数字前面);EN 在 buffer 起点 + RTL 段(sor=R);EN 前没有任何 L。这些场景 v2.79 行为保留——v2.72/v2.73 的 W4/W5 baseline smoke 字节级稳定。
  • LTR 段效果:W7 也在 LTR 路径跑(spec 一致性),sor=L(扫到起点时)。HotPDF 简化的单嵌入 LTR I 规则下,L 与 EN 都落 level 0,所以 EN→L 不直接改 Levels[],但确实改 N1 吸收模式:L 与"被桥接的 EN"之间的 SP 现在满足 N1 同向条件能合并。纯 LTR 输入无内嵌 RTL 时是字节级 no-op;LTR 输入含尾部 RTL 时(如 "Hello 123 shalom")SP 相对 RTL 块的位置移一位。
  • W6(按 UAX #9 严格定义残余 ES/CS/ET → ON)在 HotPDF 简化 Classes[] 表中隐式落地:ES、CS、ET、WS、ON 都归 class 0 (bcOther),N 规则统一处理 class 0。W6 的 "升 ON" 在我们模型中是 no-op(N1/N2 不区分 ON 与 WS)。代码 W7 helper 注释里同步说明。
  • Pipeline 顺序(RTL 路径):W1 NSM 继承 -> W4 数字间 ES/CS -> W5 数字邻接 ET -> W7 EN 前置 L -> N0 配对括号 -> N1 同向 NI 吸收 -> N2 嵌入默认 -> I2 级别分配 -> L2 反转-再反转。LTR 路径同顺序但缺 W4/W5(仍 RTL 唯一;LTR W4/W5 移植是下一个候选)。
  • 新增共享 helper `_ApplyUAX9W7Rules(Classes, ParaIsRTL)`,in-place 修改 Classes[]。扫描跳过 weak/NSM/EN/AN,只 L/R/AL 算 strong;扫到 buffer 起点未找到 strong 时用段落方向(sor)作隐式 strong。RTL 路径传 ParaIsRTL=True,LTR 路径 False。
  • 剩余 UAX #9 工作:LTR 路径 W4/W5 移植(当前 RTL 唯一)、OpenType GSUB Arabic/Syriac/Devanagari 上下文 joining、RegisterUnicodeTTF 后续(ToUnicode CMap、SMP cmap format 12、/FontFile2 嵌入 + subsetting)仍待。

2026-05-18 Version 2.79.0

  • 把 v2.78.0 的 N0/N1/N2 规则镜像到 LTR 段重排路径。修复对称 bug:多词 RTL phrase 嵌入 LTR 段(比如英文句子含多个 Hebrew/Arabic 词带词间空格)时,RTL 读者看到的词序倒——之前词间空格停在 level 0,把 RTL phrase 切成多段独立 R run。v2.79 N1 把空格吸收进周围 R run,整个 phrase 成一个 level-1 子串,L2 反转保留词的逻辑顺序。
  • 重构 LTR 段路径(`_ApplyUAX9L2ReversalLTRPara`)使其结构与 RTL 段对齐:先把每个 UTF-16 code unit 分类到 Classes[] 字节数组,再在 Classes[] 上做 W1 NSM 继承(v2.79 前是直接在 Levels[] 上做),调用共享 helper `_ApplyUAX9NRules`(接受嵌入方向参数:1=L 用于 LTR 段、2=R 用于 RTL 段);I 规则再从 Classes[] 推 Levels[]。RTL 与 LTR 两条路径现共享单一 N 规则实现。
  • 共享 helper `_ApplyUAX9NRules(Wide, Classes, EmbedDirCls)` 把嵌入方向作为类参数(1 或 2),统一应用 N0 配对括号解析 + N1 同向 NI 吸收 + N2 嵌入默认。RTL 路径传 2 (R);LTR 路径传 1 (L)。v2.78 RTL 端行为字节级保留。
  • LTR 端可见影响:"Hi shalom olam World"(逻辑输入 H i SP shin lamed vav final-mem SP ayin vav lamed final-mem SP W o r l d)之前输出把两个 Hebrew 词相对位置换位(每个词内部正确反转,但词与词的相对位置错了)。v2.79 保留逻辑词序——RTL 读者看到的 Hebrew chunk 内顺序与预期一致。括号包裹的多词 RTL phrase 通过 N0 + N1 协作同样修复。
  • LTR 段 pipeline 顺序(镜像 RTL):分类到 Classes[] -> W1 NSM 继承 -> N0 配对括号 -> N1 同向 NI 吸收 -> N2 嵌入默认 -> I 规则(推 Levels[])-> L2 反转 level-1 run。W4/W5 数字处理 pass 还没移植到 LTR 路径;LTR 段中含数字 + 分隔符的输入直接走 N 规则。
  • 既有 LTR smoke 字节级稳定:纯 LTR ASCII、LTR 段中单词 RTL、RTL + LTR 混排但 RTL 词间无空格——重构后产生相同 Levels[](W1 NSM 继承在 Levels[] 上做与在 Classes[] 上做对外输出可观测等价)。v2.67 / v2.69 / v2.71 的 LTR smoke 继续字节级通过。
  • 剩余 UAX #9 工作:W6/W7 残余弱类清理、W4/W5 LTR 路径数字处理(当前 RTL 唯一)、OpenType GSUB Arabic/Syriac/Devanagari 上下文 joining、RegisterUnicodeTTF 后续(ToUnicode CMap、SMP cmap format 12、/FontFile2 嵌入 + subsetting)仍待。

2026-05-18 Version 2.78.0

  • 给 RTL 段重排 pipeline 加 Unicode UAX #9 简化 N0 配对括号解析 + N1 同向 strong 中间中性类吸收 + N2 残余中性回退到嵌入方向。修复 HotPDF 之前的可见 bug:多词 LTR phrase 嵌在 RTL 段(比如 Hebrew/Arabic 段里夹带英文短语带词间空格)时,词序会倒——因为空格停在嵌入级别(level 1)、把 LTR phrase 切成多段独立 L run。N1 把空格吸收进周围 L run,整个 phrase 成一个 level-2 子串,L2 反转-再反转后保留逻辑顺序。
  • N0 配对括号:覆盖 22 个 BMP 括号对,从 ASCII 圆/方/花括号到数学/排印(ceiling、floor、math 角括号、math 白方括号、白花括号)到 CJK 标点括号(角括号、双角括号、corner、白 corner、黑/白 lenticular、tortoise shell、白 tortoise、白方)再到全角形式。BD16 栈式配对检测(按 spec 上限 63 条);配对解析扫描内部第一个强方向(L、R、AL、EN、AN——其中 EN/AN 按 spec 算 R 类),如果内部方向与嵌入方向不一致则查左侧(开括号之前)的强方向上下文作为回退。
  • N1 吸收:扫描 Classes[] 找 bcOther 的最长 run,看左右两侧(跳过 NSM)非中性强方向,两侧方向影响一致(都是 L,或都是 R 等价:含 EN/AN)则整 run 取该方向。实际影响:嵌在 Arabic/Hebrew 段里的英文短语中的空格、逗号、句号、冒号现在被归到 phrase 而不是把它切碎。
  • N2 默认:未被 N0/N1 处理的 bcOther 取嵌入方向。RTL 段取 R(class 2)。N2 后 Classes[] 全部落到 {1..6},I2 不再依赖 bcOther 默认 level 1。
  • 用户可见影响:之前 HotPDF 把 "aleph aleph SP a b c SP d e f SP aleph aleph"(逻辑输入:英文 phrase "abc def" 嵌在 RTL 段)输出为 "aleph aleph SP d e f SP a b c SP aleph aleph"——词被换位(因为词间空格停在 level 1)。v2.78 输出 "aleph aleph SP a b c SP d e f SP aleph aleph"——phrase 完整保留。嵌入 phrase 周围的括号通过 N0 + 消费端 L4 mirror 正确渲染。
  • Pipeline 顺序:W1 NSM 继承 -> W2/I2(EN/AN BIDI 表分类,不在本 pass)-> W4 数字间 ES/CS -> W5 数字邻接 ET -> N0 配对括号 -> N1 同向吸收 -> N2 嵌入默认 -> I2 级别分配 -> L2 反转-再反转。N 规则夹在 W 与 I 之间;既有 W 规则输出喂给 N 规则作输入。
  • 对没有 RTL 段嵌入多词 LTR phrase 的既有 smoke 字节级稳定:纯 RTL、纯 LTR(函数原样返回)、单词 LTR 嵌 RTL(没有内部中性需要吸收)、Arabic + 格式化数字(带空格 + 逗号)——空格从 bcOther 变 R-等价但级别相同,L2 反转输出不变。v2.66/v2.67/v2.68/v2.71/v2.72/v2.73 基线 smoke 继续字节级通过。
  • 剩余 UAX #9 工作:W6/W7 残余弱类清理、LTR 段的 N 规则镜像(多词 RTL phrase 嵌入 LTR 段)、OpenType GSUB Arabic/Syriac/Devanagari 上下文 joining、RegisterUnicodeTTF 后续(ToUnicode CMap、SMP cmap format 12、/FontFile2 嵌入 + subsetting)仍待。

2026-05-18 Version 2.77.0

  • 给 RegisterUnicodeTTF 增加 PDF 1.7 ISO 32000-1 9.7.4.2 /CIDToGIDMap 流。每个 BMP Unicode codepoint(CID)现在映射到嵌入字体程序内真正的 glyph index。v2.77 之前,HotPDF 的 Type 0 / CIDFontType2 + Identity-H 配置没写 /CIDToGIDMap 条目,按 spec 默认为 /Identity——消费阅读器直接拿 CID 当字形表索引,结果几乎每个字符都映射到错误的字形(真字体字形表里 glyph index N 几乎从不等于 Unicode codepoint N)。
  • 实现复用 v2.75 /W 数组已做的 cmap 解析。CpToGid[0..$FFFF] 内存表序列化为 131072 字节大端流(每个 CID 2 字节)并 FlateDecode 压缩;典型真字体压缩率 80-95%(绝大多数 BMP codepoint 无字形映射、序列化为 0x0000)。压缩后流以间接对象形式挂到 CIDFontType2 descendant 字典的 /CIDToGIDMap 条目。
  • 用户可见影响:用 RegisterUnicodeTTF + SetFormUnicodeFontDict + AcroForm Tx widget 生成的 PDF,现在消费阅读器渲染的字形与源字体原生渲染一致。v2.77 之前依赖 /Identity 默认值的 PDF 在 spec 严格的阅读器上肉眼可见地渲染错误(应该出现的 Latin / 数字 / 标点位置出现随机字形);那些回退到通过 Unicode 解读 cmap 的阅读器恰好渲染正确——但走的是非标准 fallback 行为。
  • Pipeline 位置:/CIDToGIDMap 构造在 v2.75 同一 WArrayValid 分支里建 /W 时进行,两条挂接共享 cmap walk,新增的只有第二次压缩。cmap / hmtx / maxp 解析失败时,silent fallback 同时跳过 /W 和 /CIDToGIDMap;消费阅读器看到 v2.77 之前的字典并回退到自己的默认 cmap 解读。
  • 本刀不包含(推到 v2.78+):SMP cmap format 12(U+10000+ codepoint 在表里仍映射为 GID 0)、/FontFile2 流嵌入(消费阅读器仍按 /BaseFont 名通过自己的字体匹配机制解析)、字体子集化(128 KB 原始映射表覆盖全 BMP)、ToUnicode CMap(复制/粘贴 + 屏幕阅读器往返)。

2026-05-18 Version 2.76.0

  • 把 v2.75.0 的 TTF /W advance-width 数组接进 HotPDF 自身的 multiline word-wrap 算法。v2.65.0 BuildUnicodeMultilineFieldContent.CodeUnitAdvance helper 现在查 RegisterUnicodeTTF 填充的 per-codepoint advance 缓存,取代 0.5/1.0 narrow/wide 启发式。producer 端断行现在与 consumer 端 /W 渲染对齐:HotPDF 选的 wrap 点与消费阅读器渲染 /Tj 时实际换行位置一致。
  • 新增私有字段:FAcroFormUnicodeAdvances(dynamic Word 数组,大小 65536 或 0)缓存每 codepoint 的 advance 宽度(PDF 设计单位,1000 per em)。FAcroFormUnicodeAdvancesActive 标志区分 "缓存已被 RegisterUnicodeTTF 填充" 与 "未加载字体(回退到启发式)"。RegisterUnicodeTTF 在构建 /W 数组的同一次 hmtx + cmap 遍历中填这两个字段,不需要额外解析。
  • CodeUnitAdvance 查找顺序:若 FAcroFormUnicodeAdvancesActive 且缓存对该 code unit 有非零条目,把缩放后宽度除以 1000.0 作为 em 分数返回;否则回退到 v2.65 启发式(ASCII / Latin-1 narrow,CJK / Hangul / Hiragana / Katakana / 全角宽 wide)。Surrogate pair 半边仍走启发式(缓存仅覆盖 BMP)。
  • 缓存生命周期:THotPDF.Create 时清空 / inactive,SetFormUnicodeFontDict('', nil) 显式重置时清空,每次后续 RegisterUnicodeTTF 调用时重新填充。占 128 KB(64K codepoint * 2 字节)仅在加载过 TTF 时;从未用过 Unicode 字体的实例零内存代价。
  • RegisterUnicodeFontDict(v2.70.0)和无 /W 数组的 RegisterUnicodeTTF(v2.74.0,比如 maxp / cmap / hmtx 表缺失)让缓存保持空 / inactive,multiline word-wrap 回退到 v2.65 启发式——这些路径输出与 v2.65 字节级一致。
  • 用户可见影响:用典型 Latin 字体(Arial、Segoe UI、Times)的 multiline AcroForm Tx widget 现在在与消费阅读器实际渲染一致的字符边界换行。v2.76 之前,v2.65 启发式把每个 ASCII 字符当 0.5 em 宽;真实字体中 "M" ~0.8 em、"i" ~0.3 em,启发式高估/低估 30-60%,导致消费阅读器换行与 HotPDF 规划不符,造成文本溢出或短行。
  • Single-line BuildUnicodeTextFieldContent 和 comb BuildUnicodeCombFieldContent 不用 CodeUnitAdvance,本刀不影响(单行无 wrap,comb 用等宽 cell)。
  • 本刀不包含:SMP cmap format 12(U+10000+ codepoint 仍按 surrogate 半边走启发式)、ToUnicode CMap、/FontFile2 流嵌入、字体子集化。这些推到 v2.77+。

2026-05-18 Version 2.75.0

  • 扩展 v2.74.0 的 THotPDF.RegisterUnicodeTTF,加入 hmtx + maxp + cmap 三表解析,让生成的 CIDFontType2 descendant 携带按 codepoint 真实数值的 /W advance-width 数组。消费阅读器现在按真实字体度量渲染文本宽度,不再对每个字形回退 /DW = 1000——AcroForm /AP 外观流中的文本与源字体渲染宽度一致。
  • 新增 unit-level helper 解析三张额外 SFNT 表:_TTFParseMaxpNumGlyphs(从 maxp 读 numGlyphs)、_TTFParseHmtxAdvances(按 hhea 的 numberOfHMetrics 大小生成每字形 advance-width 数组)、_TTFFindAndParseCmap + _TTFParseCmapFormat4(通过 BMP segment-mapping format 4 子表查 Unicode codepoint -> 字形 ID)。_TTFBuildWArray 拼装 PDF 1.7 9.7.4.3 form 1 稀疏 /W 数组:把连续非默认宽度 codepoint 合并成 [startCID [w1 w2 w3 ...]] run,跳过宽度等于 /DW = 1000 的 codepoint 以裁剪数组大小。
  • cmap 子表选择优先 Microsoft Unicode BMP(platform 3, encoding 1, format 4)——Windows 字体通用 cmap 布局。回退到 Unicode platform(0, encoding 3 或 4)覆盖跨平台 OpenType 字体,再到 platform 3 encoding 10(Unicode full / 仅 format 4),最后任意 format 4。非 format 4 子表(format 0/2/6/8/12/13/14)在本刀忽略——SMP 通过 format 12 推到 v2.76+。
  • /W 数组通过直接走树挂到 CIDFontType2 descendant:Type0 -> /DescendantFonts -> [0] -> AddValue('W', WArray)。v2.70 RegisterUnicodeFontDict 签名不变;descendant 字典在装配后原地修改。
  • hmtx 的 FUnit 宽度按 head 表 unitsPerEm 缩放到 PDF 设计单位(1000 per em)。cmap 无映射(返回 0 / .notdef)的 codepoint 当作 DW = 1000 处理,从 /W 中省略。典型 Latin 字体(Arial、Segoe UI、Times)产出的 /W 含 500-1500 条 codepoint;CJK 字体可达 10K+ 条。
  • 本刀不包含(推到 v2.76+):/W 宽度数据尚未被 HotPDF 自身的 multiline word-wrap 算法消费——v2.65 BuildUnicodeMultilineFieldContent 仍用 0.5/1.0 窄/宽启发式做断行计算;/W 数组纯粹用于消费端渲染精度。完整 producer 端 perfect-wrap(用 /W 驱动 wrap 计算)推到 v2.76+。也推后:ToUnicode CMap(cmap 反向映射)、/FontFile2 流嵌入、字体子集化、SMP cmap format 12。
  • /W 解析带 try/except 回退——maxp / cmap / hmtx 缺失或任一子表畸形时,RegisterUnicodeTTF 静默继续使用 v2.74 仅含 metric 的结果;消费阅读器对每个 codepoint 回退 /DW = 1000。无 silent 损坏:表读取的每个多字节都有边界检查。

2026-05-18 Version 2.74.0

  • 新增 THotPDF.RegisterUnicodeTTF,基于文件路径的 v2.70.0 RegisterUnicodeFontDict 包装:加载 TTF / OTF 字体文件,解析 head、hhea、OS/2、post 和 name 表,把 FUnit 度量缩放成 PDF 设计单位(每 em 1000),把数值交给 RegisterUnicodeFontDict 完成 Type 0 / CIDFontType2 + Identity-H 复合字体字典构造。返回的字典直接给 SetFormUnicodeFontDict 用——调用方不再需要知道字体的精确 metric 值,也不必懂 SFNT 二进制格式。
  • API:RegisterUnicodeTTF(FontPath, LogicalName='')。FontPath 是 .ttf / .otf 文件的绝对或相对路径。LogicalName 保留给 v2.75+(届时可用于区分同 PostScript 名的字体)。
  • 解析的表:head(unitsPerEm,FontBBox xMin/yMin/xMax/yMax in FUnits)、hhea(ascender、descender in FUnits)、OS/2(usWeightClass 用于估算 /StemV、sCapHeight、fsSelection 用于 italic bit)、post(italicAngle 从 FIXED 16.16 转角度、isFixedPitch -> /Flags bit 1)、name(PostScript 名取自 nameID 6 的 Windows 或 Mac 平台记录,回退到 nameID 4 的完整字体名)。每个表偏移/长度 + 每次多字节读取都有边界检查,畸形字体抛清晰异常而不是内存错误。
  • 自动导出的 metrics:/FontBBox L/B/R/T 缩放到 PDF 设计单位、/Ascent + /Descent 缩放、/CapHeight 从 OS/2 sCapHeight(version < 2 时回退到 ascent)、/ItalicAngle 从 post.italicAngle、/StemV 估算为 50 + (usWeightClass - 400) / 5 限制在 [50, 250]、/Flags = Symbolic + FixedPitch(post 说是)+ Italic(fsSelection bit 0 置位或 italicAngle != 0)。/BaseFont 用解析的 PostScript 名;name 表无可用名时回退 "UnnamedTTF"。
  • 异常分支(带描述消息):文件缺失、size 不合理(< 12 字节或 > 64 MB)、无法识别的 sfntVersion(必须是 0x00010000 / 'OTTO' / 'true' / 'typ1')、缺必需表(head / hhea / OS/2 / post / name)、表小于所需字段。
  • 本刀不包含(推到 v2.75+):/W advance-width 数组从 hmtx 读出(需要 maxp 提供 numGlyphs + cmap 提供 Unicode -> glyph 映射;今 v2.65 multiline word-wrap 仍用 0.5/1.0 启发式)、ToUnicode CMap 从 cmap 反向(今 v2.56+ Identity-H 映射假定 CID == Unicode codepoint)、把字体原字节嵌入为 /FontFile2 流、字体 subsetting。v2.74.0 阶段消费阅读器仍需通过自身 font-matching 解析 /BaseFont 名。
  • Smoke smoke_unicode_ttf.dpr 加载 Windows 系统字体(按顺序探测 %SystemRoot%\Fonts 下 arial.ttf / segoeui.ttf / times.ttf / verdana.ttf)验证 RegisterUnicodeTTF 返回携带文件解析 metric 的 Type 0 字典。Verifier 检查结果 /BaseFont 不是 stub 占位、FontBBox / Ascent / Descent / ItalicAngle 在真实字体合理范围。

2026-05-18 Version 2.73.0

  • 新增 Unicode UAX #9 简化 W5(European Terminator 邻接 European Number)规则,应用于 RTL 段落。修复 Arabic 文本含货币 / 百分号时的可见 bug——"$12.50"、"50%"、"€100"、"شار 12.50$ ريال"——之前货币符号或百分号字形在数字子串与周围 RTL 文本的边界处孤立漂移。v2.73 W5 让邻接数字的 ET 加入数字 run 一起反转,在 RTL 段落里保留 "货币 + 数字" 单元作为一个 LTR 子串。
  • 识别的 W5 ET(codepoint 集):U+0023 # (number sign)、U+0024 $ (dollar)、U+0025 % (percent)、U+00A2..U+00A5 (¢ £ ¤ ¥)、U+00B0 ° (degree)、U+00B1 ± (plus-minus)、U+066A ٪ (Arabic percent)、U+2030..U+2031 (‰ ‱ per-mille / per-ten-thousand)、U+20A0..U+20CF (Currency Symbols block 含 € ₹ ₽ ₩ 等)。任一侧(跳过 NSM)有 EN 的 ET 序列整体转换为 EN。
  • 规范扩展:HotPDF 简化 W5 同时把邻接 AN(Arabic-Indic 数字)的 ET 转换为 AN,超出 spec 的 "仅 EN" 限制。这让 ASCII 和 Arabic-Indic 数字上下文对 ET 处理对称——"٥٠٪"(Arabic-Indic 50 + Arabic percent)和 "50%"(ASCII)行为一致。
  • 具体修复:"شار $12.50 ريال"(logical UTF-16 0634 0627 0631 0020 0024 0031 0032 002E 0035 0030 0020 0631 064A 0627 0644)。v2.72 输出:0644 0627 064A 0631 0020 0031 0032 002E 0035 0030 0024 0020 0631 0627 0634——$ 字形孤立在 memory 位置 11,介于数字子串与尾部空格之间,读起来别扭。v2.73 输出:0644 0627 064A 0631 0020 0024 0031 0032 002E 0035 0030 0020 0631 0627 0634——"$12.50" 完整保持为 RTL 流中的一个 LTR 子串。
  • BIDI_Class 表修正:U+066A ARABIC PERCENT SIGN 从 bcAL 移到 bcOther(以便 W5 ET pass 能识别并转换它)。Arabic block 查表现在包含数字区段(bcAN)、harakat / shadda 块(bcNSM)、alef khanjareeya(bcNSM)、十进制 / 千分位分隔符(bcAN)和百分号(bcOther + 经 W5 处理为 ET)。
  • 管线顺序:W1 NSM 继承(LTR 路径显式,RTL 隐式)-> W4 数字间 ES/CS -> W5 数字邻接 ET -> I2 级别分配。W5 在 W4 之后运行,让经 W4 转成 EN 的分隔符也被 W5 视为 "EN 邻接",复杂的 "数字 + 分隔符 + 货币" 表达式在单次管线遍历中完整解析。
  • 不含 ET 邻接数字的输入字节级与 v2.72 输出一致。现有 v2.59-v2.72 smoke(RTL Arabic / Hebrew / SMP RTL / NSM positioning / LTR-para reorder / 数字处理 / W4)全部 byte-stable。
  • 剩余 UAX #9 工作(W6 / W7 cleanup、N0 bracket pair via UAX #9 BD16 stack scan、N1 / N2 neutral resolution、OpenType GSUB Arabic / Syriac contextual joining、文件级 RegisterUnicodeTTF)仍排在后续版本。

2026-05-18 Version 2.72.0

  • 新增 Unicode UAX #9 简化 W4(ES / CS 在两个同类型数字之间转换)规则,应用于 RTL 段落。修复 Arabic 文本含格式化数字时的可见 bug——"السعر 12,345 ريال"(带千分位逗号的价格)、"12.50"(小数点)、"12:30"(时间冒号)——之前在反转的 /Tj 输出中数字 run 被分隔符切开,产生在 RTL 流中倒着读的视觉错乱数字。v2.72 W4 让分隔符加入数字 run,两者一起反转一次再随整体一起反转一次,在 RTL 段落里保留数字 + 分隔符的 logical 顺序。
  • 识别的 W4 分隔符:European Separator U+002B (+) / U+002D (-)、Common Separator U+002C (,) / U+002E (.) / U+002F (/) / U+003A (:) / U+00A0 (NBSP)、Arabic Common Separator U+060C(阿拉伯逗号)。每个在被两个 EN 包围时转成 EN,在被两个 AN 包围时转成 AN。混合类型相邻(EN + AN 或 数字 + 非数字)保持分隔符为 bcOther / level 1 / 周围 RTL 流不变。完整 UAX #9 W4 简化点:跨 NSM 的 peek 已处理(紧邻分隔符的 NSM 不会破坏 W4 查找),但多个分隔符连续时是逐位置变换而不是 spec 的 "单个" 分隔符限定(实际场景影响很小,因为真实数字很少有相邻分隔符)。
  • 具体修复:"شار 12,345 ريال"(logical UTF-16 是 阿拉伯字母 空格 "1" "2" "," "3" "4" "5" 空格 阿拉伯字母)。v2.71 输出:0644 0627 064A 0631 0020 0035 0034 0033 002C 0031 0032 0020 0631 0627 0634——数字被逗号切开、每段单独反转("345" 在 "12" 前),用户在 LTR 子串内读到 "345,12"。v2.72 修复后:0644 0627 064A 0631 0020 0031 0032 002C 0033 0034 0035 0020 0631 0627 0634——"12,345" 完整作为一个 LTR 子串,用户按数值自然顺序阅读。
  • BIDI_Class 表修正(v2.72.0):U+066B ARABIC DECIMAL SEPARATOR 和 U+066C ARABIC THOUSANDS SEPARATOR 现按 Unicode UnicodeData 正确分类为 bcAN(返回值 5),不再分类为 bcAL。这两个分隔符规范上属于 Arabic 数字格式化的一部分,因此在 L2 重排时自然加入 Arabic-Indic 数字 run,不需要 W4 pass。U+066A ARABIC PERCENT SIGN 现归 bcAL(占位;规范的 ET / European Number Terminator 处理推迟到 W5)。
  • 内部重构:_ApplyUAX9L2Reversal 的 level-assignment 步骤现在构建一个显式的 Classes[] 并行数组(每个 UTF-16 code-unit 位置一字节),让 W4 pass 不必重新解码 codepoint 就能 peek 相邻 class。Surrogate-pair codepoint 的 Classes[] 和 Levels[] 字节在 high + low 两半共享。_ApplyUAX9L2ReversalLTRPara 的同类重构被推迟(W4 在 LTR 段无可见影响,因为简化单嵌入级别模型里 EN / AN 在 LTR 段已经在 base level 0)。
  • 不含 W4 分隔符(在两个同类型数字之间)的纯 RTL / LTR / 混合文本字节级与 v2.66 至 v2.71 输出一致。现有 v2.59-v2.71 smoke(RTL Arabic / Hebrew / SMP RTL / NSM positioning / LTR-para reorder / 数字处理 / detector)全部 byte-stable。
  • 剩余 UAX #9 工作(W5 ET 邻接 EN、W6 / W7 cleanup、N0 bracket pair via UAX #9 BD16 stack scan、N1 / N2 简化中性解析、OpenType GSUB Arabic / Syriac contextual joining、文件级 RegisterUnicodeTTF)仍排在后续版本。

2026-05-18 Version 2.71.0

  • 修复 Arabic / Hebrew 文本重排中的 NSM(non-spacing mark,非间距标记)位置 bug。原先 naive 整串反转把 combining mark(Arabic 的 fatha / kasra / damma / shadda 等 harakat;Hebrew 的 sheva / kamatz / patah 等 niqqud;以及 U+0300..U+036F 的 Latin combining diacritic)移到了被 /Tj 反转串里基字符的错误一侧。PDF 阅读器的 combining-mark 渲染器随后把 NSM 附着到了显示顺序中前面的那个字形上——错的 base。肉眼可见的 bug:"ALEF + FATHA + BA"(阿拉伯文)会把 fatha 渲染到 BA 字形上,而不是 ALEF 上。
  • 新增 UAX #9 W1 简化的 NSM 继承规则 + 三种 L2 重排算法(RTL 段 step 2 + step 3,LTR 段单遍)共享的 group-aware 反转通道。反转通道现在把 [base, NSM*] 视为一个单元:当 R→L 遍历时 NSM 被推入栈,遇到下一个非 NSM base 时栈按 logical 顺序 flush 到输出,紧跟在该 base 之后。Surrogate pair 仍跨整个反转通道保持完整,SMP 范围内的 NSM(罕见)也按对称方式处理。
  • 具体修复:"ALEF + FATHA + BA"(logical UTF-16 0623 064E 0628)此前 emit 为 0628 064E 0623(整串反转,fatha 跑到了 BA 和 ALEF 之间)。v2.71.0 NSM-aware 反转后同样输入 emit 为 0628 0623 064E——BA 在左、ALEF 在中、FATHA 紧跟 ALEF 之后,渲染器正确将其附着到 ALEF。
  • 内部 BIDI_Class 表扩展 bcNSM 类(返回值 6)覆盖主要 combining-mark 区段:通用 Combining Diacritical Marks U+0300..U+036F(Latin / 通用 accent)、Hebrew niqqud U+0591..U+05BD 及散落的 U+05BF / U+05C1..U+05C2 / U+05C4..U+05C5 / U+05C7(从 Hebrew R 范围中切出)、Arabic harakat U+064B..U+065F 与 alef khanjareeya U+0670(从 Arabic AL 范围中切出)。NSM 仍在段落方向 P2/P3 第一-strong 扫描中被跳过。
  • 对 LTR 段落,在初始 level 分配后增加一个显式的 W1 NSM-level-继承 pass:每个 NSM codepoint 采用前一个非 NSM codepoint 的 level,让跟在 AL base 后面的 Arabic NSM 加入该 base 的 level-1 run 一起进入单遍 L2 反转。RTL 段落里这个继承隐式发生(NSM 和 R / AL 在简化的单嵌入级别模型里都已经解析到 level 1),不需要单独的 W1 pass。
  • 不含 NSM 的纯 RTL / LTR / 混合输入字节级与 v2.66 / v2.67 / v2.68 / v2.69 / v2.70 输出完全一致(没 NSM 就没行为变化)。现有 v2.59 RTL Arabic / v2.66 LTR-in-RTL / v2.67 LTR-para / v2.68 EN-AN / v2.69 SMP-RTL smoke 全部 byte-stable。
  • 剩余 UAX #9 工作(W3-W7 详细弱类转换、N0-N2 中性 + bracket-pair 解析、OpenType GSUB Arabic / Syriac contextual joining、文件级 RegisterUnicodeTTF)仍排在后续版本。

2026-05-18 Version 2.70.0

  • 新增 THotPDF.RegisterUnicodeFontDict helper,一次调用即可构建完整的 PDF 1.7 ISO 32000-1 9.7 / 9.7.6 Type 0 / CIDFontType2 + Identity-H 复合字体字典。使用 v2.56.0 SetFormUnicodeFontDict AcroForm /AP 路径的调用方不再需要手写 ~30 行 CIDSystemInfo / FontDescriptor / CIDFontType2 / Type 0 / Identity-H 子字典构造代码。返回一个间接 THPDFDictionaryObject,可直接传给 SetFormUnicodeFontDict。
  • API:RegisterUnicodeFontDict(BaseFont, FontBBoxL/B/R/T, Ascent, Descent, CapHeight, ItalicAngle, StemV, Flags, DefaultWidth)。所有 metric 参数都有合理默认值(CJK / Arabic 嵌入字体典型几何:FontBBox [-1000 -300 2000 1300]、Ascent 1000、Descent -300、CapHeight 750、StemV 100、Flags 4 = Symbolic、DW 1000)。Latin / Hebrew / Cyrillic 等字体可单独覆盖任一参数(更小 bbox、Flags = 32 非 symbolic 等)。
  • 构建的字典结构与 v2.59-v2.69 各 smoke_acroform_cid_rtl / smoke_bidi_* 中使用的 spec-correct Type 0 + CIDFontType2 布局完全一致:Adobe / Identity / Supplement 0 的 CIDSystemInfo、含全部 9.8.2 必需项的 FontDescriptor、Identity-H 编码、DescendantFonts 数组中的 CIDFontType2 后代字典、/DW 默认字符宽度。/CIDToGIDMap 在未显式指定时默认为 /Identity(CID == GID),这是 Identity-H + Adobe-Identity-0 ordering 的隐含约定。
  • 后续 v2.71+ 计划新增基于文件的 RegisterUnicodeTTF:解析 .ttf / .otf,提取真实 metric 和 hmtx /W advance 宽度数组实现 perfect-wrap、把字体作为 /FontFile2 嵌入、从 cmap 生成 ToUnicode CMap,内部复用 RegisterUnicodeFontDict 处理字典结构部分。

2026-05-18 Version 2.69.0

  • 扩展内部 Unicode BIDI_Class 查找表,覆盖 SMP(Supplementary Multilingual Plane)右向脚本区段 U+10800..U+10FFF。段落方向检测和 L1-L2 重排现在正确识别 Phoenician(腓尼基)、Imperial Aramaic(帝国阿拉米)、Palmyrene、Nabataean、Hatran、Lydian、Meroitic Hieroglyphs / Cursive、Kharoshthi、Old South Arabian、Old North Arabian、Manichaean、Avestan、Inscriptional Parthian / Pahlavi、Psalter Pahlavi、Old Turkic、Old Hungarian、Hanifi Rohingya(AL,缅甸 Rohingya 语言当今实际使用的脚本)、Garay、Yezidi、Arabic Extended-C(AL)、Old Sogdian、Sogdian、Old Uyghur、Chorasmian、Elymaic 等。
  • SMP RTL 段落现在通过 DetectBidiParagraphDirection 自动识别为 RTL(第一个 strong 字符扫描穿越 surrogate pair 匹配相应的 R / AL 类),并按 v2.66 RTL L1-L2 路径产出视觉顺序的 UTF-16BE Tj 输出。LTR 段中嵌入的 SMP RTL 子串如果设置了 EnableLTRParaReorder(v2.67 opt-in)也会被反转;否则按 logical 顺序输出由消费阅读器的 intrinsic BIDI 处理。
  • Surrogate pair 在所有 L2 反转中持续保对:SMP RTL codepoint 在 /Tj hex 流中保持完整的 high+low 对,同时段落整体正确重排。
  • v2.67 smoke 测试(smoke_bidi_ltr_reorder)断言 #7 原本用 U+10840 IMPERIAL ARAMAIC LETTER ALEPH 演示 "LTR 段中 SMP codepoint 当作 bcOther 处理"。本次发布同步更新该断言以反映现在正确的 bcR 分类:LTR 段中的 U+10840 变成 level-1 R/AL run 的一部分,会与邻近的 Hebrew / Arabic 子串一起被重排到视觉位置。
  • 剩余 UAX #9 工作(W1 NSM 继承 + W3-W7 弱类型转换 + N0-N2 中性 + bracket pair 解析 + I1 显式级别解析 + OpenType GSUB Arabic / Syriac contextual joining)仍排在后续版本。SMP RTL 覆盖落地后,所有标准化 Unicode RTL 脚本现在都能正确分类用于段落方向和基本 L1-L2 重排。

2026-05-18 Version 2.68.0

  • 修复 Arabic / Hebrew 段中嵌入数字的方向显示 bug。新增 Unicode UAX #9 弱类 BIDI_Class:European 数字(EN,U+0030..U+0039)和 Arabic-Indic 数字区段(AN,U+0660..U+0669 + U+06F0..U+06F9)现在被单独识别。简化 UAX #9 I2 + L2 级别分配:RTL 段中这些数字 codepoint 现在取 resolved level 2(与嵌入 LTR 子串相同),而不是和周围 RTL 文字一起被当作 level 1。两遍 L2 重排因此保留数字在视觉 RTL 流里的 logical 顺序,与符合规范的 "شارع 12345" 用户阅读体验一致——即使 PDF /Tj 流是按 LTR 渲染的。
  • 具体修复:输入 "شار 12345"(logical UTF-16 0634 0627 0631 0020 0031 0032 0033 0034 0035)此前 Tj operand 输出为 0035 0034 0033 0032 0031 0020 0631 0627 0634(整串反转——数字被倒置,是肉眼可见的 bug)。v2.68 修复后同一输入 emit 为 0031 0032 0033 0034 0035 0020 0631 0627 0634——数字保持 1-2-3-4-5 reading order,Arabic 单词仍按视觉 RTL 渲染。
  • DetectBidiParagraphDirection 现在按 UAX #9 P2 把 EN 和 AN 视作弱类:纯数字或前导数字的段落不再以数字锚定 RTL,第一个 STRONG 字符(L / R / AL)才决定。空 / 纯弱输入仍按 P3 默认 LTR。Smoke 验证:纯数字 → LTR,前导数字+Arabic → RTL(AL 是第一个 strong),前导数字+Latin → LTR(L 是第一个 strong)。
  • Arabic block U+0600..U+06FF 在 lookup 表中拆开,让两段数字范围作为 bcAN 单独命中;周围的 U+0600..U+065F + U+066A..U+06EF + U+06FA..U+06FF 仍是 bcAL。其它 Arabic block codepoint(标点、百分号、货币等)暂时仍是 bcAL;更细的弱类处理(ET / CS / ON / NSM)等后续 W1-W7 / N0-N2 全管线一起做。
  • 无数字 run 的纯 RTL 输入与 v2.66 / v2.67 字节级一致(不触发任何 W 类转换)。LTR 段不受影响(数字本来就按段落基方向 LTR 渲染)。修复对所有已经使用 AutoDetectFormBidi 或 FormUnicodeRTL 配合 UseUAX9LevelReversal=True(默认)的调用方自动生效。
  • 剩余 UAX #9 + GSUB 工作(W1 NSM 继承 + 完整 W3/W4/W5/W6/W7 + N0-N2 brackets + I1 显式级别 + OpenType GSUB Arabic / Syriac contextual joining + SMP RTL scripts BIDI_Class)仍排在后续版本。

2026-05-18 Version 2.67.0

  • 新增 Unicode UAX #9 L2 重排,应用于 LTR 段中嵌入的 RTL 子串。 新属性 THotPDF.EnableLTRParaReorder(默认 False)让调用方显式选择 spec-strict / reader-independent 的 /AP 输出:LTR 段中嵌入 Hebrew / Arabic 在 /AP 生成时就被反转到视觉顺序, 消费阅读器不必再跑自己的 intrinsic BIDI。
  • 单遍算法,与 v2.66.0 的 RTL 段 L1-L2 对称:LTR 段 base level 0;每个 strong-R / strong-AL codepoint 取 level 1;其它保持 level 0。UAX #9 L2 最低奇数级别 = 1,所以循环只一遍:反转每个最大 level-1 子串(连续 R/AL run)。L 字符和弱 / 中性字符原样拷贝。两遍中 surrogate pair 全程保对。
  • 公开方法 THotPDF.ApplyUAX9L2ReversalLTRPara 将该算法作为独立 helper 暴露(UTF-8 进 UTF-8 出)。与 v2.66.0 ApplyUAX9L2Reversal 区分:后者 ParagraphRTL=False 路径仍保留 v2.66 直通契约(依赖 v2.66 字节级行为的调用方不受影响), LTR 方向重排请改用新方法。
  • EnableLTRParaReorder 默认 False,保留 v2.59-v2.66 字节级 /AP 输出。该 toggle 在 UseUAX9LevelReversal=False 时无效 (v2.59 naive 反转从不处理 LTR 段);其余情况下两 toggle 相互独立。
  • 完整 UAX #9 W1-W7 弱类型转换 + N0-N2 中性 + bracket pair + I1-I2 显式级别 + OpenType GSUB shaping 仍排到 v2.68+。

2026-05-18 Version 2.66.0

  • 新增 Unicode UAX #9 L1-L2 run-aware reversal,应用于 AcroForm Unicode 外观流。新属性 THotPDF.UseUAX9LevelReversal(默认 True)把 v2.59.0 RTL 段落的"整串简单反转"换成符合规范的两遍 L2 重排,保留 RTL 段中嵌入 LTR 子串的内部 logical 顺序。
  • 具体修复:输入 "<Hebrew>AB"(logical UTF-16 05D0 05D0 0041 0042)此前在 Tj operand 中以十六进制 0042 0041 05D0 05D0 ("BAאא") 输出——把 "AB" 倒着写,按 UAX #9 是错的。L1-L2 启用后同一输入 emit 为 0041 0042 05D0 05D0 ("ABאא")。纯 RTL 输入且无 L runs (例如 v2.59.0 预成形 Arabic smoke) 与传统简单反转字节一致。
  • 公开方法 THotPDF.ApplyUAX9L2Reversal 把新算法作为独立 helper 暴露(UTF-8 进 UTF-8 出),方便调用方自建外观流或在 /AP 生成前预览重排结果。
  • 本次刀使用 UAX #9 I1 + L2 的简化单嵌入级子集:RTL 段落里每个 strong-L codepoint 取得 level 2;其它一切(R、AL、弱、中性、 surrogate)保持 level 1。L2 先反转每个 level-2 run(恢复 LTR 子串的 logical 顺序)再反转整个 level-1 全段(产生视觉 RTL 排版)。Surrogate pair 在两遍中均保持完整。
  • 完整 UAX #9 W1-W7(弱类型转换)+ N0-N2(中性 + bracket pair)+ I1-I2(显式级别解析)+ LTR 段嵌入 RTL run 的重排仍排到 v2.67+。v2.66.0 解决最常见的可观察 bug:含多字符 LTR 子串的 RTL 段落。
  • UseUAX9LevelReversal 默认 True。设 False 可恢复 v2.59.0 朴素整串反转,获得字节级与旧版一致的输出。两种形式仍按相同方式读 FormUnicodeRTL 与 AutoDetectFormBidi。

2026-05-18 Version 2.65.0

  • 新增 Unicode 标准附件 #9 段落方向自动检测,应用于 AcroForm Unicode AP 外观流。新属性 THotPDF.AutoDetectFormBidi 把 BuildUnicode*FieldContent 三个 helper(单行、多行、分格) 从 v2.59.0 的手动 FormUnicodeRTL 切换为按调用自动检测段落方向。设置一次属性即可让同一批 setter 序列处理同一文档里 LTR + RTL 混合字段,无需手动切换标志。
  • 方向由 UAX #9 P2 + P3 规则解析:扫描输入找第一个强字符 (BIDI_Class L / R / AL)。R 或 AL 锚定段落为 RTL;L 锚定 LTR;无强字符按 P3 默认 LTR。
  • 识别的 BIDI_Class: Strong L ASCII Latin、Latin-1、Latin Extended、Greek、 Coptic、Cyrillic、Armenian、CJK 汉字、Hangul、 Hiragana、Katakana、Yi Strong R Hebrew (U+0590..U+05FF)、Hebrew presentation forms (U+FB1D..U+FB4F)、N'Ko、Samaritan、Mandaic Strong AL Arabic、Arabic Supplement、Arabic Extended-A、 Syriac、Thaana、Arabic presentation forms A + B (U+FB50..U+FDFF + U+FE70..U+FEFF) Surrogate pair 和弱 / 中性字符(数字、标点、空白)在扫描中跳过。
  • 公开方法 THotPDF.DetectBidiParagraphDirection 暴露检测器, 调用方可预先计算方向(用于 /Lang 标签、对称中性字符输出、 自定义字段路由等)。接收 UTF-8 AnsiString,返回 Boolean (True = RTL)。空输入按 P3 返回 False。
  • AutoDetectFormBidi 默认 False,保持 v2.59.0-v2.64.0 的字节级行为完全一致。设为 True 时,三个 helper 按调用忽略 FormUnicodeRTL。设回 False 即恢复手动控制。
  • 完整 UAX #9 隐式级 + 弱类型 + 中性括号解析(规则 W1-W7、 N0-N2、I1-I2、L1-L4)和自动 Arabic / Syriac OpenType GSUB contextual joining 仍排到 v2.66+。混合 bidi 段落超出段落级方向部分仍需调用方手动介入。

2026-05-18 Version 2.64.0

  • 新增 PDF 1.2 ISO 32000-1 10.5.5 ExtGState 半色调屏蔽(/HT)项支持,以及 Type 1 spot-function 半色调字典 builder。新方法 THotPDF.RegisterHalftoneType1 构建 PDF 1.7 Table 76 Type 1 半色调字典,调用方可控 CMYK 分色屏蔽(频率 lpi、屏幕角度、 spot 形状、可选的精确屏蔽、转换函数、描述性半色调名)。配合 THotPDF.RegisterHalftoneState 把该字典引用包成 ExtGState /HT 项,供 THPDFPage.SetGraphicsState 使用。
  • /SpotFunction 可以是 PDF 1.7 Table 78 的 21 个内置 spot 名(SimpleDot、InvertedSimpleDot、DoubleDot、 InvertedDoubleDot、CosineDot、Double、InvertedDouble、Line、 LineX、LineY、Round、Ellipse、EllipseA、InvertedEllipseA、 EllipseB、EllipseC、InvertedEllipseC、Square、Cross、 Rhomboid、DiamondRhomboid),也可以是调用方自建的 spot Function 字典引用。未知的内置名抛异常,并在消息里列出完整 spec 列表。Spot 名和 Function 互斥。
  • /TransferFunction 可选。传 Function 字典引用做调用方自控色调再现,或设 TransferFunctionDefault = True emit /TransferFunction /Identity(消费阅读器默认)。两种形式互斥。
  • RegisterHalftoneState 接受半色调字典引用(典型为 RegisterHalftoneType1 返回值)或 HTDefaultName = True 走 /HT /Default 名字字面量复位路径,恢复消费阅读器内置半色调。
  • 所有项写入时自动把文档版本提到 PDF 1.2。Frequency 必须 > 0; 无效的 spot-function 名在字典分配前抛异常。
  • 剩余半色调类型排到 v2.65+:Type 5 多 component、Type 6 / 10 / 16 threshold-stream 半色调。Type 1 落地后,矩阵 "ExtGState entries" 候选距完整覆盖只剩一刀。

2026-05-18 Version 2.63.0

  • 新增 PDF 1.4 ISO 32000-1 11.6.5 + 11.7.4 ExtGState 软掩膜 + alpha-is-shape 项支持。新方法 THotPDF.RegisterSoftMaskState 把 /SMask 和 /AIS 两个透明度图形状态参数打包进一个 ExtGState 字典,至此 v2.20.0 + v2.60.0 + v2.61.0 + v2.62.0 + v2.63.0 覆盖 ExtGState 全部 spec 定义条目,仅余多类型 /HT halftone 屏蔽(推到 v2.64+)。
  • /SMask 可以是字面名 /None(调用方传 SMaskNone = True, 常见于复杂合成后的复位模式),也可以是间接 soft-mask 字典引用(调用方传 SMaskDict,进阶用法)。两种形式互斥。 soft-mask 字典内容和底层透明度 Form XObject 由调用方自行构造;v2.64+ 会加专门 builder 助手。
  • /AIS 控制当前 alpha 值在图形状态后续部分被解释为 alpha (false) 还是 shape (true)。哨兵默认 AIS = -1 完全跳过 /AIS 项;AIS = 0 或 1 分别 emit /AIS false / /AIS true,其它值抛带说明的异常。
  • /SMask(None 或 Dict)或 /AIS 至少要供一项;全为默认值的调用会抛异常。任何项写入时自动把文档版本提到 PDF 1.4。
  • 返回自动生成的 ExtGState 名(GS1、GS2…)交给 THPDFPage.SetGraphicsState 使用。和 v2.42.0 AddImageWithSMask 区分:那条路径是单个 XObject 上的图像级 alpha;本方法是 ExtGState 级遮罩,对所有后续绘图操作生效。
  • 剩余 ExtGState 项排到 v2.64+:/HT halftone 屏蔽 (Type 1 / 5 / 6 / 10 / 16)。

2026-05-18 Version 2.62.0

  • 新增 PDF 1.2/1.3+ ISO 32000-1 11.7.5.4 ExtGState 黑色生成 + 底色去除项支持。新方法 THotPDF.RegisterBlackGenerationState 把 4 个 Function 引用的图形状态参数 /BG、/BG2、/UCR、/UCR2 打包进一个 ExtGState 字典,适用于在光栅化时做 RGB→CMYK 转换的 CMYK 印前流程(典型场景:报纸 / 包装 / 大幅面生产, 油墨投放决策与输出设备相关)。
  • /BG 和 /UCR(PDF 1.2)是 1 输入 / 1 输出 Function 字典; /BG2 和 /UCR2(PDF 1.3+)可以是 Function 字典,也可以是字面名 /Default 让消费阅读器使用其默认曲线。setter 每个 Function 槽接受一个 THPDFObject(典型由 v2.52.0 RegisterSampledFunction 生成),加 BG2DefaultName / UCR2DefaultName 两个布尔标志走 /Default 名字面形式。同一个 /BG2 或 /UCR2 槽里混 Function 字典和 Default 标志会抛带说明的异常。
  • /BG、/BG2、/UCR、/UCR2(Function 或 Default)至少要供一项; 全为默认值的调用会抛异常。任何 /BG2 / /UCR2(Function 或 Default)写入时自动把文档版本提到 PDF 1.3。
  • 返回自动生成的 ExtGState 名(GS1、GS2…)交给 THPDFPage.SetGraphicsState 使用。可与 v2.60.0 RegisterTransferFunctionState(按通道输出曲线)自然搭配—— 典型 CMYK 生产链注册一个 BG/UCR ExtGState 做 RGB→CMYK 转换, 再注册一个 /TR ExtGState 做输出设备上的最终色调再现。
  • 剩余 ExtGState entries 排到 v2.63+:/HT halftone 屏蔽 (Type 1 / 5 / 6 / 10 / 16) + ExtGState 级 /SMask + /AIS soft mask。

2026-05-17 Version 2.61.0

  • 新增 PDF 1.2/1.3+ ISO 32000-1 8.6.5.7 + 8.6.5.8 + 11.7.4 ExtGState 印前控制项支持。新方法 THotPDF.RegisterPrintControlState 把面向印前的图形状态参数 /OP、/op、/OPM、/SA、/RI、/FL 打包进一个 ExtGState 字典, 适用于需要 overprint、stroke adjustment、rendering intent、 flatness 控制的 PDF/X-1/X-3/X-4 生产工作流。
  • 6 个参数各自独立可选。默认哨兵值:Overprint = -1(跳过 /OP + /op)、OverprintMode = -1(跳过 /OPM)、StrokeAdjustment = -1(跳过 /SA)、RenderingIntent = ''(跳过 /RI)、Flatness < 0(跳过 /FL)。调用方至少要传一项;全部默认时抛异常。
  • Overprint 同时设 /OP 和 /op(PDF spec 默认 /op 继承 /OP 但 spec-strict PDF/X 流水线要求两项都显式)。OverprintMode = 1 启用 PDF 1.3 CMYK-aware overprint(任意零分量绘成透明);自动把文档版本提到 1.3。
  • RenderingIntent 接受四种标准值 'AbsoluteColorimetric' / 'RelativeColorimetric' / 'Saturation' / 'Perceptual';其它非空字符串抛异常。Flatness 限定在 spec 范围 [0.0..100.0], 超出抛异常。
  • 返回自动生成的 ExtGState 名(GS1、GS2…)交给 THPDFPage.SetGraphicsState 使用。可与 v2.20.0 RegisterExtGState (透明度 / blend mode)以及 v2.60.0 RegisterTransferFunctionState (输出曲线)自然搭配——典型 PDF/X 流水线每种链一个状态。
  • Function 驱动的 /BG、/UCR、/BG2、/UCR2(black generation + undercolor removal)以及更复杂的 /HT halftone screen + ExtGState 级 /SMask soft mask 留作后续工作。

2026-05-17 Version 2.60.0

  • 新增 PDF 1.3+ ISO 32000-1 11.7.3.4 ExtGState /TR(transfer function)支持。新方法 THotPDF.RegisterTransferFunctionState 注册一个携带 /TR 项的 ExtGState 字典,在颜色光栅化时对输出应用色调再现曲线。曲线由调用方用 v2.52.0 Function Type 0 Sampled LUT(或任何其它 Function 字典)提供;本 wrapper 和已存在的 RegisterSampledFunction 原语天然配套,让手工调的 gamma / 亮度 / 印刷特性曲线落地,不必走 ICC profile 链路。
  • 两种调用形式:
  • SingleFunc 形式:一个 Function 应用到所有输出通道 (gamma / 亮度场景)。
  • PerChannel 数组形式:4 元素数组 [TR_R, TR_G, TR_B, TR_A] 对应 RGB-A 通道,或 [TR_C, TR_M, TR_Y, TR_K] 对应 CMYK 再现曲线;按 PDF spec 4 个槽位都必须非 nil。
  • 返回自动生成的 ExtGState 名称(GS1、GS2…),交给 THPDFPage.SetGraphicsState 使用。在需要应用 transfer 曲线的绘制操作(文字 / 路径 / 图像)之前 set 该状态。
  • 混合调用(SingleFunc 非 nil + PerChannel 非空)会抛异常, spec 强制的互斥关系在 API 表面就拦截。

2026-05-17 Version 2.59.0

  • 新增 AcroForm Unicode /AP 的右到左方向支持(PDF 1.7 ISO 32000-1 12.7.4.3)。新的 THotPDF.FormUnicodeRTL 布尔属性:开启后三个 Unicode /AP helper(single-line、multi-line、comb)在发出 hex Tj 操作数前反转 UTF-16 码元顺序,让预先字形整形过的阿拉伯文 / 希伯来文 / 叙利亚文按视觉右到左顺序渲染。UTF-16 代理对在反转时整体保留(高代理紧跟低代理的原始顺序),SMP 码位(古文字 letterform 等)也能正确显示。
  • 作用范围:v2.59.0 只做方向反转这一步。调用方负责另两件 v2.59.0 不做的事情:(1)完整 UAX #9 双向算法消解(混合 LTR / RTL / 数字 / 中性字符)和(2)阿拉伯 / 叙利亚字形 contextual joining (Initial / Medial / Final / Isolated 四形态选择)。调用方应在传入 AddTextField 前把阿拉伯文预先映射到 Unicode 表示形态区段(U+FB50..U+FDFF + U+FE70..U+FEFF)。希伯来文没有 contextual joining,所以不需要预映射。
  • /V(逻辑 Unicode 顺序)不变:PDF text string 永远是输入顺序, spec-following 文本提取器看到正确的逻辑序列。只有 /AP Tj 操作数翻转顺序,渲染器画时视觉为 RTL。本次发布之后 v2.55.0-v2.59.0 AcroForm 国际化 /AP 链完整闭环:/RV 富文本、单行/多行/comb CID 字体 /AP、以及 RTL 方向反转。完整 UAX #9 + 自动 GSUB 字形整形仍是 v2.60.0+ scope。

2026-05-17 Version 2.58.0

  • 把 v2.56.0 / v2.57.0 的调用方自带 Unicode 字体 /AP 链扩展到 ffComb 分支(PDF 1.7 ISO 32000-1 12.7.4.3 Tx /Ff 第 25 位)。 当 SetFormUnicodeFontDict 已注册 Type 0 复合字体、且 Tx widget 加上 ffComb + 非 ASCII 初值时,HotPDF 现在写出真正的 /AP /N 外观流:每个 CJK 字符放进一个等宽 comb 格,每格用一个绝对 Tm 矩阵定位 + 一个 UTF-16BE hex-string Tj 操作符。
  • UTF-16 代理对(码位 >= U+10000,例如 emoji 和 CJK 扩展 B/C/D 超平面汉字)占据一个 comb 格,与 Acrobat 处理复合字体单字形 comb 的方式一致。每格的居中公式与 v2.46.0 ASCII comb 分支完全相同,视觉布局沿用 ASCII 单字符布局。
  • ASCII comb 字段与 v2.46.0 输出字节级相同。非 ASCII comb 但没注册 SetFormUnicodeFontDict 时仍走 v2.46.0 空 /AP 占位 (不变)。本次发布之后 v2.55.x–v2.58.0 AcroForm 国际化 /AP 链(单行、多行、comb)完整闭环;RTL bidi 整形(UAX #9 + 阿拉伯 contextual joining)作为 v2.59.0+ scope。

2026-05-17 Version 2.57.0

  • 把 v2.56.0 的调用方自带 Unicode 字体 /AP 路径扩展到多行文本字段(PDF 1.7 ISO 32000-1 12.7.4.3 ffMultiline / Tx /Ff 第 13 位)。当 SetFormUnicodeFontDict 已注册 Type 0 复合字体、且 Tx widget 加上 ffMultiline + 非 ASCII 初值时,HotPDF 现在按 UTF-16 字符做 word-wrap,并对每条可见行写一个 UTF-16BE hex-string Tj,通过 Td/T* + /TL leading 布局(与 v2.46.0 ASCII 多行排版方式相同)。
  • 换行宽度按简单 advance 估算:CJK / 宽字符(≥ U+2E80)每个 1 em, 窄字符(ASCII 范围 + Latin-1 标点)每个 0.5 em。这样 Latin / CJK 混排不用读字体 /W 数组也能合理折行。CR / LF / CRLF 硬换行按字面处理。超出 widget 矩形的行按字号能容纳的行数截断 —— 与 ASCII 多行分支一致。
  • ASCII 多行、ASCII 单行、ASCII comb 以及 v2.56.0 非 ASCII 单行路径全部字节级不变。非 ASCII comb 路径(ffComb + 多字节文本) 目前仍走 v2.56.0 单行 helper;ffComb 专属的 Unicode 布局留作 v2.58.0+,RTL bidi(UAX #9 + 阿拉伯 contextual joining)也是 v2.58.0+ scope。

2026-05-17 Version 2.56.0

  • 新增调用方自带 Unicode 字体的 AcroForm /AP 渲染支持(PDF 1.7 ISO 32000-1 12.7.2 + 12.7.4.3)。本次发布前,GenerateTextFieldAP 遇到任何含 ≥0x80 字节的 InitialValue 都只能写一个空的 /AP 占位 Form XObject,非 ASCII 文本只能靠 /NeedAppearances=true 让阅读器重生成(依赖阅读器自带字体),或者通过 v2.55.0 的 /RV 富文本路径 (依赖阅读器富文本引擎)。v2.56.0 把这个缺口补上:调用方注册一个 Type 0 / CIDFontType2 + Identity-H 复合字典之后,HotPDF 写出真正的 /AP /N 流,里面通过 Tf 选用注册字体、用 UTF-16BE 十六进制字符串 Tj 操作符渲染值。
  • 新方法 THotPDF.SetFormUnicodeFontDict(LogicalName, FontDict): 注册一个逻辑名 + 字体字典对。注册之后,AcroForm 顶层 /DA、每个 Tx widget 的 /DA、以及非 ASCII Tx 初始值的 /AP 流都切到逻辑名; AcroForm 的 /DR/Font 字典除了 /Helv 和 /ZaDb 也带上该字体;Form XObject 的 /Resources/Font 子字典也引用同一个 indirect 字体,让 /AP 自包含,不依赖渲染时去 /DR 查表。传 '' + nil 撤销注册,恢复 v2.46.0 的 /Helv-only 行为。
  • 新方法 THotPDF.CreateIndirectFontDict:公开 helper,分配一个空的 THPDFDictionaryObject 并注册为 indirect PDF 对象,让调用方能构造任何需要序列化成 "N G R" indirect 引用的字典(custom 字体、 色彩空间、OCG、Function 等)。和 SetFormUnicodeFontDict 配套用来搭 Type 0 复合字体最自然。
  • 纯 ASCII Tx 字段继续用 /Helv,/AP 输出与 v2.46.0 / v2.55.0 字节完全相同。多字节 Tx 但**没**注册 SetFormUnicodeFontDict 时仍走 v2.46.0 的空 /AP 占位(语义不变,保后向兼容)。
  • 多行 + 分格的非 ASCII /AP 覆盖、RTL bidi 整形(UAX #9 + 阿拉伯 contextual joining)仍是 v2.57.0+ 的工作;v2.56.0 覆盖单行逻辑序的 CJK / 西里尔 / 越南语 / 希腊语等脚本。

2026-05-17 Version 2.55.0

  • 新增 AcroForm 富文本 Tx widget 支持(PDF 1.7 ISO 32000-1 12.7.4.3 + Annex L)。新方法 THPDFPage.AddRichTextField 写出带 /RV(rich-text XHTML 主体)、/DS(CSS 风格默认样式)、 ffRichText(/Ff 第 26 位)以及 /V + /DV 纯文本回退的文本 widget。 当 RichText 位打开时,Acrobat / Foxit 直接根据 /RV 渲染,从阅读器自身安装的 Unicode 字体里取字形回退 —— 这样 HotPDF 不必在 /DR Resources 里嵌入 CID 字体也能让多字节内容(CJK / 西里尔 / 阿拉伯 / 带音标拉丁)正确显示。让 HotPDF 自己的外观流(/AP) 生成器原生支持非 ASCII 仍是后续工作;懂富文本的阅读器无需这步。
  • /V 和 /RV 都自动检测多字节输入,需要时切换成 UTF-16BE 十六进制字符串(FE FF BOM + 大端码元),与 AddTextField 处理国际化纯文本字段一致。ASCII 内容保持 PDF 文字字面量字符串,与 v2.46.0 纯文本输出字节完全相同。
  • AcroForm 国际字符候选剩三项后续:(1)CID 字体 /DR Resources 路径,让 HotPDF 生成的 /AP 流自己覆盖非 ASCII,而非依赖阅读器富文本引擎;(2)阿拉伯 / 希伯来富文本的 RTL bidi 整形; (3)/AA /K 键盘动作 helper。

2026-05-17 Version 2.54.0

  • 扩展 PDF 1.7 ISO 32000-1 7.10.2 Function Type 0(Sampled)支持 cubic-spline 插值。RegisterSampledFunction 新增可选 Order 参数 (默认 1,按 PDF 1.7 Table 38 仅接受 1 或 3)。Order = 3 在每个输入轴上使用三次样条插值;Order = 1 保留现有的线性行为,字节完全相同,v2.52.0 / v2.53.0 调用方零感知。
  • 新增多输入 / 多输出 Function Type 0 端到端覆盖。新增的 smoke 注册一个 2 输入 / 3 输出、4×4 网格、cubic 插值的 sampled 函数, 按 spec sample 顺序规则("input 坐标沿第一个输入维变化最快") 逐字节核对所有 16 网格点 × 3 通道,对照值由 Python verifier 独立按 analytic 公式重新计算。
  • Acrobat / Foxit / qpdf 都支持 Order = 3;部分老牌移动端阅读器遇到不认识的 Order 值会悄悄降级为线性插值,所以 cubic 仍然保留为 opt-in,以维持最大化阅读器兼容性。

2026-05-17 Version 2.53.0

  • 新增 Separation 色彩空间的高级注册接口,tint transform 用 Function Type 0(LUT)实现(PDF 1.7 ISO 32000-1 8.6.6.4 + 7.10.2)。新方法 THotPDF.RegisterSeparationLUT(ColorantName, AlternateCS, Samples) 直接复用 v2.52.0 的 RegisterSampledFunction 原语,调用方只需提交按 (M 元组) 排列的扁平字节流,不必手工构造 Function Type 0 字典。
  • LUT 变体适合非线性色彩过渡场景:PANTONE Hexachrome 风格的 tint ramp、gamma 校正的密度曲线、按印刷特性表手绘的色调曲线、把 sRGB→spot 色彩转换塞进 ICC LUT 但不愿带完整 ICC profile 链路等。线性 ramp(最常见的单油墨 PANTONE spot color)仍可继续用 v2.45.0 的 RegisterSeparation。
  • Samples 是 8-bit RGB / CMYK / Gray 字节,按 (c0_0, c1_0, ..., cM-1_0, c0_1, ..., cM-1_S-1) 顺序排列,S = Length(Samples) / M 自动推导,且 S ≥ 2(保证函数可插值)。线性插值是默认行为 (PDF 1.7 /Order 1)。返回 Sep1 / Sep2 / … 这样的色彩空间名, 可直接用 THPDFPage.SetFillColorSpace / SetStrokeColorSpace + SetFillColor([tint]),tint ∈ [0, 1]。
  • 内部把 THPDFSeparationParams.TintFunc 从 THPDFDictionaryObject 扩到 THPDFObject 公共基类,可以同时容纳 Function Type 2 (v2.45.0 路径)和 Function Type 0 stream(v2.53.0 路径);两者在 THPDFArrayObject.AddObject 里都序列化成 "N G R" indirect 引用,对下游解析透明。

2026-05-17 Version 2.52.0

  • 新增 PDF 1.7 ISO 32000-1 7.10.2 Function Type 0(Sampled)注册支持。Sampled function 以规则的 N 维网格 + M 维输出样本表表达任意 input→output 映射;阅读器在网格点之间线性插值得到中间值。 适合那些用 ICC profile 太重、但又想精细控制的色彩 LUT 场景 —— ExtGState `/TransferFunction` 色调曲线、`/Separation` / `/DeviceN` 的 sampled tint transform(区别于已支持的线性 Type 2 / 算式 Type 4 路径)、halftone 阈值曲线,以及任何接受 Function 字典的 PDF 结构。
  • 新方法 THotPDF.RegisterSampledFunction(Domain, Range, Size, BitsPerSample, Samples) 写出一个 indirect `/FunctionType 0` stream,字典包含 `/Domain`(2N)、`/Range`(2M)、`/Size`(N)、 `/BitsPerSample`(1 / 2 / 4 / 8 / 12 / 16 / 24 / 32)、 `/Order 1`(线性插值),加上原始按位打包的样本载荷。返回 indirect `THPDFStreamObject`,调用方可以把它接到任何接受 Function 字典的位置。
  • Samples 字节数组长度按 `ceil(GridPoints * M * BitsPerSample / 8)` 校验; `BitsPerSample` 8 / 16 / 24 / 32 是字节对齐大端, 1 / 2 / 4 / 12 按 PDF 1.7 7.10.2 的 MSB-first 打包规则, 最末字节按需补零。
  • Function Type 3(stitching)/ Type 4(PostScript calculator) 已在 v2.18.0 / v2.47.0 起通过内部 helpers 可用;本次发布把 Type 0 提升到同样的公开 API 级别。

2026-05-17 Version 2.51.6

  • 收尾 v2.51.x 二进制安全 sweep:修复最后一处 TStringList "Source=Target" 往返点 —— FStructRoleMap(PDF 1.7 ISO 32000-1 14.7.3 /StructTreeRoot /RoleMap)。通过 AddStructRoleMap 注册的自定义结构 role 名在 Windows CP_ACP=65001("Beta: Use Unicode UTF-8")系统上不再被悄悄破坏;存储改为 array of (Source: TBytes; Target: TBytes) 记录的动态数组, 所有插入/读取访问都用字节级 Move() 保真。标准 PDF struct types (Document、P、H1..H6、Span、Div 等)按 spec 都是 ASCII,所以之前的版本在常规用例下能跑;本次修复只影响把工具特定的 Latin-1 role 名映射到标准类型的调用者。
  • 把 PDF Name #XX 转义(PDF 1.7 7.3.5)扩展到 SaveDictionaryObject 的字典 KEY。之前字典键直接原样写入输出流,任何 ≥ 0x80 的字节都不会被转义,生成 spec 不合规的输出 —— 通过 AddStructRoleMap 传入非 ASCII source role 即触发。字节级 trim + #XX 转义逻辑现在抽到共享 helper(_EscapePDFNameBytes),由 SaveNameObject (name 值)和 SaveDictionaryObject(字典键)共同调用。ASCII 键(HotPDF 自动生成字典的通用情况)转义后字节完全相同, 所以现有文件和测试不受影响。
  • 本次发布之后 v2.51.x 二进制安全链(PubSec 接收者、DeviceN ColorantNames、PDF Name 值输出、PDF Name 键输出、struct role map 存储)结构性闭环 —— HotPDF 已知任何字节 blob 路径都不再存在 AnsiString-via-UnicodeString round-trip。34/34 smoke + 27/27 verifier 全部通过。

2026-05-17 Version 2.51.5

  • 延续 v2.51.4 的二进制安全 sweep,把 Windows CP_ACP=65001 ("Beta: Use Unicode UTF-8 for worldwide language support")系统下另外两条会悄悄破坏非 ASCII 字节的发送路径一并修好。
  • DeviceN 油墨名(ColorantNames)现在以 THPDFDeviceNParams 内的 array of TBytes 动态数组存储(之前是 TStringList 的 UnicodeString 条目)。旧实现强制每个油墨名经过 AnsiString → UnicodeString → AnsiString 往返,UTF-8 ANSI 主机会把 ≥ 0x80 的字节改写成 3 字节替换序列 EF BF BD。纯 ASCII 油墨名 (PANTONE / Hexachrome 以及 "Red" / "Blue" 之类)不受影响; 自定义非 ASCII 油墨名(例如非英语印刷流程里的 Latin-1 重音 spot-ink 名)则在本次发布前悄悄被破坏。
  • PDF Name 输出路径(SaveNameObject)现在二进制安全且符合 spec。 旧实现 AnsiString(Trim(String(ValObject.Value))) 对每个 PDF Name 都跑同样的破坏性往返。现在按字节比较裁剪("char ≤ 0x20" 与 Delphi 的 Trim 语义一致),并按 PDF 1.7 ISO 32000-1 7.3.5 把任何超出 0x21..0x7E 常规字符范围的字节用 #XX 转义写出。 纯 ASCII 且无前后空白、无嵌入特殊字节的 Name 与之前字节完全一致, 所以现有文件和测试都不受影响。
  • 新增 round-trip 验证(smoke_devicen_binary_safe)注册一个名字里含 0xA0 字节的 DeviceN 油墨,并断言生成的 /Names 数组带 spec 规定的 "#A0" 转义、而不是会出错的 "#EF#BF#BD" 序列。本次发布之后 v2.51.x 二进制安全链(PubSec 接收者、DeviceN 油墨名、 通用 PDF Name 输出)正式闭环;33/33 smoke + 26/26 verifier 全部通过。

2026-05-17 Version 2.51.4

  • 修复 Public-Key Security Handler(PDF 1.7 ISO 32000-1 7.6.5) 中接收者信封字节悄无声息被破坏的问题。当宿主系统启用 Windows "Beta: Use Unicode UTF-8 for worldwide language support" (CP_ACP=65001)时,先前基于 TStringList 存储 PKCS#7 envelopedData blob 的方案会强制经过 AnsiString → UnicodeString → AnsiString 往返,把每个非 ASCII 字节改写成 UTF-8 替换序列(EF BF BD)。这种破坏同样作用于算法 9 的 SHA-1 文件密钥计算 和 写出的 /Recipients 十六进制字符串,所以生成的 PDF 用 HotPDF 自己(在同一 CP_ACP 锁定系统上)能解密,但任何第三方阅读器都无法从原始信封字节推导出相同的密钥 —— 加密文件实际只能由 HotPDF 本身在同一系统上读取。
  • 修复方案把接收者存储改为 array of TBytes 动态数组; AddPubKeyRecipient 公开 API 签名不变。信封字节经 Move() 逐字节拷贝,不再有任何代码页转换路径。
  • /Recipients 十六进制现在与传入的信封字节完全一致,不受宿主 locale 影响;Acrobat / Foxit / qpdf 以及任何懂 PKCS#7 的阅读器都能按 spec 还原文件加密密钥。
  • v2.33.0 起的 round-trip 验证脚本 smoke_pubsec_verify 因此而失败已久,v2.51.4 把它从 FAIL 变为 OK。本次发布之后整条 v2.51.x audit 链(R=5 audit、R=6 user-pw audit、R=6 owner-pw K0 修复、PubSec 接收者字节修复)完整闭环,25/25 smoke verifier 全部清干净 —— 自 v2.33.0 以来第一次。

2026-05-16 Version 2.51.3

  • 修复 AES-256 V=5 R=6 所有者口令验证哈希以符合 ISO 32000-2 算法 2.B 字面规定。PDF 2.0 hash dance 要求初始 K 取 SHA-256(password || salt || udata),其中所有者口令路径的 udata 是完整的 48 字节 /U 项。之前的 HotPDF 版本(v2.22.0 至 v2.51.2)只把 udata 放进迭代 K1 步而漏掉初始 K,得到的 /O[0:32] 不合 spec,Acrobat / Foxit / qpdf 类阅读器会在所有者口令验证时拒绝。用户口令解密路径不受影响 —— 因为它的 hash dance 是用空 udata 调用的,初始 K 在修复前后字节相同。
  • 修复后 Acrobat / Foxit / qpdf 可以用 HotPDF 写出的 R=6 文件完成所有者口令验证。v2.51.3 之前生成的 R=6 文件用用户口令解密仍然可读,但所有者口令不能被 spec-following reader 解锁; 如需被外部阅读器验证所有者口令,请用 v2.51.3 重新生成。
  • /U / /UE / /Perms 以及 V=5 R=5 和 V=4 路径与 v2.51.3 前的输出完全相同 —— 修复只动 PDF20HashDanceR6 的初始 K SHA-256, 且只在 UEntry 非空(即 CreateKeysV5 owner-pw 调用点)时改变输出。
  • 新增 round-trip audit(smoke_aes256_r6_owner_verify)通过修复前和 spec 两种算法各自推导 /O[0:32],断言 emitted /O[0:32] 与 spec 变体一致、且修复前的变体已不再被产出。

2026-05-16 Version 2.51.2

  • 将 v2.51.1 的 /Perms audit 扩展到 V=5 R=6("hash dance")标准安全处理程序路径(ISO 32000-2 7.6.4.3.4 / 算法 2.B)。新增的往返验证脚本用纯 Python 重新实现迭代式 SHA-256 / SHA-384 / SHA-512 + AES-128-CBC 混合器,独立从空用户口令派生 32 字节文件加密密钥,再 AES-256-ECB 解密 16 字节 /Perms 块,并逐项断言每个字段与加密字典里的值一致(/P 小端、0xFF 填充、'T' / 'F' 标志位、'a' 'd' 'b' 字面标记)。本次发布行为不变,纯为 v2.51.1 已锁定的 R=5 路径之外补齐 R=6 路径的回归覆盖。

2026-05-16 Version 2.51.1

  • 在回归测试套件中锁定 V=5 R=5 标准安全处理程序的 /P → /Perms 绑定。AES-256 加密路径按 ISO 32000-1 Algorithm 10 写出 16 字节 /Perms 块:字节 0-3 是 /P 的 32-bit 小端整数、字节 4-7 是 0xFF 填充、字节 8 是 'T' / 'F' 跟随 /EncryptMetadata、字节 9-11 是字面 'a' 'd' 'b' 标志、字节 12-15 是随机填充。新增的 audit smoke 用算法 2.A(SHA-256(pw || U_Key_Salt) → AES-CBC 解密 /UE → 取出文件密钥)独立派生密钥、AES-256-ECB 解密 /Perms、再逐字段对照加密字典里的值。本次发布行为不变, 纯为防止日后重构悄悄打破 spec 强制的 /P ↔ /Perms 联动。

2026-05-16 Version 2.51.0

  • 新增 PDF 1.3+ ISO 32000-1 8.7.4.5.7 tensor product patch 网格阴影 (Shading Type 7),网格阴影家族最后一项(前三项是 v2.48 Type 4 自由形式 / v2.49 Type 5 lattice / v2.50 Type 6 Coons)。每个 tensor-product patch 携带完整的 4×4 双三次贝塞尔控制点网格 p[i][j] (12 个边界点 + 4 个内部点),加每角一个颜色,可以同时精细控制 patch 边界与内部曲面。适合 SVG mesh-gradient 完整往返、以及任何需要 tensor-product Bezier 曲面(而非 Coons 拼接曲面)的矢量插画。
  • 新方法 THotPDF.RegisterTensorProductPatchMesh(XMin, YMin, XMax, YMax, NumComponents, Patches) 打包二进制 Shading 流 (/ShadingType 7 + /BitsPerCoordinate 16 + /BitsPerComponent 8 + /BitsPerFlag 8),外包成 Pattern Type 2,返回 Pattern 名 (Sh1、Sh2、…),用法与前三项完全一致。
  • Patches 平铺数组每个 patch 由 16 个控制点(32 个 X+Y 浮点,按 spec stream 序:p[0][0..3]、p[1][3]、p[2][3]、p[3][3]、p[3][2..0]、 p[2][0]、p[1][0]、p[1][1]、p[1][2]、p[2][2]、p[2][1])+ 4 个角颜色(p[0][0] / p[0][3] / p[3][3] / p[3][0],每个 NumComponents 浮点)组成,stride = 32 + 4 × NumComponents。所有 patch flag = 0。
  • 本次发布之后,整个 ISO 32000-1 8.7.4.5 mesh shading 家族 (Types 4 / 5 / 6 / 7)已全部覆盖。

2026-05-16 Version 2.50.0

  • 新增 PDF 1.3+ ISO 32000-1 8.7.4.5.6 Coons patch 网格阴影 (Shading Type 6),是网格阴影家族第三项(前两项是 v2.48 Type 4 自由形式与 v2.49 Type 5 lattice)。每个 Coons patch 的四条边都是三次贝塞尔曲线、每个角都带一个颜色;阅读器在四条边内拟合 Coons 曲面,得到一块带任意曲边的彩色四边形。适合贴在弯曲路径上的金属 / 箔渐变、SVG gradient mesh 导入、以及任何用三角网逼近不划算的曲边四边形。
  • 新方法 THotPDF.RegisterCoonsPatchMesh(XMin, YMin, XMax, YMax, NumComponents, Patches) 打包二进制 Shading 流(/ShadingType 6 + /BitsPerCoordinate 16 + /BitsPerComponent 8 + /BitsPerFlag 8), 外包成 Pattern Type 2,返回 Pattern 名(Sh1、Sh2、…),可直接用 SetFillPattern / SetStrokePattern 调用。
  • Patches 平铺数组每个 patch 由 12 个控制点(24 个 X+Y 浮点) + 4 个角颜色(4 × NumComponents 浮点)组成,stride = 24 + 4 × NumComponents。控制点 c1..c12 按 patch 边界顺时针顺序排列,c1 / c4 / c7 / c10 是 4 个角,其余 8 个是贝塞尔内控点。 所有 patch 的 flag 字节固定为 0(独立 patch);共边继承(flag 1 / 2 / 3)未在本便利重载中暴露。
  • Type 7(tensor product patch mesh)作为后续工作。

2026-05-16 Version 2.49.0

  • 新增 PDF 1.3+ ISO 32000-1 8.7.4.5.5 lattice 形式 Gouraud 三角网格阴影(Shading Type 5),是 v2.48.0 Type 4 自由形式的姊妹特性。当源数据本身位于规则采样网格上(地形、有限元结果、科学热图等)、调用方不愿显式编排三角形拓扑时,由阅读器自动把相邻两行连成 triangle strip。
  • 新方法 THotPDF.RegisterLatticeFormGouraudShading(XMin, YMin, XMax, YMax, NumComponents, VerticesPerRow, Vertices) 把 M 行 × N 列 lattice 打包为二进制 Shading 流(/BitsPerCoordinate 16、/BitsPerComponent 8、 /VerticesPerRow N)。与 Type 4 不同:没有逐顶点的 flag 字节, /BitsPerFlag 也不再出现在 shading 字典里。返回 Pattern 名(Sh1、 Sh2、…),可直接用 SetFillPattern / SetStrokePattern 调用。
  • Vertices 平铺数组按 row-major 排列,每个顶点 (X, Y, c0, c1, …, c_{NumComponents-1});总顶点数必须是 VerticesPerRow 的整数倍,至少两行。NumComponents 接受 1 (DeviceGray)、3 (DeviceRGB)、4 (DeviceCMYK)。
  • Type 6(Coons patch)/ Type 7(tensor product)网格阴影仍待。

2026-05-16 Version 2.48.0

  • 新增 PDF 1.3+ ISO 32000-1 8.7.4.5.4 自由形式 Gouraud 三角网格阴影 (Shading Type 4),适合向量插画中超出 Type 2 / 3 轴向 / 径向渐变能力范围的颜色过渡。任何带顶点颜色的三角化曲面都可以直接嵌入。
  • 新方法 THotPDF.RegisterFreeFormGouraudShading(XMin, YMin, XMax, YMax, NumComponents, Vertices) 把三角网格打包为二进制 Shading 流 (/BitsPerCoordinate 16、/BitsPerComponent 8、/BitsPerFlag 8), 包装成 Pattern Type 2,返回 Pattern 名(Sh1、Sh2、…),可直接用 THPDFPage.SetFillPattern / SetStrokePattern 调用。
  • Vertices 是平铺数组,每个顶点 (X, Y, c0, c1, …) row-major;每三个顶点构成一个独立三角形。输出坐标 / 分量以大端无符号整数编码; /Decode 数组将编码范围映射回调用方指定的用户空间矩形。

2026-05-16 Version 2.47.0

  • 新增 DeviceN 色彩空间支持(PDF 1.3+ ISO 32000-1 8.6.6.5),将 Separation 推广到 N 个 colorant,覆盖六色印刷、金属 / 荧光油墨混合、PDF/X 自定义油墨等印前工作流。新方法 THotPDF.RegisterDeviceN(ColorantNames, AlternateCS, TintC1Matrix) 返回自动分配的色彩空间名(DevN1、DevN2、…),可直接配合 SetFillColorSpace + SetFillColor 使用,提供 N 个 [0..1] tint 分量。
  • tint transform 是 PostScript 计算器(Function Type 4),按调用方传入的 N×M 矩阵线性加权混合,无 spot 支持的阅读器在备用色彩空间中按"各 colorant 贡献相加"渲染。
  • ColorantNames 接受任意 spot ink 名,含空格的按 PDF 1.7 7.3.5 自动转义为 #20。特殊名 "None" 表示未使用的 colorant 槽位。
  • AlternateCS 接受 DeviceGray、DeviceRGB 或 DeviceCMYK;其它值抛异常。

2026-05-15 Version 2.46.0

  • 扩展 AcroForm 自动外观流(AutoFormAppearances)对文本字段的支持, 符合 PDF 1.7 ISO 32000-1 12.7.4.3。v2.28.0 早期版本对所有文本控件都只输出单行字面值;现在多行字段和分格(comb)字段各走专门的 AP 布局路径。
  • 多行文本字段(Flags 含 ffMultiline)会按控件宽度对初始值做单词换行,识别 CR / LF / CRLF 行分隔符,按可见行数截断,并使用 Td + T* + /TL 的标准多行版式输出。
  • 分格文本字段(Flags 含 ffComb 且 MaxLen > 0)按格宽逐字符使用绝对 Tm 矩阵定位,渲染效果与 Acrobat 一致。
  • 单行文本字段的 AP 内容流保持 v2.28.0 字节级行为,旧调用方无需改动。

2026-05-14 Version 2.45.0

  • 新增 Separation 色彩空间支持,覆盖印前 spot color 工作流(ISO 32000-1 8.6.6.4)。新方法 THotPDF.RegisterSeparation(ColorantName, AlternateCS, TintC1) 返回自动分配的色彩空间名(Sep1、Sep2、…), 可直接配合 THPDFPage.SetFillColorSpace / SetStrokeColorSpace + SetFillColor([tint]) / SetStrokeColor([tint]) 使用,其中 tint ∈ [0..1] 表示 spot 油墨的浓度。
  • AlternateCS 接受 DeviceGray、DeviceRGB 或 DeviceCMYK;TintC1 描述 tint=1.0 时的备用色彩。HotPDF 自动构造线性 Function Type 2 作为 tint transform,无 spot 支持的阅读器在 tint=0(无墨)与 TintC1 (满墨)之间平滑插值。
  • Pantone 等含空格的油墨名可直接传入,HotPDF 在写出 PDF 名字时按 PDF 1.7 7.3.5 自动转义为 "#20" 序列。
  • 接口经过 RequirePDFVersion 限制为 PDF 1.3 及更高版本,避免严格模式 1.2 / 更旧输出意外引入新特性。

2026-05-14 Version 2.44.0

  • 新增 CIELab 色彩空间支持,符合 ISO 32000-1 8.6.5.3。新方法 THotPDF.RegisterLabColorSpace(Xw, Yw, Zw, aMin, aMax, bMin, bMax) 返回自动分配的色彩空间名(Lab1、Lab2、...),可直接配合 THPDFPage.SetFillColorSpace / SetStrokeColorSpace + SetFillColor([L, a, b]) / SetStrokeColor([L, a, b]) 使用。
  • 典型流程:每种光源各注册一个空间(ICC 印刷常用 D50,sRGB 显示常用 D65),用 L* 取值 [0..100],a*、b* 取值 [-128..127] 绘制。Adobe Acrobat、Foxit、MuPDF、浏览器内置阅读器都会按 WhitePoint 把 Lab 转换为设备 RGB。
  • 接口经过 RequirePDFVersion 限制为 PDF 1.3 及更高版本,避免严格模式 1.2 / 更旧输出意外引入新特性。

2026-05-14 Version 2.43.1

  • 在真实位图上端到端验证现有 CCITTFaxDecode 编码器:240×120 pf1bit 黑白条纹 TBitmap 分别经 icCCITT31(G3 1D)、icCCITT32(G3 2D)、 icCCITT42(G4 / T.6)三种模式嵌入 PDF,压缩比按预期排序 G3-1D > G3-2D > G4,Adobe Acrobat / Foxit / MuPDF 解码均无警告,逐像素采样确认黑白条纹三种模式都能完美还原。本次仅补充 PDF 1.7 ISO 32000-1 7.4.9 的 smoke 覆盖,编码器代码未改动。

2026-05-14 Version 2.43.0

  • 新增未着色 Tiling Pattern(PaintType=2)的端到端渲染支持,符合 ISO 32000-1 8.6.6.1。瓷砖流只描述几何,颜色通过页面 scn / SCN 操作符的 tint 参数在填充 / 描边时传入。
  • THPDFPage 新增 SetFillPatternRGB / SetStrokePatternRGB / SetFillPatternGray / SetStrokePatternGray / SetFillPatternCMYK / SetStrokePatternCMYK 方法,接收 RegisterTilingPattern 返回的图案名以及 DeviceRGB / DeviceGray / DeviceCMYK 的 tint 颜色分量(取值 0..1)。每个调用会在首次使用时为页面自动注册对应的 [/Pattern /BaseCS] 着色空间。
  • 已有 SetFillPattern / SetStrokePattern 继续覆盖 PaintType=1(已着色) 情形 —— 瓷砖自带颜色,scn 操作符不需要 tint 参数。
  • RegisterTilingPattern 入口保持不变:Colored=true 写 PaintType=1, Colored=false 写 PaintType=2。

2026-05-14 Version 2.42.0

  • 新增 PDF 1.4 软掩膜(SMask)图像支持。新方法 THotPDF.AddImageWithSMask 接收一段原始 RGB 像素数据与一段对应的 8 位 alpha 数据,分别输出彩色图像 XObject 与 DeviceGray 软掩膜 XObject,并按 ISO 32000-1 8.9.5.4 自动写入 /SMask 引用关系。返回的图像索引可继续走 ShowImage 渲染,无需额外接线。
  • 新增便捷封装 THotPDF.AddImageWithSMask32,可直接从 32 位 BGRA 的 TBitmap 拆出彩色与 alpha 通道,让带 alpha 的 PNG 风格位图能一次嵌入。
  • 上述接口均通过 RequirePDFVersion 限制为 PDF 1.4 及更高版本,避免严格的 1.3 输出意外引入新版本特性。

2026-05-14 Version 2.41.0

  • 新增 PDF 1.5 对象流输出。同时启用 THotPDF.UseObjectStreams 与 UseXRefStream 后, SaveToStream 会把可压缩的间接对象(除 stream 对象、加密字典以及 trailer 引用的 Catalog / Info 字典外)打包到一个或多个 /Type /ObjStm 容器流中。
  • 对含有大量小字典的 PDF(多页、注释、AcroForm 控件、结构树节点、可选内容图层等) 显著减小文件体积。30 页纯图形测试从 14502 字节缩减到 8488 字节(减少 41.5%), 视觉输出保持一致。
  • 交叉引用流新增 type-2 压缩对象条目,field-2 为宿主 ObjStm 对象号,field-3 为该对象在容器中的序号。若 UseXRefStream 未启用(旧式 xref 表无法表达 type-2 条目)或 Version 低于 PDF 1.5,UseObjectStreams 会被自动降级为 false。

2026-05-12 Version 2.40.1

  • 修复在启用 "Beta:使用 Unicode UTF-8 提供全球语言支持" 区域设置的 Windows 10 / 11 上生成的 PDF 在 Adobe Acrobat、Foxit、SumatraPDF、浏览器内置阅读器中显示空白页的问题。 之前 HotPDF 会在每个页面内容流开头多写 3 字节 UTF-8 BOM (EF BB BF),严格阅读器视为语法错误,整页拒绝渲染;MuPDF 等容错阅读器会跳过 BOM 正常显示,因此问题在部分环境下不易察觉。

2026-05-11 Version 2.40.0

  • 新增 CID-keyed CFF 字体子集化,支持中日韩等大字符集 OpenType-CFF 字体,以及多 FD Adobe 字体。
  • 改进 CFF 子例程处理,大型 OpenType-CFF 字体现在可嵌入为更小的 PDF 子集字体,而不必整字体嵌入。

2026-05-11 Version 2.39.1

  • 改进真实 OpenType-CFF 字体兼容性,覆盖 Adobe MinionPro 类字体中较大的 CFF 表和多字节偏移结构。
  • 新增 HPDFExtractTTFPostScriptName,方便自定义字体加载流程读取 TrueType 和 OpenType-CFF 字体中的 PostScript 名称。

2026-05-11 Version 2.39.0

  • 新增 CFF Global Subr 和 Local Subr 子集化,进一步降低嵌入式 OpenType-CFF 字体体积。
  • 保留实际使用的 CFF 子例程字节码,并将未使用条目安全替换为紧凑的 return stub。

2026-05-11 Version 2.38.0

  • StoreFont 新增 OpenType-CFF 自动嵌入。GDI 载入 sfnt 'OTTO' 容器时,HotPDF 会输出 CFF-based CID 字体。
  • 新增 HPDFSfntIsOTF、HPDFOTFFindCFFTable 和 HPDFSubsetOTFContainer,供需要直接处理 OpenType-CFF 字体的应用调用。

2026-05-11 Version 2.37.0

  • 新增 HPDFSubsetCFF,用于 Compact Font Format 和 Type 1C 字体载荷,可生成更小的嵌入式 CFF 字体程序。
  • CFF 子集化会保留原始 glyph ID,确保现有 Type0 / Identity-H 文本输出保持兼容。
  • CFF 子集器已通过单元接口开放,适合使用自定义字体加载流程的应用直接调用。

2026-05-10 Version 2.36.0

  • 新增可选 TrueType 字体子集化。启用 THotPDF.EnableFontSubsetting 后,只嵌入实际使用字形,可显著减小 PDF 体积。
  • 新增符合 PDF 规范的子集字体命名,同时保留源字体 glyph ID,兼容 Identity-H 文本输出。
  • 默认仍使用完整字体嵌入,保证需要完整字体程序的工作流不受影响。

2026-05-10 Version 2.35.1

  • 修复使用 Windows 替代字体生成的 PDF 在 Adobe Acrobat 中可能触发的 "Font Capture" 崩溃。HotPDF 现在会让 PDF 字体名称与内嵌 TrueType 数据中的 PostScript 名称保持一致。

2026-05-10 Version 2.35.0

  • 新增 PDF 增量更新支持,可保留源文件字节,并通过 THotPDF.SaveIncrementalUpdate 追加合规的增量修订。
  • 新增 THotPDF.MarkDirty,应用可在增量更新中显式标记需要重新保存的对象。
  • 支持多签名工作流,可在保留既有签名 ByteRange 的前提下追加后续签名字段或文档修改。

2026-05-10 Version 2.34.0

  • 新增 THPDFPage.SetAnnotationBorderStyle,支持 Solid、Dashed、Beveled、Inset 和 Underline 等注释边框样式。
  • 新增 Popup 注释关联能力,弹出注释可引用父注释,阅读器中会显示预期的批注连接关系。
  • 新增 THPDFPage.LastAnnotation,便于在不改变现有注释创建接口的情况下继续设置样式或 Popup。

2026-05-10 Version 2.33.0

  • 新增 PDF Public-Key Security Handler。文档现在可使用 X.509 证书接收者加密,而不只依赖共享密码。
  • 新增 THotPDF.EnablePubKeyEncryption 和 THotPDF.AddPubKeyRecipient,用于证书接收者加密流程。
  • HPDFCrypt 新增纯 Pascal SHA-1 支持,并保留现有哈希原语。

2026-05-10 Version 2.32.0

  • 新增页面级透明组。THPDFPage.SetTransparencyGroup 和 SetTransparencyGroupICC 可让透明 PDF 内容合成结果更可预测。
  • 支持 DeviceGray、DeviceRGB、DeviceCMYK、省略颜色空间,以及 ICC 配置文件透明组。
  • 新增 THPDFPage.ClearTransparencyGroup,用于移除已设置的页面透明组。

2026-05-09 Version 2.31.0

  • 为 AcroForm 按鈕新增交互动作支持。此前 HotPDF 生成的按鈕控件外观正常但点击无效果;现在按鈕可执行:提交表单数据、重置字段値、运行 JavaScript 脚本或跳转至 URI。
  • 提交表单动作将当前 AcroForm 字段値发送到服务器 URL,可在 Adobe Reader 和 Foxit 中无需单独 Web 阅读器直接实现基于 PDF 的数据收集表单。
  • 重置表单动作将所有字段恢复为默认値,可选仅针对指定字段子集。JavaScript 和 URI 动作遵循 v2.26.0 引入的 PDF 版本门控机制。
  • 此前 AddPushButtonWithAction 仅接受按鈕控件的视觉属性;此版本后,按鈕外观与点击行为均可在单次调用中完整控制。

2026-05-09 Version 2.30.0

  • 新增全屏演示 PDF 页面转场,THPDFPage.SetPageTransition 可设置页面切换动画。
  • 新增 THPDFPage.SetPageDuration,页面可按指定延迟自动前进。
  • 新增 Fly、Push、Cover、Uncover、Fade 等 PDF 1.5 转场样式的版本感知处理。

2026-05-09 Version 2.29.0

  • 新增 ViewArea 和 ViewClip 属性,完成 PDF 1.4 ViewerPreferences 页面边界集,与已有的 PrintArea 和 PrintClip 形成完整配套。
  • ViewArea 控制阅读器用于屏幕显示的页面框(MediaBox、CropBox、TrimBox、BleedBox 或 ArtBox);ViewClip 控制屏幕显示时的裁切边界——对包含出血和裁切标记、正常预览时不应显示的设计审稿 PDF 尤为重要。
  • 这两个选项在印前检查和打印预览工作流中尤为有用,允许屏幕裁切与打印裁切独立设置。两个属性均要求 PDF 1.4,目标版本较旧时自动省略。

2026-05-09 Version 2.28.0

  • 新增 THotPDF.AutoFormAppearances,可为文本框、列表、按钮、复选框和单选按钮自动生成 AcroForm 外观流。
  • 自动外观模式会加入 Helvetica 和 ZapfDingbats 默认资源,让忽略 /NeedAppearances 的阅读器也能可靠显示表单。
  • AutoFormAppearances 默认关闭,保持旧版 AcroForm 行为兼容。

2026-05-09 Version 2.27.0

  • 将 PDF 版本门控扩展至 ViewerPreferences 和 PageMode 条目。各首选项値现在强制执行其最低 PDF 版本要求:UseAttachments 和 UseOC 需要 PDF 1.5,PrintScaling 需要 PDF 1.6,Duplex、NumCopies 和 PickTrayByPDFSize 需要 PDF 1.7。
  • 针对旧版 PDF 的文档会自动省略不支持的查看器首选项条目,避免在严格 PDF 处理器和归档验证器中产生警告或静默误解。
  • 启用 THotPDF.StrictVersionLock 后,PDF 1.3 或 PDF 1.4 输出不会携带任何较新的查看器首选项字段,使文件符合印刷生产和长期归档工作流的规范要求。

2026-05-09 Version 2.26.0

  • 对此前忽略目标版本的部分旧功能 API 新增 PDF 版本门控。注释类型、JavaScript 动作、Type 0 字体、ToUnicode CMap 及 Catalog 附加动作,现在会在写入前验证文档版本是否支持该功能。
  • 目标为 PDF 1.0 或 PDF 1.1 的文档在使用较新功能时会自动升级到所需版本,避免生成不符合规范的 PDF,且无需修改调用代码。
  • 启用 THotPDF.StrictVersionLock 后,需要版本升级的功能调用将被拒绝而非静默升级,适用于必须以指定 PDF 版本输出的工作流。
  • 该版本检查机制在 v2.27.0 扩展至 ViewerPreferences 和 PageMode,并在 v2.31.0 前覆盖所有主要功能领域。

2026-05-09 Version 2.25.0

  • 修复 PDF 字典输出中的键名大小写敏感性问题,符合 PDF 规范要求。此前,仅大小写不同的相关键名——如 ExtGState 字典中的 /ca(填充透明度)和 /CA(描边透明度)——被错误地合并为一条,导致其中一个値被静默丢失。
  • 此修复恢复了在同一图形状态中对每个填充和描边分别设置透明度的正确行为。Adobe Acrobat、Foxit 和浏览器 PDF 阅读器均可正确渲染修复后的输出。
  • 公开的字典查询 API 仍保持大小写不敏感,读取存在键名大小写不一致的非规范 PDF 时,现有代码无需修改。

2026-05-09 Version 2.24.0

  • v2.4.0 之后新增的功能 API 增加 PDF 版本门控,帮助生成文件保持在选择的 PDF 版本范围内。
  • 新增 THotPDF.StrictVersionLock。关闭时 HotPDF 会按需自动提升文档版本;开启时,超过目标版本的功能调用会被拒绝。
  • ConfigurePDFVersion 现在会防止 XRef 流、XMP 元数据、Tagged PDF 和 AES-256 选项泄漏到较旧 PDF 目标。

2026-05-09 Version 2.23.0

  • 新增三步数字签名工作流,用于集成外部 PKI 和 CMS / PKCS#7 签名库:创建带占位符的签名域,计算待签名的字节范围和摘要,最后在外部签名完成后注入完整的 CMS 签名。
  • 字节范围策略确保哈希値覆盖签名容器以外的文件内容,与 PDF 签名验证器和 PAdES 一致性检查器的要求完全吧合。
  • 所有加密操作——证书链、CMS 结构、时间戳令牌——均由调用方的外部签名库(OpenSSL、Windows CAPI、Bouncy Castle 等)负责,使 HotPDF 独立于任何 PKI 基础设施。
  • 支持多签名文档:每个附加签名作为增量更新追加,不重新生成整个 PDF,也不覆盖先前的签名字节范围。

2026-05-09 Version 2.22.0

  • 新增 PDF 2.0 AES-256 V=5 R=6 标准安全处理器,可通过 THotPDF.UseAES256R6 启用。
  • HPDFCrypt 新增纯 Pascal SHA-384 和 SHA-512。
  • 新增 PDF 2.0 hash-dance 辅助逻辑,用于 Acrobat DC 和 PDF/A-4 相关加密工作流。

2026-05-09 Version 2.21.0

  • Tagged PDF 从基础标记扩展为完整的 PDF 可访问性结构树。
  • 新增角色映射、页面 StructParents 分配和 ParentTree 输出,便于 PDF/UA 验证器遍历结构树。
  • 改进 StructTreeRoot 处理,后续 Tagged PDF 调用会更新同一个文档结构。

2026-05-09 Version 2.20.0

  • 新增页面级 /UserUnit 和 /Tabs 支持,对应 THPDFPage.SetUserUnit 和 THPDFPage.SetTabsOrder。
  • 新增 Optional Content(PDF 图层),包括 THotPDF.RegisterOptionalContentGroup 和 BeginOptionalContent / EndOptionalContent。
  • 新增 ExtGState 辅助接口,用于透明度 alpha 和混合模式设置。

2026-05-09 Version 2.19.0

  • 新增 CropBox、BleedBox、TrimBox、ArtBox 和页面旋转辅助接口,服务印刷和生产 PDF 工作流。
  • 新增 THotPDF.AddStructureElement,可构建包含角色、页面链接和 MCID 引用的 Tagged PDF 结构树。
  • 新增平铺图案支持,可用于重复的彩色或非彩色 PDF 图案填充和描边。

2026-05-09 Version 2.18.0

  • 新增 THPDFPage.DrawInlineImage,小型栅格图像可直接写入页面内容流。
  • 新增多段轴向渐变,用于更平滑的 PDF 色彩过渡效果。
  • 新增 OutputIntents 支持,服务 PDF/A、PDF/X 和色彩管理出版流程。

2026-05-09 Version 2.17.0

  • 新增 AES-256 标准安全处理器(PDF 1.7 扩展级别 3),适用于需要比默认 AES-128 更强保护的文档。加密后的 PDF 可在 Adobe Acrobat 9 及更高版本、Foxit Reader、Google Chrome、Apple Preview 及所有现代 PDF 阅读器中打开。
  • 用户密码和所有者密码均按 Adobe 扩展级别 3 规范采用 SHA-256 哈希推导,确保与任何合规 PDF 解密器的兼容性,无需自定义支持。
  • 此版本的 AES-256 加密为可选启用;现有 AES-128 路径仍为默认,以保持与旧版 Acrobat 的向后兼容性。
  • v2.22.0 进一步新增 PDF 2.0 AES-256-R6,采用改进的密钥派生算法,满足 Acrobat DC 和 PDF/A-4 工作流的最高安全强度需求。

2026-05-09 Version 2.16.0

  • 新增六种注释子类型,面向多媒体、法律合规及无障碍访问场景:Watermark(显示与打印水印覆盖层)、Redact(标记待删除内容区域)、Screen(嵌入视频和音频)、3D(交互式 U3D/PRC 模型查看器)、RichMedia(交互式媒体内容)以及 Popup(关联父注释的评论气泡)。
  • Watermark 注释支持分离的显示和打印渲染,可在屏幕上显示“草稿”或“机密”标记而不打印,或仅在打印时显示。
  • Redact 注释标记内容区域以备分发前删除;阅读器的标注清除工作流在最终应用前保留原始内容,避免不可逆操作。
  • 六种注释类型均设有 PDF 版本门控,目标版本低于相应要求时,较新的注释子类型将自动从输出中省略。

2026-05-09 Version 2.15.0

  • 新增 Type 3 字体支持,可将自定义矢量字形定义直接嵌入 PDF 输出。Type 3 字体非常适合公司徽标、专有符号集、地图图标及无法用标准字体表达的图形符号。
  • 每个字形均以 PDF 内容流形式使用标准绘图操作符定义,因此 Type 3 字形在任何缩放比例和打印精度下均保持清晰,无需外部字体文件。
  • 完整工作流——注册字体、定义各字形、在页面上选用字体——通过 RegisterType3Font、AddType3Glyph 和 SetType3Font 完成。
  • Type 3 字体可在 Adobe Reader、Foxit 及所有规范兼容的 PDF 阅读器中正确渲染,无需在阅读器系统上安装字体。

2026-05-09 Version 2.14.0

  • 新增纯 Pascal 实现的 AES-256 和 SHA-256,作为更强文档加密的内部加密原语。这两种算法是 PDF 1.7 扩展级别 3 及 PDF 2.0 加密标准所要求的。
  • 新加密代码无需外部 DLL 或操作系统提供的加密库,使 HotPDF 在所有受支持的 Delphi 和 C++Builder 目标及 Windows 版本上保持自包含。
  • AES-128 仍为此版本的默认文档加密格式;新加密原语为 v2.17.0 的 AES-256 安全处理器和 v2.22.0 的 PDF 2.0 安全处理器奠定基础。

2026-05-09 Version 2.13.0

  • 新增 PDF 矢量渐变填充:轴向(线性)渐变和径向渐变。矢量渐变生成的 PDF 文件远小于等效的位图渐变,且在任何缩放比例和打印精度下均保持清晰。
  • 两种渐变类型均支持 DeviceGray、DeviceRGB 和 DeviceCMYK 颜色空间,覆盖屏幕显示、RGB 印刷和 CMYK 四色印刷工作流。
  • 渐变可作为填充色或描边色应用于页面上的任意路径,可在 Adobe Reader、Foxit、SumatraPDF 及所有现代 PDF 阅读器中正确渲染。

2026-05-09 Version 2.12.0

  • 通过 THPDFPage.AddSignatureField 新增未签名签名域控件。生成的 PDF 包含可视化签名占位区,外部 PKI 工作流、签名门户及 CMS / PKCS#7 签名库可直接填入真实加密签名。
  • 只要存在至少一个签名域,AcroForm 签名标志即自动设置,无需额外调用即可保持文档规范合规。
  • 签名域控件可被 Adobe Acrobat、Foxit 及其他 PDF 签名方案识别为可签名字段。完整的字节范围签名工作流参见 v2.23.0。

2026-05-09 Version 2.11.0

  • 新增 Tagged PDF 基础架构,符合 PDF/UA 及现代无障碍访问标准要求。标签文档包含逻辑阅读顺序和语义结构,屏幕阅读器、辅助技术及文本提取工具均可借助此结构正确处理内容。
  • THotPDF.EnableTaggedPDF 启用文档结构树,THotPDF.Lang 设置主文档语言,两者均为无障碍一致性验证所必需。
  • 标记内容操作符允许将页面上的各绘图操作标注语义角色(段落、标题、图形等)和标识符,使生成的 PDF 支持正确的内容重排和屏幕阅读提取。
  • 结构元素和标记内容标识符按页面和文档分别管理,与 PDF 规范对多页标签文档的要求一致。

2026-05-09 Version 2.10.0

  • 新增 PNG 预测器和 TIFF 水平差分预测器,用于 FlateDecode 图像流。预测器在压缩前对每行数据进行自适应滤波,可显著减小摄影图像和渐变图像的输出体积。
  • PNG 最优预测器(自适应行模式)和 TIFF 预测器 2(水平差分)作为独立编码辅助函数提供,可在自定义图像嵌入流程中单独使用。
  • 两种预测器均支持 DeviceGray、DeviceRGB 和 DeviceCMYK 颜色空间,覆盖屏幕显示和四色印刷生产流程。

2026-05-09 Version 2.9.0

  • 在传统 Info 字典之外新增 XMP 元数据流输出。XMP 是嵌入式 PDF 元数据的现代标准,PDF/A、PDF/X 及众多归档工作流均要求支持。
  • 桌面搜索引擎、数字资产管理系统及不再读取旧 Info 字典的 PDF 处理流程均可识别 XMP 元数据。
  • XMP 流在输出文件中保持不压缩,方便外部工具和命令行实用程序直接检查元数据内容,无需解码文件。
  • 需要精确控制元数据的应用,可通过 THotPDF.CustomXMP 提供完整的自定义 UTF-8 XMP 数据包,跳过自动生成逻辑。
  • 启用文档加密时,XMP 流默认随文档一起加密,保证元数据的保密性。

2026-05-09 Version 2.8.0

  • 新增可选的 PDF 1.5 交叉引用流输出。交叉引用流以紧凑的二进制格式编码对象偏移量表,替代传统的大段纯文本,使每个生成的 PDF 文件均可获得体积减小的收益。
  • PDF 1.5 特性(如 v2.41.0 引入的对象流)依赖交叉引用流;目标版本低于 1.5 时该选项自动关闭。
  • 使用交叉引用流的 PDF 可被现代阅读器、归档工具和 PDF/A 验证器更高效地处理。
  • 传统纯文本 xref 表仍为默认输出,以兼容旧版 PDF 阅读器和严格格式校验工具。

2026-05-09 Version 2.7.0

  • 新增原生 DeviceCMYK 颜色输出,适合印刷和 PDF/X 工作流。
  • 新增 THotPDF.RegisterICCProfile,用于注册 ICCBased 颜色空间。
  • 新增通用填充和描边颜色空间 API,支持 ICC 配置文件和任意分量颜色值。

2026-05-09 Version 2.6.0

  • 新增 Highlight、Underline、Squiggly 和 StrikeOut 文本标记注释。
  • 新增 Polygon、Polyline、Ink 和 Caret 注释。
  • 新增 GoTo、GoToR 和 Launch 链接动作,用于文档内跳转、跨文档跳转和外部文件启动。

2026-05-09 Version 2.5.0

  • 新增 AcroForm 支持,包括文本框、复选框、单选按钮、下拉框、列表框和按钮。
  • 新增 THPDFFormFieldFlags,应用可直接配置常用表单字段选项,无需手工处理位标志。
  • 生成的表单会设置 /NeedAppearances,常见 PDF 阅读器可自动显示字段。

2026-05-08 Version 2.4.0

  • 修复 AES-128 PDF 加密。V=4 / R=4 安全处理器现在会真正用 AES-CBC 加密字符串和流。
  • CryptKeyLength=aes128 现在会生成可由合规阅读器用配置密码打开的加密 PDF。
  • 新增 ASCIIHexDecode、ASCII85Decode 和 RunLengthDecode 编码辅助函数。
  • 更新内置 JBIG2 和 JPEG 2000 stub,未链接真实解码后端时会明确报告不支持,而不是返回占位图像数据。

2026-05-06 Version 2.3.23

  • 调整内置 zlib-ng、libjpeg-turbo 和 libtiff 对象构建,以适配当前 RAD Studio 和 Visual Studio 工具链。
  • 改进 Delphi 和 C++Builder 目标上的原生压缩与图像库堆内存对齐。
  • 验证 Delphi 和 C++Builder 自动化回归测试在 Win32、Win64 和 Win64x 目标上的运行结果。

2026-05-05 Version 2.3.22

  • 内置 64 位 Flate 压缩构建启用 zlib-ng 运行时 SIMD 调度。
  • Win32 zlib-ng 保持在稳定的 generic object 路径,兼容经典工具链。
  • 加强原生对象构建检查,编译器成功但未生成输出对象时会视为构建失败。

2026-05-05 Version 2.3.21

  • 内置 Win32、Win64 和 Win64x 对象构建启用 libjpeg-turbo SIMD 加速。
  • SIMD 支持改为通过 HotPDF 构建标志显式控制。
  • 修复 Win32 NASM OMF 输出问题,解决 Delphi Win32 无法链接 SIMD JPEG 对象集的问题。
  • 修复 SIMD 与 zlib-ng 重建后暴露的 Win64 TIFF 图像回归。

2026-05-05 Version 2.3.20

  • 内置 Flate 后端切换到 zlib-ng 兼容模式,覆盖页面流、字体、CMap、图像、TIFF 输出和 FlateDecode 辅助函数。
  • 修复 zlib-ng 迁移中发现的密集流压缩和 TIFF 小图导入问题。
  • JPEG 工作流继续由 libjpeg-turbo 支持,TIFF 工作流继续由 libtiff 支持。
  • 新增覆盖 Delphi 和 C++Builder 的自动化回归测试,包含 PDF 生成、图像导入、压缩、加密、 链接、页面设置和复制/合并/编辑流程。

2026-05-01 Version 2.3.19

  • 修复密集条码输出下的 Win64 FlateDecode 页面流生成问题。
  • 现代 Delphi 页面流和字体流压缩改用稳定的 RTL zlib 路径。

2026-05-01 Version 2.3.18

  • 修复 C++Builder Win64x 加密 PDF 生成中的 RC4 指针宽度问题。
  • 修复 C++Builder Win64x 构建中的 TIFF 导入问题。
  • 修复 LoadFromFile 打开的 PDF 追加页面流程,确保追加页以安全对象编号保存。

2026-05-01 Version 2.3.17

  • 更新 CanvasDrawing 和 GraphicDraw C++Builder 示例,使其与改进后的 Delphi 图形示例保持一致。
  • 改进 ViewerPref C++Builder 示例,增加清晰的 UI 状态、输入验证、预设保存/加载和方向测试输出。

2026-05-01 Version 2.3.16

  • 修复合并含嵌入式 CID TrueType 字体和嵌套宽度数组的 PDF 时页面复制错误。
  • 改进从多个源 PDF 连续复制页面时的对象映射,避免后续页面复用旧资源。

2026-05-01 Version 2.3.15

  • 改进 ViewerPref Delphi 示例,仅在相关选项启用时开放对应控件。
  • ViewerPref 示例新增错误提示、复制份数验证和命名预设保存/加载支持。

2026-05-01 Version 2.3.14

  • PDFmerge Delphi 示例改为自包含合并示例,可在需要时自动创建源 PDF。
  • 改进合并验证、输出索引和 UI 成功/失败提示。

2026-05-01 Version 2.3.13

  • 改进 CanvasDraw 和 GraphicDraw Delphi 示例,使视觉输出更清晰、更可重复。
  • 改进 GDI 字体路径中的 TrueType 字体选择,减少 raster fallback 导致的嵌入失败。
  • 修复同一 PDF 中多段文本复用同一字体时的字符间距问题。
  • 字体嵌入错误信息现在包含请求字体名和字符集,便于诊断。

2026-05-01 Version 2.3.12

  • 改进 CopyPage 示例,使其可直接复制包含非标准 /Pages 树的 PDF,无需额外工具。
  • CopyPage 无论使用哪条复制路径成功,都会写入 FlateDecode 压缩输出。
  • 独立 CopyPageFixed 示例已合并到主 CopyPage 示例中。

2026-05-01 Version 2.3.11

  • 生成的 PDF 默认以页面适应窗口高度方式打开。
  • 新增大量无 ps 前缀的预定义纸张尺寸名称,覆盖办公、大幅面、绘图、证件、照片和中日常用规格,并保留旧名称兼容。
  • Delphi 页面尺寸示例重组为 LargeSize、NormalSize 和 SmallSize 项目。

2026-04-29 Version 2.3.10

  • 修复 RAD Studio XE7 包构建中的 TIFF 兼容层问题。
  • 减少 RAD Studio 10 Seattle 和 10.1 Berlin 下的编译警告。
  • 修正 RAD Studio ProductVersion 9.0 到 12.0 的命令行库构建映射。
  • 恢复 RAD Studio ProductVersion 9.0 的命令行包构建。
  • 修复 RAD Studio XE2 Win64 构建,补齐 Win64 兼容 stub 所需的 CRT vsnprintf 符号。
  • 旧版 Delphi 和 C++Builder 工程文件改用数字布尔值,提高兼容性。

2026-04-29 Version 2.3.9

  • 修复 RAD Studio 10.1 Berlin 库包构建,使包从 Lib 目录编译并稳定解析包内单元。
  • 修复旧 Delphi 编译器上的 Trial 库构建,移除 Trial 水印路径中的 inline variable declaration。

2026-04-29 Version 2.3.8

  • 移除 RAD Studio 10.3 Rio 构建中 TIFF stream callback 路径的废弃 TStream.Seek 编译警告。

2026-04-29 Version 2.3.7

  • 修复使用内置 libtiff 4.7.x 对象文件时的 TIFF-to-PDF 转换问题。
  • 修复严格 Delphi 编译设置下内置 zlib Pascal bridge 的 RAD Studio Win32 编译问题。
  • 新增 TIFF-to-PDF 转换路径的 DUnitX 回归测试覆盖。

2026-04-28 Version 2.3.5

  • 修复使用经典 Borland C++ Win32 编译器重建内置 zlib、libjpeg-turbo 和 libtiff 源包的问题。
  • 重建并对齐随附 Win32 和 Win64 第三方对象文件,提高 Delphi 和 C++Builder 构建可靠性。
  • 无公开 API 变化。现有 Delphi、C++Builder 和 RAD Studio 项目可将本版作为兼容性更新使用。

2026-04-19 Version 2.3.3

  • 改进 RAD Studio 工程可靠性,避免宿主 INCLUDE 环境设置干扰 HotPDF 资源编译。
  • 减少包构建中的重复资源警告,尤其是 RAD Studio 13.1 Florence 项目。

2026-04-19 Version 2.3.2

  • 包和示例工程输出目录会自动跟随当前 RAD Studio 版本。
  • 改进 HotPDF370 包构建行为,避免旧资源文件与新生成资源一起链接。

2026-04-19 Version 2.3.1

  • C++Builder TextOut 示例现在可在受支持 RAD Studio 版本中免手工修改构建。
  • 修复 TextOut 示例 Win64x 重建流程,确保链接前已准备好所需资源文件。

2026-04-19 Version 2.3.0

  • 新增完整 C++Builder 13.1 Florence 支持,覆盖 Win32、Win64 和 Win64x 目标。
  • 统一 Delphi 与 C++Builder 的 TextOut 重载可用性,同时保留旧 C++Builder 辅助名称兼容。
  • 清理 Delphi Win64 静态链接,移除内置 zlib、libjpeg-turbo 和 libtiff 对象文件带来的旧链接警告。
  • 改进 Win64x 包输出,C++Builder 项目可通过常规 RAD Studio 搜索路径找到生成的 HotPDF 库。

2026-04-17 Version 2.2.1

  • 消除内置第三方压缩和图像库在 Delphi Win32 下产生的链接警告。
  • 改进 Delphi 和 C++Builder 构建中 zlib、JPEG 和 TIFF 支持的静态链接兼容性。

2026-04-14 Version 2.2.0

  • 内置第三方库升级到 zlib 1.3.2、libjpeg-turbo 3.1.90 和 libtiff 4.7.1。
  • 现代化 Win32 和 Win64 静态链接,适配更新后的压缩、JPEG 和 TIFF 库。
  • 扩展使用 JPEG 或 TIFF 输入的 PDF 生成场景兼容性。
  • 更新 HTML help,使其匹配当前 HotPDF API、示例和库布局。

2026-04-13 Version 2.1.4

  • 修复 RAD Studio XE2 到 XE8 的 Win64 编译问题。
  • 改进旧 Delphi 编译器使用的内置对象文件兼容性。
  • 移除可能触发重复资源链接警告的旧包资源文件。
  • 修正 Delphi XE3 HotPDF 包工程元数据。

2026-04-12 Version 2.1.3

  • Delphi TestNumeric 示例扩展为实用的数值 PDF 输出回归测试。
  • 增加小数、分数、整数和 PDF 重新加载行为验证。
  • 可选调试输出文件现在通过专用编译开关启用。
  • 更新调试日志和数值输出测试相关帮助内容。

2026-04-10 Version 2.1.2

  • 改进 Win32 和 Win64 PDF 输出中的嵌入字体渲染。
  • 修复在嵌入字体之间切换时字形渲染不完整的问题,包括 Calibri 等常见 Windows 字体。
  • 更新 FontTest 示例和生成的样例 PDF,便于视觉检查。
  • 刷新字体嵌入、Unicode 文本输出和嵌入字体示例相关 HTML help。

2026-04-08 Version 2.1.1

  • 更新 Delphi 和 C++Builder ViewerPreferences 示例,使其匹配当前 HotPDF API。
  • 增加较新 PDF viewer preference 选项覆盖。
  • 改进生成的示例输出,使其只反映 UI 中选中的 viewer preference 设置。
  • 更新 ViewerPreferences 和相关文档属性的 HTML help 页面。