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

133 lines
5.5 KiB
Markdown
Raw Normal View History

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