AI活用

给无法输入的PDF申请书填上文字的方法:让AI按坐标把文字叠印到没有填写栏的表格上的7个步骤

给无法输入的PDF申请书填上文字的方法:让AI按坐标把文字叠印到没有填写栏的表格上的7个步骤

早上好,我是まさきん。

我想填一份健康保险的给付申请书,结果发现PDF里没法输入文字。它没有填写栏,是所谓的“扁平PDF”。我让AI按坐标把文字叠印上去,做成了可以打印的版本,这里把过程记下来。

※本文为发布时的信息,最新条件请以官方网站为准。

POINT
  • 无法输入的PDF申请书,很多时候是不带填写栏(AcroForm)的“扁平PDF”
  • 工具有3个:用 pdfplumber 取表格线的坐标,用 reportlab 画文字,负责叠加的是 pypdf
  • 坐标原点在 pdfplumber 里是左上角,在 reportlab 里是左下角。上下相反,所以需要转换
  • A4 约为595×842pt 。1pt是1/72英寸,坐标全部按这个单位来考虑
  • 银行账号、亲笔签名、盖章这3处没有叠印,而是打印之后手写

碰上了没有填写栏的PDF

我做的是健康保险的给付申请书,也就是把自己先垫付的医疗费申请退回来的文件。制度的流程写在通过疗养费和医疗费补助拿回垫付医疗费的步骤里。

申请书是以PDF形式发放的。但是打开之后栏位点不了,哪里都没有可以打字的地方。表格一共有2张。

打印出来全部手写也是一个办法。话虽如此,同样的数字和期间要写好几处,还是有点麻烦。写错了就得重新打印。所以我决定,只把能打的部分交给AI来打。

扁平PDF的分辨方法

PDF申请书有2种:一种是嵌入了输入栏的,另一种只是把表格线当作图画画上去的。

Adobe的官方帮助里,把后者称为“flat form”,说明是作者没有添加输入栏的表单。这篇文章里就叫它扁平PDF。

分辨方法很简单。

第3种可以用 pypdf 这个库来确认。只需调用官方文档里的 get_fields()。没有输入栏的话会返回 None。

from pypdf import PdfReader

reader = PdfReader("form.pdf")
print(reader.get_fields())  # 没有输入栏时为 None

我的申请书就是在这一步知道没有输入栏的。如果是有输入栏的PDF,就用不着这篇文章的方法了。正常地往栏里输入更可靠。

用到的工具有3个

让AI使用的工具,都是免费的Python库。分工很清晰。

工具作用这次用到的功能
pdfplumber读取原PDF取表格线和矩形的坐标,把页面转成图片
reportlab画出新的PDF在透明的纸上只放文字
pypdf把PDF叠在一起检查有没有输入栏,把文字那张纸叠到表格上

可以想象成一张透明胶片。在申请书上面放一张透明胶片,只在上面印上文字,再把两张贴合在一起。做的事情就只有这些。

原来的PDF本身并没有被改写。所以位置偏了的话,只重做透明胶片就行。我觉得这一点让人很轻松。

PDF的单位是点

坐标的单位是点(pt)。reportlab 的官方文档里写着,1pt是1/72英寸。

A4是210mm×297mm,换算成点 约为595×842pt 。我在手边把 reportlab 的A4常量打印出来,是595.28×841.89。

也就是说1mm约为2.83pt。栏的高度如果是8mm,大约就是23pt。把这个换算记在脑子里,就能对AI给出的数字有个大致判断。

步骤的全貌

流程分7个阶段。即使不是工程师,我想只要能跟上顺序就足够了。

  1. 确认PDF里有没有输入栏
  2. 取出表格线的坐标
  3. 确定放文字的单元格矩形
  4. 制作只载有文字的叠印用PDF
  5. 叠到原PDF上
  6. 转成图片用眼睛确认
  7. 打印并填写手写的部分

我做的是给AI智能体下指示和做确认,代码不是我自己写的。不过,我觉得最好还是知道它在做什么。因为位置偏了的时候,才知道该怀疑哪里。

取表格线的坐标

pdfplumber 会把页面上的图形以列表形式返回。按官方的说明,线是 .lines,矩形是 .rects。还有把矩形拆成4条边再和线合在一起的 .edges。

import pdfplumber

with pdfplumber.open("form.pdf") as pdf:
    page = pdf.pages[0]
    print(page.width, page.height)
    for r in page.rects:
        print(r["x0"], r["x1"], r["top"], r["bottom"])

x0 是左端,x1 是右端,两者都是到页面左端的距离。top 和 bottom 则是到页面上端的距离。

申请书里的表,有时不是用矩形,而是用一组线画出来的。这种时候就看 .lines 和 .edges,从竖线和横线的交点把单元格的矩形拼出来。

这是最不起眼、也最重要的一道工序。只要栏的左端、右端、上端、下端这4个数字定下来,之后就可以机械地往下做了。

制作叠印用的PDF

接下来用 reportlab 制作只有文字的PDF。要输出日文需要注册字体。官方文档里登载的是使用 UnicodeCIDFont 的方法。日文字体列了2种:明朝体的 HeiseiMin-W3 和黑体的 HeiseiKakuGo-W5。

from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont

pdfmetrics.registerFont(UnicodeCIDFont("HeiseiKakuGo-W5"))
c = canvas.Canvas("overlay.pdf", pagesize=A4)
c.setFont("HeiseiKakuGo-W5", 10)

H = A4[1]                  # 页面的高度
x0, x1, bottom = 100, 300, 142   # 用pdfplumber取到的值
y = H - bottom + 6         # 上下翻转后稍微抬高一点
c.drawString(x0 + 4, y, "示例 太郎")       # 左对齐
c.drawRightString(x1 - 4, y, "12,345")   # 右对齐
c.showPage()               # 如果有第2页就接着画
c.save()

姓名和数字当然都是示例。关键是 H - bottom 这一行,它把从上往下的距离换成了从下往上的距离。理由在后面的章节里写。

想把特定的字体嵌入PDF时,也可以用 TTFont,也就是指定手边的字体文件来注册的方法。如果想让打印出来的外观保持一致,选这个也许也是个办法。

叠到原PDF上并用图片确认

最后用 pypdf 把两者叠起来。用的是官方文档里作为“图章”介绍的 merge_page()。

from pypdf import PdfReader, PdfWriter

overlay = PdfReader("overlay.pdf")
writer = PdfWriter(clone_from="form.pdf")
for i, page in enumerate(writer.pages):
    page.merge_page(overlay.pages[i])
writer.write("filled.pdf")

merge_page() 什么都不指定的话,就是叠在上面。加上 over=False 则变成垫在下面。这次是想把文字放在上面,所以保持原样就可以。

做好的PDF不要马上打印,先转成图片看一看。用 pdfplumber 的 to_image(resolution=150) 可以把页面变成PNG。有没有超出栏位,在这一步把所有的栏都看一遍。

调整固定支出,要做的事情也差不多:把数字拿出来,用眼睛确认,再修正偏差。我想通信费按这个顺序来看也比较容易推进。

无需签约也能获得100积分的资费模拟 手机话费也一边用数字确认,一边用乐天移动重新调整

点击后会跳转到乐天登录页面,登录后即可看到活动内容。

坐标系是上下相反的

这项工作里最容易出错的,是坐标的方向。我把在官方文档里确认过的内容做成了表。

项目pdfplumberreportlab
原点页面的左上角页面的左下角
横向x0 x1 是到左端的距离x 向右增大
纵向top bottom 是到上端的距离y 向上增大
单位点点(1/72英寸)
转换直接读取y = 页面高度 − 从上往下的距离

pdfplumber 里还有 y0 和 y1 这两个值。它们是到页面下端的距离。所以用 y0 的话,不用做减法就能交给 reportlab。

不过,把 top 和 y0 混着用会乱。一开始就定好用哪一个,并且也告诉AI,这样应该比较好。比如干脆定成“按从上往下的距离来读,画的时候再翻转”。

注意
  • 如果文字出现在纸面上下相反的一侧,很可能是忘了翻转坐标
  • 横向两边都以左端为基准,可以直接使用
  • 纸张高度不要写死,用从原PDF读出来的值更放心

偏过一次的原因是读错了右端

其实文字偏过一次。原因出在某个填写栏右端的坐标上。AI把它读成了比正确位置更靠右。

单元格的矩形是放文字的基准。右端的数字不对,以它为基准的文字也会跟着动。这种偏差,把叠加的结果转成图片一看就能发现。

修正的办法很简单。把表格线的坐标重新读一遍,把单元格的右端换成正确的值,然后重新制作叠印用的PDF。

一般来说,申请书里的表格线条交错复杂。把相邻栏的线或外框的线错当成目标栏的边,是有可能发生的。所以坐标并不是“取到了就结束”。我认为它和用图片确认这道工序是成套的。

留给手写的3处

并不是全部都叠印了。下面这3处,我特意留了空。

栏是否叠印理由
姓名、期间、金额等栏叠印了打错可以在屏幕上改
银行账号没有叠印不想留在文件里
亲笔签名没有叠印因为是本人书写的栏
盖章没有叠印因为是打印之后才盖的

亲笔签名栏由本人亲手书写,这是一般的想法。印上去的名字不能代替亲笔签名。盖章也一样,要印到纸上之后再盖。

银行账号这一项,与其说是规则,不如说是我自己的判断。交给AI的数据里也好,生成的PDF里也好,我都不想留下它。数字不多,手写的负担也很小。

还有一个判断。贴着“严禁拆封”封条的文件,我没有打开就直接随信寄出,并在相应的栏里写了“如随附文件所示”。

CHECK
  • 哪些栏可以打印、哪些栏需要亲笔签名,要按申请对象的填写说明来确认
  • 拿不准的话,先问过申请对象再打印
  • 更正的方法也要遵照申请对象的规定

填写的值要和原件核对

交给AI来做,注意力很容易被位置对齐吸引过去。但是申请书里真正重要的,是内容里的数字。

我把填写的值和收据、明细单的扫描件做了对照,一项一项地确认金额和期间是否一致。这一步我把它留作由人眼来看的工序。

核对的思路和让AI用会计SaaS和邮件核对副业经费的那篇是一样的:有两个来源就互相比对。申请书也不例外。

把纸集中起来扫描的安排,练习题的批量扫描与分拣那篇文章也许可以参考。

个人信息的处理

申请书里装满了个人信息。让AI干活的时候,我觉得最好先定好交出去的范围。

尤其最后一点很容易忘。确认用的图片上,填好的内容原样都在。不要一直留在工作文件夹里才比较安心。

这篇文章里,我也没有写健康保险的名称和金额。写亲身经历的时候也好,筛选交出去的数据的时候也好,我认为思路是一样的。

也有不写代码的方法

读到这里,也许有人会觉得“我不会做到这个地步”。这种情况下,Adobe Acrobat 的“填写和签名”可以作为候选。

Adobe的官方帮助里,登载了往扁平表单里输入的方法,也就是使用 Acrobat Reader 的 Fill & Sign 工具。可以在表单的任意位置放置文字和符号。因为是用鼠标定位置,所以不需要考虑坐标。

表格只有1〜2张,要写的栏也不多。那样的话我觉得这个方法更快。让AI按坐标来打的方法,是在栏很多的时候才见效。要反复制作同一种格式的时候,应该也比较适合吧。

适合的人·不适合的人

我想适合的是下面这样的人。

另一方面,不适合的是这些情况。

即使交给AI,确认仍然是留给人的工作。如果要省掉这一步,手写也许更安全。

常见问题

Q. 怎么分辨是不是无法输入的PDF?

点击栏位不出现光标的话,很可能是没有输入栏的PDF。可靠的做法是用 pypdf 的 get_fields() 来查。没有输入栏的话会返回 None。

Q. 不会编程也能用吗?

前提是让AI智能体来写代码。我自己也没有写。不过,图片确认和原件核对需要自己来做。

Q. 为什么文字会出现在上下颠倒的位置?

因为坐标的原点不同。pdfplumber 的原点在左上角,reportlab 的原点在左下角。用页面高度减去从上往下的距离,位置就对了。

Q. 日文出现乱码,或者显示不出来。

有可能是没有注册日文字体。在 reportlab 里可以用 UnicodeCIDFont 注册日文字体。要用手边的字体的话就是 TTFont。

Q. 扫描的纸质申请书也能用同样的方法吗?

扫描的PDF,很多时候整页是一张图片。这种情况下表格线并不是作为图形存在的,所以用 pdfplumber 取不到坐标。我想需要另想办法,从图片上测量栏的位置。

Q. 亲笔签名栏和盖章栏也可以打印吗?

亲笔签名栏由本人亲手书写,这是一般的想法。盖章也是在打印之后进行。具体怎么处理因申请对象而异,请确认一下填写说明。

Q. 打印出来位置偏了。

原因可能是打印时的缩放。如果设置成按纸张大小缩小,整体会稍微变小。这次是把原表格和文字叠成了一个PDF,所以表格线和文字会一起移动。把倍率设成实际大小会比较放心。

Q. 原来的PDF会被改写吗?

不会。是读取原PDF,把叠加的结果用另一个名字保存。位置偏了的话,只重做叠印用的PDF就行。

参考的官方信息

总结

即使是没有填写栏的PDF,只要能取到表格线的坐标,就能把文字叠上去。绊住我的是读错了坐标。重新读一遍就能修好,所以用图片确认这道工序看来最好不要省。

申请书是邮寄出去的。能打的地方用打的,该手写的地方用手写。先把这条界线定好,做起来应该会从容一些吧。

手续上的工夫省下来之后,家庭开支的其他调整也能稍微顾得上了。像通信费这样每个月的固定支出,顺着这个势头看一次也许不错。

无需签约也能获得100积分的资费模拟 办手续的同时,顺便用乐天移动重新看看手机话费

点击后会跳转到乐天登录页面,登录后即可看到活动内容。

本文包含联盟营销广告。如果您通过本站链接申请相关商品或服务,我们可能会从合作企业获得报酬。此外,运营者是乐天集团的员工,也可能通过员工推荐计划获得报酬。文章内容与评价均基于运营者的真实体验与调查完成,与是否存在广告无关,但请您在阅读前了解上述关系。详情请参阅免责声明与联盟营销说明。 免责声明与联盟营销说明

本文包含机器翻译内容。正式条件请以乐天移动官方网站的多语言页面为准。

如果您对乐天移动的通信区域·信号状况有疑问,可以通过官方的 电波改善调查申请表 进行咨询。

ABOUT THE AUTHOR
まさきん

乐天集团员工·数字营销人(持有FP资格)

40岁出头,双职工家庭、四个孩子的父亲。在乐天集团担任数字营销工作的同时,也活用理财规划师(FP)资格,专注于家庭理财与资产配置。

查看简介

相关文章

RELATED
Gemini(Google AI Pro)家庭共享最多5名家人|设置步骤与共享范围 AI活用

Gemini(Google AI Pro)家庭共享最多5名家人|设置步骤与共享范围

Google AI Pro除管理员外最多可与5名家人共享。Gemini App和存储空间可以共享,YouTube Premium Lite仅限管理员,Flow的上限由全家分摊。本文根据官方帮助整理了设置步骤和儿童账号的处理方式。

2026.10.13 · 阅读约9分钟
AI订阅扣款日与记账对不上的3个原因|Claude升级后扣款日会改变 AI活用

AI订阅扣款日与记账对不上的3个原因|Claude升级后扣款日会改变

Claude在月中升级到Max时,计费周期会重置,当天成为新的扣款日。美元换算日元的汇率在数据到达发卡公司时确定,账单和记账App的同步也会晚几天。本文整理了以收据邮件为准来确定月份的5个步骤。

2026.10.13 · 阅读约10分钟
把freee会计的未处理明细改成每月一次的核销班次:每月5日7:30、分3类、最多15条交给人处理的设计 AI活用

把freee会计的未处理明细改成每月一次的核销班次:每月5日7:30、分3类、最多15条交给人处理的设计

freee会计里有大约18条未处理明细,连续6次每周检查都原封不动地留着。这里整理一下每月5日早上7:30在家里的Mac上运行的核销班次的设计,以及“能确定的部分每月合并成1个任务、拿不准的明细逐条交给人、上限15条”的界线。

2026.10.12 · 阅读约11分钟
转到乐天移动,最高可获14,000积分 无需签约也能获得100积分的资费模拟 →
立即申请 →