# PDF 数据提取方案 ## 一、项目概述 本项目从工程图纸 PDF 中提取尺寸标注数据(数值 + 公差 + 直径符号Φ),基于 Java + Spring Boot + PDFBox 3.x。 --- ## 二、Φ(直径)符号识别方案 ### 2.1 问题背景 工程图纸中,直径标注以 Φ 符号前缀表示(如 Φ280 ±0.15)。不同 PDF 生成方式导致 Φ 符号在文本层的表现完全不同: | PDF 类型 | Φ 的存在方式 | 可提取性 | |---------|-------------|---------| | AutoCAD 导出 PDF(类型A) | 文字字符(BerlinSansFB-Bold 字体),独立 TextGroup | ✅ 可提取 | | 其他 CAD 导出 PDF(类型B) | 矢量图形路径(画的圆+斜线) | ❌ 不可提取 | ### 2.2 类型A:文字形式 Φ 的提取 **流程:** 1. `PositionedTextStripper` 提取文本,乱码字符(如 `¡¤`/U+00A1 U+00A4)通过 `correctGarbledText()` 映射为 Φ(U+03A6) 2. `TextGrouper` 将相邻文本元素分组,但 Φ 和数字因字体/位置差异通常分属不同 TextGroup 3. `DimensionIdentifier.findLeadingSymbolGroup()` 在维度数字 TextGroup 的左侧搜索独立的 Φ TextGroup 并拼接 **关键参数:** - 符号集合:`Φ`(U+03A6), `φ`(U+03C6), `Ø`(U+00D8), `ø`(U+00F8), `∅`(U+2205), `⌀`(U+2300) - 空间搜索范围:`gapFromSymLeft ∈ [-10, maxGapFromLeft)`,`dy < maxYDelta` - `maxGapFromLeft = max(dimFontSize × 5.0, 70)` - `maxYDelta = max(dimFontSize × 3.0, 40)` - 渲染顺序过滤(seqDiff):`dimSeq - phiSeq >= 180` - AutoCAD PDF 中 Φ 符号在早期渲染层(seqNum ≈ 33-37),数字在后期层(seqNum ≈ 228-239) - 真正配对的 seqDiff = 195~205 - 误配 "160" 的 seqDiff = 173,误配 "6" 的 seqDiff = 6~7 - 阈值 180 可完美分离 ### 2.3 类型B:矢量图形 Φ 的启发式推断 当 PDF 中 Φ 是图形路径时(整个页面无任何 Φ 文本字符),采用配合公差启发式规则: **规则1 — 配合公差直接推断:** - 文本含配合公差代号(如 `230 m6 +0.017`)→ 自动加 Φ - 排除:大写 R 开头的代号(如 `15R4`),因为 R+数字 通常表示圆角半径而非配合公差 **规则2 — 附近配合公差 TextGroup:** - 搜索范围:dx < 100, dy < 50(像素坐标) - 附近存在 2-3 字符的配合代号文本(如独立的 "H7" TextGroup)→ 加 Φ - 示例:`280 ±0.15` 附近有 `H7`(dx=56, dy=14) → 识别为 Φ280 **已知局限:** - 当 Φ 是矢量图形且附近无配合公差代号时(如 `Φ55 ±0.15`),无法自动识别 - 此类情况需用户在提取结果中手动添加 Φ 前缀 --- ## 三、文本提取管线 ``` PDF文件 ↓ PositionedTextStripper.writeString() ← 提取每次文本绘制调用 │ ├─ 重建 Unicode 文本(TextPosition.getUnicode()) │ ├─ correctGarbledText():修复乱码映射 │ └─ 生成 TextElement(含坐标、字号、seqNum) ↓ TextGrouper.group() ← 按空间邻近性分组 │ └─ 生成 TextGroup 列表 ↓ DimensionIdentifier.identifyDimensions() ← 正则匹配 + 符号拼接 │ ├─ TextNormalizer.normalizeText():二次乱码修复 │ ├─ 正则模式匹配(公差/配合/螺纹/纯数值) │ ├─ findLeadingSymbolGroup():搜索独立Φ TextGroup │ ├─ inferDiameterFromContext():配合公差启发式 │ └─ findNearbyTolerance():搜索附近公差文本 ↓ DimensionResult 列表 ← 最终输出 ``` --- ## 四、乱码映射表 ### 4.1 correctGarbledText()(PositionedTextStripper) | 原始乱码 | 映射结果 | 说明 | |---------|---------|------| | `¡¤` (U+00A1 U+00A4) | Φ (U+03A6) | BerlinSansFB-Bold 字体直径符号 | | `¡ã` (U+00A1 U+00E3) | ° (U+00B0) | 度数符号 | | `¡À` (U+00A1 U+00C0) | ± (U+00B1) | 正负公差 | | `¦µ` (U+00A6 U+00B5) | Φ (U+03A6) | 直径符号变体 | | `¡Á` (U+00A1 U+00C1) | ± (U+00B1) | 正负公差变体 | | 单独 `¡` (U+00A1) | Φ (U+03A6) | ¡¤ 跨 writeString 拆分时 | | 单独 `¤` (U+00A4) | 删除 | ¡¤ 跨 writeString 拆分时 | | U+FFFD | 删除 | Unicode 替换字符 | ### 4.2 TextNormalizer.normalizeText() 与 correctGarbledText 类似的映射,加上: - `Φ\u0080` → `Φ`(Windows GBK 尾随控制字符) - `Ø\u0080` → `Ø` - `±\u0080` → `±` - `Æø` (U+00C6 U+00F8) → `Ø` - NFC 规范化 --- ## 五、正则模式优先级 `identifyDimensions()` 中的匹配顺序(先匹配先处理): 1. **PAT_COMPOUND_TOL** — 复合公差文本(如 `+0.03 0`)→ 跳过(不作为尺寸) 2. **PAT_DIM_SYM_TOL** — 对称公差(如 `280 ±0.15`) 3. **PAT_DIM_ASYM_TOL** — 非对称公差/斜线(如 `55 +0.03/-0.02`) 4. **PAT_DIM_LIMIT_TOL** — 非对称公差/空格(如 `55 +0.03 -0.02`) 5. **PAT_DIM_FIT** — 配合公差(如 `230 m6`、`280 H7`) 6. **PAT_THREAD** — 螺纹标注(如 `M24×1.5`) 7. **PAT_PLAIN_DIM** — 纯尺寸数值(如 `270`、`R15`) --- ## 六、关键文件 | 文件 | 职责 | |-----|------| | `PositionedTextStripper.java` | PDFBox 文本提取、乱码修复、seqNum 赋值 | | `TextGrouper.java` | 按空间邻近性将 TextElement 分组为 TextGroup | | `TextNormalizer.java` | 二次乱码映射、NFC 规范化 | | `DimensionIdentifier.java` | 尺寸识别核心逻辑(正则 + Φ拼接 + 启发式) | | `TitleBlockFilter.java` | 标题栏区域过滤、GD&T/粗糙度排除 | | `RegionFilterService.java` | 区域框选模式尺寸提取 | | `PdfExtractionService.java` | 提取服务入口 |