wms/nflg-qms-pdf-extract/pdf数据提取方案.md

133 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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` | 提取服务入口 |