133 lines
5.5 KiB
Markdown
133 lines
5.5 KiB
Markdown
|
|
# 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` | 提取服务入口 |
|