# Devlog · H1:CCITT/JBIG2 1-bit 扫描解码(Phase 5 开工)

> 2026-06-11 · 里程碑 H1(见 [hardening-iteration.md](../plans/hardening-iteration.md))· 状态:✅ 验收过

## 做了什么

`--ocr`/转写管线此前对 CCITT G4/G3(传真压缩)与 JBIG2 编码的扫描 PDF 完全失效
(`images.rs` 在 build 期直接跳过 bpc≠8 的图,连位置占位都没有)。本次打通
1-bit 位图 → Gray8 → OCR 全链路,改动集中在
[crates/docparse-pdf/src/images.rs](../../crates/docparse-pdf/src/images.rs):

- **build 期**(`build_images_from_resources`):放行 bpc==1;ImageMask 默认按
  1bpc 处理(传真 PDF 常见形态);解析 /DecodeParms(含 /DP 别名、数组形)与
  /JBIG2Globals 流字节——必须在这里做,因为 `decode()` 跑在 rayon worker
  线程,没有 `Document` 访问;/Decode [1 0] 记为极性翻转。
- **decode 期**(`decode_bilevel`):三条路——
  1. `CCITTFaxDecode` → `hayro-ccitt`,参数映射照 PDF 32000-1 §7.4.6
     (K<0→G4、K=0→G3 1D、K>0→G3 2D;Columns 默认 1728、EndOfBlock 默认
     true、BlackIs1→invert_black),与 hayro-syntax `filter/ccitt.rs`
     (MIT/Apache-2.0)交叉核对;
  2. `JBIG2Decode` → `hayro-jbig2::Image::new_embedded(data, globals)`;
  3. 其余(packed 1-bit Flate/裸流)按 stride=⌈w/8⌉ 展开,DeviceGray 0=黑。
  截断流保留已解码前缀、尾部补白(扫描件黑字白底,补白不会造字形);
  Columns≠Width 直接退回位置占位(几何会错切,宁缺毋滥,TODO 注明)。

## 依赖决策(review 改写了计划)

计划原文要"依赖征询、JBIG2 记边界"。实际查依赖树发现 `hayro-ccitt`/
`hayro-jbig2`/`hayro-jpeg2000` **早已是传递依赖**(docparse-raster→hayro),
提为直接依赖零新增供应链面;且 `hayro-ccitt` 就是为 PDF CCITTFaxDecode 语义
写的(K/BlackIs1 对口),比通用 `fax` crate(TIFF 向)更合适。"JBIG2 纯 Rust
生态弱"的判断过时,JBIG2 几乎免费入列。JPX:`hayro-jpeg2000` 在树上,但无
真实样例,本次不接(README 边界已注明仍占位)。

## 验证

| 项 | 结果 |
|---|---|
| CCITT G4 合成样例(PIL 画字→单 strip G4 TIFF→手工包 PDF) | `--ocr` 三行逐字正确,release 0.55s |
| CCITT G3(同上,compression=group3,K=0) | 逐字正确 |
| JBIG2 真实样例(pdf.js 语料 `JBIG2Globals.pdf`,拉丁古籍扫描,带 Globals) | `--ocr` 出整页文字,release 8.9s(OCR 推理主导;debug 下 JBIG2 算术解码约 3min,勿用 debug 测扫描件性能) |
| 三件套(lorem/1901.03003/bialetti) | 零回归 |
| 双记分牌 | ODL NID 0.792/MHS 0.685/TEDS 0.419;Docling NID 0.822/MHS 0.643/TEDS 0.474——与基线逐位一致 |
| 单测 | +3(packed 1-bit 展开/截断补白/极性翻转),docparse-pdf 21 绿;clippy 0 warning |

样例生成器内联在会话里(PIL 单 strip TIFF→最小 PDF;`TiffImagePlugin.STRIP_SIZE`
打大保证单 strip——G4 多 strip 各自重置参考行,拼接流不可直接当一条 T.6 流解),
产物在 `tmp/h1/`(不进 repo)。注意本机 `tiff2pdf`/libtiff 二进制是坏的 x86
旧件("bad CPU type"),走了手工包 PDF 的绕行。

## 经验

- **先查依赖树再做"依赖征询"**:传递依赖里可能已经有现成答案,征询题可以
  退化为选型题(且零供应链增量)。
- **极性是 1-bit 解码的头号坑**:TIFF photometric / PDF BlackIs1 / /Decode
  数组三层都可能翻转;e2e 用已知文字样例一锤定音,比读规范推导快。
- **debug 构建不能用来评估算术编码解码器性能**(JBIG2 debug/release ≈ 20×)。
