Files
2026-07-13 21:35:39 +08:00

292 lines
11 KiB
Markdown
Raw Permalink 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 含有可填写的表单字段:
- 在此文件所在目录下运行以下脚本:`python scripts/extract_form_field_info.py <input.pdf> <field_info.json>`。该脚本会生成一个 JSON 文件,其中包含字段列表,格式如下:
```
[
{
"field_id": (字段的唯一 ID),
"page": (页码,从 1 开始),
"rect": ([左, 下, 右, 上] 边界框,使用 PDF 坐标,y=0 为页面底部),
"type": ("text"(文本)、"checkbox"(复选框)、"radio_group"(单选组)或 "choice"(下拉选择)),
},
// 复选框包含 "checked_value" 和 "unchecked_value" 属性:
{
"field_id": (字段的唯一 ID),
"page": (页码,从 1 开始),
"type": "checkbox",
"checked_value": (将该字段设为此值即可勾选复选框),
"unchecked_value": (将该字段设为此值即可取消勾选复选框),
},
// 单选组包含一个 "radio_options" 列表,列出所有可选选项:
{
"field_id": (字段的唯一 ID),
"page": (页码,从 1 开始),
"type": "radio_group",
"radio_options": [
{
"value": (将该字段设为此值即可选择该单选选项),
"rect": (该单选按钮的边界框)
},
// 其他单选选项
]
},
// 下拉选择字段包含一个 "choice_options" 列表,列出所有可选选项:
{
"field_id": (字段的唯一 ID),
"page": (页码,从 1 开始),
"type": "choice",
"choice_options": [
{
"value": (将该字段设为此值即可选择该选项),
"text": (选项的显示文本)
},
// 其他下拉选项
],
}
]
```
- 使用以下脚本将 PDF 转换为 PNG 图片(每页一张)(在此文件所在目录下运行):
`python scripts/convert_pdf_to_images.py <file.pdf> <output_directory>`
然后分析图片,确定每个表单字段的用途(注意将边界框的 PDF 坐标转换为图片坐标)。
- 创建一个 `field_values.json` 文件,格式如下,包含每个字段要填写的值:
```
[
{
"field_id": "last_name", // 必须与 `extract_form_field_info.py` 中的 field_id 一致
"description": "用户的姓氏",
"page": 1, // 必须与 field_info.json 中的 "page" 值一致
"value": "Simpson"
},
{
"field_id": "Checkbox12",
"description": "若用户年满 18 岁则勾选的复选框",
"page": 1,
"value": "/On" // 如果是复选框,使用其 "checked_value" 进行勾选;如果是单选按钮组,使用 "radio_options" 中的某个 "value" 值
},
// 更多字段
]
```
- 在此文件所在目录下运行 `fill_fillable_fields.py` 脚本,生成已填写的 PDF
`python scripts/fill_fillable_fields.py <input pdf> <field_values.json> <output pdf>`
该脚本会验证你提供的字段 ID 和值是否有效;如果打印了错误信息,请修正相应字段后重试。
# 非可填写字段
如果 PDF 没有可填写的表单字段,你需要添加文本注释。首先尝试从 PDF 结构中提取坐标(更精确),如有需要再退回到视觉估算。
## 第一步:优先尝试结构提取
运行以下脚本,提取文本标签、线条和复选框及其精确的 PDF 坐标:
`python scripts/extract_form_structure.py <input.pdf> form_structure.json`
这会生成一个 JSON 文件,包含:
- **labels**(标签):每个文本元素及其精确坐标(以 PDF 点为单位:x0, top, x1, bottom
- **lines**(线条):定义行边界的水平线
- **checkboxes**(复选框):作为复选框的小正方形矩形(包含中心坐标)
- **row_boundaries**(行边界):根据水平线计算出的行顶部/底部位置
**检查结果**:如果 `form_structure.json` 包含有意义的标签(与表单字段对应的文本元素),则使用 **方法 A:基于结构的坐标**。如果 PDF 是扫描件或基于图片,几乎没有标签,则使用 **方法 B:视觉估算**
---
## 方法 A:基于结构的坐标(推荐)
`extract_form_structure.py` 在 PDF 中找到了文本标签时使用此方法。
### A.1:分析结构
读取 form_structure.json 并识别:
1. **标签组**:相邻的文本元素构成一个完整的标签(例如,"Last" + "Name"
2. **行结构**:具有相近 `top` 值的标签位于同一行
3. **字段列**:填写区域从标签结束之后开始(x0 = 标签.x1 + 间距)
4. **复选框**:直接从结构中获取复选框坐标
**坐标系统**:PDF 坐标中 y=0 位于页面顶部,y 值向下递增。
### A.2:检查缺失元素
结构提取可能无法检测到所有表单元素。常见情况:
- **圆形复选框**:只有正方形矩形会被检测为复选框
- **复杂图形**:装饰性元素或非标准表单控件
- **褪色或浅色元素**:可能无法被提取
如果你在 PDF 图片中看到了 form_structure.json 中未包含的表单字段,你需要对这些特定字段使用 **视觉分析**(见下方的「混合方法」)。
### A.3:使用 PDF 坐标创建 fields.json
对于每个字段,根据提取的结构计算填写坐标:
**文本字段:**
- 填写区域 x0 = 标签 x1 + 5(标签后留出小间距)
- 填写区域 x1 = 下一个标签的 x0,或行边界
- 填写区域 top = 与标签 top 相同
- 填写区域 bottom = 下方行边界线,或标签 bottom + 行高
**复选框:**
- 直接使用 form_structure.json 中的复选框矩形坐标
- entry_bounding_box = [checkbox.x0, checkbox.top, checkbox.x1, checkbox.bottom]
使用 `pdf_width``pdf_height`(表示 PDF 坐标)创建 fields.json
```json
{
"pages": [
{"page_number": 1, "pdf_width": 612, "pdf_height": 792}
],
"form_fields": [
{
"page_number": 1,
"description": "姓氏填写字段",
"field_label": "Last Name",
"label_bounding_box": [43, 63, 87, 73],
"entry_bounding_box": [92, 63, 260, 79],
"entry_text": {"text": "Smith", "font_size": 10}
},
{
"page_number": 1,
"description": "美国公民「是」复选框",
"field_label": "Yes",
"label_bounding_box": [260, 200, 280, 210],
"entry_bounding_box": [285, 197, 292, 205],
"entry_text": {"text": "X"}
}
]
}
```
**重要**:使用 form_structure.json 中的 `pdf_width`/`pdf_height` 及坐标。
### A.4:验证边界框
在填写之前,检查你的边界框是否有错误:
`python scripts/check_bounding_boxes.py fields.json`
该脚本会检查边界框是否存在相交,以及填写区域是否因字号过大而空间不足。修复所有报告的错误后再进行填写。
---
## 方法 B:视觉估算(备用方法)
当 PDF 是扫描件或基于图片,结构提取未能找到可用的文本标签(例如所有文本都显示为 "(cid:X)" 模式)时使用此方法。
### B.1:将 PDF 转换为图片
`python scripts/convert_pdf_to_images.py <input.pdf> <images_dir/>`
### B.2:初步字段识别
检查每页图片,识别表单区域并获取字段位置的**大致估算**:
- 表单字段标签及其大致位置
- 填写区域(用于文本输入的线条、方框或空白区域)
- 复选框及其大致位置
对于每个字段,记下近似的像素坐标(暂不需要精确)。
### B.3:缩放精确定位(对精度至关重要)
对于每个字段,围绕估算位置裁剪出一个区域,以精确确定坐标。
**使用 ImageMagick 创建缩放裁剪图:**
```bash
magick <page_image> -crop <width>x<height>+<x>+<y> +repage <crop_output.png>
```
其中:
- `<x>, <y>` = 裁剪区域左上角(使用你的大致估算值减去边距)
- `<width>, <height>` = 裁剪区域的大小(字段区域加上每边约 50px 的边距)
**示例:** 精确定位大约在 (100, 150) 位置的「姓名」字段:
```bash
magick images_dir/page_1.png -crop 300x80+50+120 +repage crops/name_field.png
```
(注意:如果 `magick` 命令不可用,可尝试使用相同参数的 `convert`。)
**检查裁剪后的图片**,确定精确坐标:
1. 确定填写区域开始的精确像素位置(标签之后)
2. 确定填写区域结束的位置(下一个字段或边缘之前)
3. 确定填写线条/方框的顶部和底部位置
**将裁剪坐标转换回完整图片坐标:**
- full_x = crop_x + crop_offset_x
- full_y = crop_y + crop_offset_y
示例:如果裁剪从 (50, 120) 开始,填写方框在裁剪图内的坐标为 (52, 18):
- entry_x0 = 52 + 50 = 102
- entry_top = 18 + 120 = 138
**对每个字段重复上述步骤**,尽可能将相邻字段合并到同一次裁剪中。
### B.4:使用精确定位坐标创建 fields.json
使用 `image_width``image_height`(表示图片坐标)创建 fields.json
```json
{
"pages": [
{"page_number": 1, "image_width": 1700, "image_height": 2200}
],
"form_fields": [
{
"page_number": 1,
"description": "姓氏填写字段",
"field_label": "Last Name",
"label_bounding_box": [120, 175, 242, 198],
"entry_bounding_box": [255, 175, 720, 218],
"entry_text": {"text": "Smith", "font_size": 10}
}
]
}
```
**重要**:使用 `image_width`/`image_height` 以及缩放分析中得到的精确像素坐标。
### B.5:验证边界框
在填写之前,检查你的边界框是否有错误:
`python scripts/check_bounding_boxes.py fields.json`
该脚本会检查边界框是否存在相交,以及填写区域是否因字号过大而空间不足。修复所有报告的错误后再进行填写。
---
## 混合方法:结构 + 视觉
当结构提取对大多数字段有效,但遗漏了部分元素(如圆形复选框、非标准表单控件)时使用此方法。
1. 对 form_structure.json 中检测到的字段使用**方法 A**
2. **将 PDF 转换为图片**,用于对缺失字段进行视觉分析
3. 对缺失字段使用**缩放精确定位**(来自方法 B)
4. **合并坐标**:对于从结构提取获得的字段,使用 `pdf_width`/`pdf_height`。对于视觉估算的字段,需要将图片坐标转换为 PDF 坐标:
- pdf_x = image_x * (pdf_width / image_width)
- pdf_y = image_y * (pdf_height / image_height)
5. 在 fields.json 中使用**统一的坐标系统**——将所有坐标转换为 PDF 坐标,配合 `pdf_width`/`pdf_height`
---
## 第二步:填写前验证
**在填写之前务必验证边界框:**
`python scripts/check_bounding_boxes.py fields.json`
该脚本检查:
- 边界框是否相交(会导致文本重叠)
- 填写区域是否因指定字号过小而空间不足
修复 fields.json 中报告的所有错误后再继续。
## 第三步:填写表单
填写脚本会自动检测坐标系统并处理转换:
`python scripts/fill_pdf_form_with_annotations.py <input.pdf> fields.json <output.pdf>`
## 第四步:验证输出
将填写后的 PDF 转换为图片,验证文本位置:
`python scripts/convert_pdf_to_images.py <output.pdf> <verify_images/>`
如果文本位置不正确:
- **方法 A**:确认你使用了 form_structure.json 中的 PDF 坐标并配合 `pdf_width`/`pdf_height`
- **方法 B**:确认图片尺寸匹配且坐标是精确的像素值
- **混合方法**:确保视觉估算字段的坐标转换正确