# 可填写字段 如果 PDF 含有可填写的表单字段: - 在此文件所在目录下运行以下脚本:`python scripts/extract_form_field_info.py `。该脚本会生成一个 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 ` 然后分析图片,确定每个表单字段的用途(注意将边界框的 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 ` 该脚本会验证你提供的字段 ID 和值是否有效;如果打印了错误信息,请修正相应字段后重试。 # 非可填写字段 如果 PDF 没有可填写的表单字段,你需要添加文本注释。首先尝试从 PDF 结构中提取坐标(更精确),如有需要再退回到视觉估算。 ## 第一步:优先尝试结构提取 运行以下脚本,提取文本标签、线条和复选框及其精确的 PDF 坐标: `python scripts/extract_form_structure.py 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 ` ### B.2:初步字段识别 检查每页图片,识别表单区域并获取字段位置的**大致估算**: - 表单字段标签及其大致位置 - 填写区域(用于文本输入的线条、方框或空白区域) - 复选框及其大致位置 对于每个字段,记下近似的像素坐标(暂不需要精确)。 ### B.3:缩放精确定位(对精度至关重要) 对于每个字段,围绕估算位置裁剪出一个区域,以精确确定坐标。 **使用 ImageMagick 创建缩放裁剪图:** ```bash magick -crop x++ +repage ``` 其中: - `, ` = 裁剪区域左上角(使用你的大致估算值减去边距) - `, ` = 裁剪区域的大小(字段区域加上每边约 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 fields.json ` ## 第四步:验证输出 将填写后的 PDF 转换为图片,验证文本位置: `python scripts/convert_pdf_to_images.py ` 如果文本位置不正确: - **方法 A**:确认你使用了 form_structure.json 中的 PDF 坐标并配合 `pdf_width`/`pdf_height` - **方法 B**:确认图片尺寸匹配且坐标是精确的像素值 - **混合方法**:确保视觉估算字段的坐标转换正确