PDF对企业应用来说是刚需。
 
然而PDF显然不是一种对机器友好的格式,它只是对人类友好,就是说方便阅读打印,但让程序去提取其中的内容却很难。下面简单说说为什么是这样。
 
以前还读书的时候(20+年前了),一个同学跟我展示了PDF文档,他说这种格式不是普通的文本,它是图片,所以比较大。其实他说的并不完全正确,pdf中可以包含图片,也可以包含文字,且其中的文字跟传统的位图还真是不一样。那么PDF中的内容究竟是什么?
以增值税普通发票的电子档为例,相信大家手头都有,对,就这个:

上面的文字究竟是图还是文本?上面的印章是不是位图?

判断是不是位图很简单,就是用一个PDF阅读器打开,Adobe的或者福昕的都行,选择“Select”工具,看看上面的文字能否选中。你很快就发现,不光上面的楷体字、宋体字能选中,印章里的文字居然也能选中,并且能复制下来,你把复制下来的文本贴到记事本里,这就是普通的文本。

这是不是就说明PDF里的文字就是我们常规理解的文本呢?其实没那么简单,PDF的一个特性就是不管处于怎样的环境,PDF都能显示出完整的、一致的内容。这意味着这张发票放到一个完全不支持中文的系统中,它也能显示出上面的汉字,并且效果完全一直,当然也包含了不同的字体,楷体,宋体,都没问题,这就比较厉害了。
 
其实原理也不难,那就是把字体信息直接嵌入到PDF文件内部。也许你觉得不对,我一个中文宋体的字库就有超过10MB了,而我的PDF才几百K,怎么可能?——要做到这点也不难,只嵌入部分即可。用福昕的PDF阅读器打开这张电子档发票,菜单"File" - "Properties" - "Fonts",你会看到这样的信息:

KaiTi,很明显是楷体,SimSun这是宋体,STSong则是华文宋体,而且编码格式都有,上面的UniGB-UCS2-H是什么编码格式我不太清楚,可能是Unicode,UC应该是Unicode的简写。
 
这里顺便提个事情,就是字体的侵权问题,如果在自己生成的PDF中嵌入字体厂商未授权的字体,很可能会造成侵权!而如果你做了一个网页,网页上指定了用未授权的字体(如微软雅黑)显示一段文字,这样则不会造成侵权,原因在于你并没有在网页上嵌入微软雅黑,你只是告诉用户的浏览器,这段文字建议用微软雅黑显示,用户看到的微软雅黑字体还是用户自己电脑提供的,跟你没有关系,所以不会造成侵权,侵不侵权在于你是否二次“发布”了这个字体。其实要避免侵权也不难,对我们来说,用可免费商用的字体即可,目前还不少,如Google的Noto系列,华为鸿蒙Sans、阿里巴巴普惠体以及小米最近发布的MiSans,这些都是可以免费商用的。
 
字体,本质上来说是矢量图,当然过去也曾经有过点阵字体,但现在基本算是淘汰了,矢量图一个特点是无论你放大多少倍,它都不会失真或出现马赛克,所以一定要我说PDF中保存的文字到底是图还是字,那我就说,那是矢量图,这肯定是正确的,因为如今的字体本质上就是矢量图嘛。
 
而PDF中除了字之外,别的你能看到的很多的元素,其实也都是矢量图,如表格的线条,印章的圆圈这些,要证明也很简单,使劲放大,看看有没有失真出现马赛克或者模糊即可。
 
当然了PDF中还可能有位图,判断方法还是上面提到的,放大来看。
 
PDF的文字的组织形式与我们txt,word或是html都不太一样,举个例子,你打开PDF看到的两个挨在一块的文字,如“力量”,这两个字你觉得它是一个连在一起的词,其实未必,很可能它是通过两个不同的坐标来指定的,从文档结构上来说,你读取“力”字,不意味着它的下一个元素就一定是“量”字,尽管视觉上是这样。这就给我们提取PDF的文本信息带来很大的挑战,我们很难通过直接分析PDF的文档结构提取其中的文字,比较稳妥的方法还是得通过显示的结果来获取内容,是的,就是OCR的路线。但即便要使用OCR的路线,也是问题很多,下面这个例子就反映了一个很典型的问题:

阅读器显示的“μ”和“接”中间的这个符号在我看来很像一个竖线,即“|”,而实际上它却是英文字母“l”,用阅读器复制到记事本中可以证实这点,确实是字母“l”,只不过Windows10种的字母“l”显示得跟数值“1”一模一样,真是令人无语,如果使用OCR的方式,字母“l”很可能就被识别为竖线,或者字母“I”(大写的i),还有就是“μ”这种希腊字母,在OCR中直接被识别成了英文字母“u”,这就很明显有问题了,对于需要精确导入信息的企业应用来说,这显然是不行的。
 
另一个问题也在上图中有体现,就是对空格的理解,福昕阅读器复制出来的内容中包含了一个空格,而别的没有,究竟有没有这个空格,这个很难说,就像我在黑板上写了两个字,间隔远点就算有空格,间隔近了就没有空格,可这个远和近如何界定,这个就比较难办了。
 
我曾经做过一套PDF内容提取的框架,能通过一些配置信息尝试从PDF中提取文本内容,但遇到了很大的麻烦,就如上面提到的那样,两个看上去连贯的文字,读出来的先后顺序是没法保证的,绕来绕去还绕回了OCR的路子,但OCR也是问题不少,总得来说,对于需要精准信息提取的应用来说,真是最好别用PDF了。——但话虽然这么说,客户可不一定能理解,做和不做也不完全由得技术说了算,现实如此也……

PDF的信息提取的问题的更多相关文章

  1. Beta版本发布说明

    发布地址 https://github.com/LongWerLingShi/DataObtainingAndHandling/tree/beta 版本开发背景 首先,应软件工程课程要求,我们小组针对 ...

  2. 数据获取以及处理系统 --- 功能规格说明书V2.0

    产品规格说明书: 版本号: V2.0 版本说明: Version 1.0 简单得需求分析以及构思,初稿形成 Version 2.0 细化beta阶段设计,增加典型用户尝尽以及功能罗列 1. 引言 1. ...

  3. Scrum Meeting 11 -2014.11.17

    今天和其他两个小组讨论了关于整合问题,在数据库连接等具体方面上还需要继续商讨. 我们小组内部讨论了,这周还是需要在处理整合的同时做项目整体的测试与改进的. Member Today’s task Ne ...

  4. Scrum Meeting 10 -2014.11.16

    开始进入大项目的整合阶段,平时和其他两个小组交流较少,整合难度还是存在的. 在具体整合前,让开发人员添加了些必要的注释,优化代码结构,方便阅读. Member Today’s task Next ta ...

  5. Scrum Meeting 9 -2014.11.15

    项目开发测试要进入尾声了.大家加把劲,这周末能整合完成就最好了. 服务器方面已经能运行我们的程序了.还需要研究如何与其他两小组整合. Member Today’s task Next task 林豪森 ...

  6. Scrum Meeting 8 -2014.11.14

    给开发加了个pdf信息提取优化任务. 弄了半天发现服务器也是个好东西.这周末可以和爬虫讨论整合的问题了. Member Today’s task Next task 林豪森 协助测试及服务器部署 协助 ...

  7. Scrum Meeting 7 -2014.11.13

    之前srcum没写好是我的错.以后会每天更新的. 老师反映之前项目小组从pdf中提取作者效果不好,我们讨论决定进行一定的优化.在整合测试的同时开始服务器程序部署. Member Today’s tas ...

  8. 会务准备期间材料准备工作具体实施总结 ----(vim技巧应用, python信息提取与整合, microsoft word格式调整批量化)

    会务准备期间材料准备工作具体实施总结(vim, python, microsoft word) span.kw { color: #007020; font-weight: bold; } code ...

  9. C#给PDF文档添加文本和图片页眉

    页眉常用于显示文档的附加信息,我们可以在页眉中插入文本或者图形,例如,页码.日期.公司徽标.文档标题.文件名或作者名等等.那么我们如何以编程的方式添加页眉呢?今天,这篇文章向大家分享如何使用了免费组件 ...

随机推荐

  1. Linux上用Jexus部署Asp.Net网站:常规部署与Docker部署

    (一)常规部署 一.把 jexus压缩包下载到linux临时文件夹中. cd /tmp wget linuxdot.net/down/jexus-6.2.x-arm64.tar.gz (不同的操作系统 ...

  2. python中的虚拟环境(在jupyter和pycharm中的使用)

    1.通过anaconda新建虚拟环境 创建虚拟环境:conda create -n your_env_name python=3.6 激活虚拟环境:activate your_env_name(虚拟环 ...

  3. 手写IOC实践

    一.IOC 1.什么是IOC? 控制反转(英语:Inversion of Control,缩写为IoC),是[面向对象编程]中的一种设计原则,可以用来减低计算机代码之间的[耦合度]其中最常见的方式叫做 ...

  4. Shell if 参数含义列表

    [ -a FILE ]  如果 FILE 存在则为真.   [ -b FILE ]  如果 FILE 存在且是一个块特殊文件则为真.   [ -c FILE ]  如果 FILE 存在且是一个字特殊文 ...

  5. setsockopt()用法及参数详解

    setsockopt()用法(参数详细说明) int setsockopt(SOCKET s,int level,int optname,const char* optval,int optlen); ...

  6. 解决用creact-react-app新建React项目不支持 mobx装饰器模式导致报错问题 。

    创建react项目 create-react-app mobx-demo cd my-app npm run start 使用react-app-rewired npm install customi ...

  7. Frequency函数(Excel函数集团)

    此处文章均为本妖原创,供下载.学习.探讨! 文章下载源是Office365国内版1Driver,如有链接问题请联系我. 请勿用于商业!谢谢 下载地址:https://officecommunity-m ...

  8. MAVEN基础讲解

    MAVEN解决的问题 1.当我们开始一个工程的时候往往需要几十甚至上百个jar包,如果没有一个管理工具的话,结果就是每个都需要自己手动导入工程目录,并且还有可能发生jar包冲突,版本冲突等问题 2.在 ...

  9. Shell脚本--数值比较组合判断

    用于数值比较的无非大于.小于.等于.大于等于.小于等于这几个. 比较格式: [ 数值1 比较符 数值2 ]   注意左边的括号与数值1之间有一个空格,同样,数值2和右边的括号之间也有空格. 数值比较运 ...

  10. SpringMVC的拦截器不起作用原因

    SpringMVC的拦截器不起作用原因如下图: 这是因为过滤器没有加上这条语句chain.doFilter(request,response);,加上就能进入springMVC的拦截器了,调这个问题整 ...