赵明刚/pdftotext

从PDF文件中提取文本和图像

dev-master 2017-06-23 07:12 UTC

This package is not auto-updated.

Last update: 2024-09-29 03:58:07 UTC


README

PdfToText类被设计用于从PDF文件中提取文本内容。

使用非常简单

include ( 'PdfToText.phpclass' ) ;

$pdf 	=  new PdfToText ( 'sample.pdf' ) ;
echo $pdf -> Text ; 		// or you could also write : echo ( string ) $pdf ;

相同的PdfToText对象可以重复使用来处理其他文件

$pdf -> Load ( 'sample2.pdf' ) ;
echo $pdf -> Text ;

此外,PdfToText类还提供了支持方法,用于获取底层PDF文件中任何文本的页码。

请查看类的博客,了解提取PDF文件文本内容所涉及的底层机制概述。

examples/目录中提供了示例。请参阅examples/README.md文件,了解其结构的简要说明。

重要PdfToText类生成UTF8编码的文本。如果您的默认字符集不是UTF-8,您可能需要将以下meta标签添加到HTML页面的<head>部分

<meta charset="utf-8" />

特性

PDF文件中的文本渲染使用一种晦涩的语言,它提供了多种方法在页面上相同位置定位相同的文本。例如

. Goto coordinates (x,y)
. Render text ( "Mail : someone@somewhere.com" )

或者

. Goto next line
. Goto (x1,y)
. Render text ( "Mail" )
. Goto (x2, y) 
. Render text ( ":" )
. Goto (x3, y)
. Render text ( "someone@somewhere.com" )

(注意,这里我使用的是伪语言)。这两段代码可能会以非常不同的方式显示相同的文本在相同的位置。

这就是为什么PdfToText类跟踪以下信息,以提供更准确的文章渲染(即使输出是纯文本)

  • 跟踪当前选择的字体。这很重要,因为

    • PDF文件中的每个字体都可以有自己的字符映射。这意味着在这种情况下,使用Adobe语言绘制的字符不指定实际的字符代码,而是字体字符映射的索引。
    • 记住当前字体的大小;这有助于评估当使用相对定位指令(如“转到下一行”)时当前的y坐标。虽然近似,但在大多数情况下都有效
  • 如果使用相同的y坐标渲染多个字符串,它们将组合在同一行上。请注意,它们必须在指令流中依次出现,以便此技巧生效

  • 下标/上标文本通常在比其出现的行稍高的y坐标处书写。这种情况被检测到,下标/上标文本将正确地出现在同一行上

如果指定了PDFOPT_BASIC_LAYOUT选项,则不会出现这些症状。

高级特性

该类能够

  • 使用PDFOPT_BASIC_LAYOUT选项渲染基本的页面布局(即,文本的绘制顺序与Acrobat Reader相同)。
  • 使用GetFormData()方法检索表单数据作为独立对象。
  • 捕获页面中的文本区域。

已知问题

以下是关于PdfToText类的已知问题列表;我正在努力解决这些问题,所以我希望这个段落很快就会完全消失!

  • 文本行中可能会出现不希望的换行。这是由于PDF文件包含使用相对定位的绘图指令。这对于使用像PdfCreator这样的生成器创建的文件尤其如此。但是,已采取一些措施来尝试将具有大致相同y坐标的文本跟踪到同一行。如果指定了PDFOPT_BASIC_LAYOUT选项,则此限制不适用。
  • 不支持加密的PDF文件

给Windows用户的注意事项

Linux平台上的Apache服务器为其线程分配默认的堆栈大小为8Mb。在Windows平台上,此值设置为1Mb。

然而,某些由《PdfToText》类使用的正则表达式在处理某些PDF文件时,可能使PHP PCRE扩展需要比1Mb更多的堆栈空间。

这种情况会导致您的Windows Apache服务器崩溃,并且浏览器会显示类似“连接重置”的消息。这种情况影响EasyPHP、XAMPP或Wamp等几个产品。

要解决这个问题,您必须在您的httpd.conf文件中启用mpm模块并定义一个新的堆栈大小,如下面的示例所示,适用于Wamp服务器

	Include conf/extra/httpd-mpm.conf
	ThreadStackSize 8388608

测试

我已经对这个类与来自各种来源的数十个文档进行了测试,并测试了每个样本文档由《PdfCreator》、《PrimoPdf》和《PDF Pro》工具生成的输出。

我还将《PdfToText》类的输出与选择“另存为...文本”选项时的《Acrobat Reader》输出进行了比较。在许多情况下,该类在定位最终文本方面比《Acrobat Reader》表现得更好。

然而,所有这些都不能保证它在每种情况下都能正常工作;因此,如果您在使用《PdfToText》类时发现任何奇怪或不正常的情况,请随时在此类的博客上联系我,并发送样本PDF文件至以下电子邮件地址

	christian.vigh@wuthering-bytes.com

其他链接

此类也可以在此找到

http://www.phpclasses.org/package/9732-PHP-Extract-text-contents-from-PDF-files.html

以及在此处,您将找到FAQ部分,并能够上传您的PDF文件样本以进行实时测试

http://www.pdftotext.eu

以及在此处

https://github.com/christian-vigh-phpclasses/PdfToText

参考

方法

构造函数

$pdf 	=  new PdfToText ( $filename = null, $options = self::PDFOPT_NONE, $user\_password = false, $owner\_password = false ) ;

实例化一个《PdfToText》对象。如果指定了文件名,其文本内容将被加载并可在《Text》属性中找到(否则,您必须调用《Load()`》方法)。

有关《$options》参数的说明,请参阅《Options》属性。

《$user_password》和《$owner_password》参数指定用于解密受密码保护的文件的用户/所有者密码(请注意,此类不是密码破解器!)。

在当前版本中,尚不支持受密码保护的文件的解密。

Load ( $filename, $user_password = false, $owner_password = false )

加载指定文件名的文本内容。

《$user_password》和《$owner_password》参数指定用于解密受密码保护的文件的用户/所有者密码(请注意,此类不是密码破解器!)。

在当前版本中,尚不支持受密码保护的文件的解密。

此方法返回解码后的文本内容,这些内容也通过《Text》属性提供。

LoadFromString ( $contents, $user_password = false, $owner_password = false )

加载指定PDF内容的文本内容。

《$user_password》和《$owner_password》参数指定用于解密受密码保护的文件的用户/所有者密码(请注意,此类不是密码破解器!)。

在当前版本中,尚不支持受密码保护的文件的解密。

此方法返回解码后的文本内容,这些内容也通过《Text》属性提供。

AddAdobeExtraMappings ( $mappings )

Adobe支持4种预定义字体:标准、Mac、WinAnsi和PDF。这些字体中的所有字符都通过一个字符时间标识,有点像HTML实体;例如,'one'将是字符'1','acircumflex'将是'â'等。

Adobe定义了成千上万的字符名称(请参阅https://mupdf.com/docs/browse/source/pdf/pdf-glyphlist.h.html)。

其中一些不在列表中;例如,'ax'字符名称的情况,其中'x'是一个十进制数。当此类字符在/Differences数组中指定时,则存在某个地方的CharProc[]数组,为这些字符中的每一个提供一个对象ID。

引用的对象包含绘制该符号的指令。在没有任何线索的情况下,你无法猜测该符号对应的Unicode字符,因为这些信息不包含在PDF文件中。

AddAdobeExtraMappings() 方法允许你指定这些对应关系。将一个数组指定为 $mappings 参数,其键是Adobe字符名称(例如,“a127”),值是对应的Unicode值。

$mappings 参数是一个关联数组,其键是Adobe字符名称。数组值可以有多种形式

  • 一个字符
  • 一个整数值
  • 最多包含四个字符或整数值的数组。内部,每个指定的值都转换为四个整数的数组,分别对应标准Adobe字符集(标准、Mac、WinAnsi和PDF)。以下规则适用
    • 如果输入值是单个字符,则输出数组与Adobe字符名称相对应的将是包含四个元素的集合,对应于提供的字符的序数值。
    • 如果输入值是整数,则输出数组将是一组四个相同值的集合
    • 如果输入值是数组
    • 小于4个元素的数组将使用最后一个数组项进行填充
    • 大于4个元素的数组将静默截断
    • 每个数组值可以是字符或数值。

注意

在当前实现中,该方法将映射应用于所有Adobe默认字体。也就是说,你无法为PDF文件中引用的一个Adobe字体指定一个映射,然后为第二个Adobe字体指定第二个映射等。

GetCaptures

$captures 	=  $pdf -> GetCaptures ( $full = false ) ;

检索由 PdfToText 类捕获的文本区域。这假设你首先将 PDFOPT_CAPTURE 标志指定给类构造函数,然后调用 SetCaptures()SetCapturesFromString() 方法。

$full 参数设置为 false(默认值)时,返回的对象是一个映射捕获名称到其值的 stdClass 对象的层次结构。

当设置为 true 时,返回的对象是 PdfToTextCaptures 类型,它包含更多信息。这仅适用于对PdfToText类进行内部调试。

注意 :

$full 参数为 false 时,可以像这样访问属性值(这里,我们正在访问第一页中 Title 捕获的值)

$captures -> Title [1] 

当此参数为 true 时,你必须指定 Text 属性以检索其内容

$captures -> Title [1] -> Text

如果你计划在开发阶段在两种返回类型之间切换,你可以使用一种在两种情况下都有效的方法

( string ) $captures -> Title [1]  

有关从PDF文件中捕获文本的更多信息,请参阅 *Capturing Text 部分。

GetFormCount

$count 		=  $pdf -> GetFormCount ( ) ;

返回PDF文件中定义的顶级表单的实际数量。

GetFormData

$object 	=  $pdf -> GetFormData ( $template_xml, $index = 0 ) ;

检索指定顶级表单索引的表单数据。数据以从类 PdfToTextFormData 继承的对象返回,它为其派生类提供了一些辅助函数。

数据检索可以基于模板XML文件,或者当 $template_xml 参数为null时,将创建使用PDF文件中定义的字段名称的默认模板。

有关模板的使用和如何构建表单数据对象的信息,请参阅本文件后面的 表单模板

GetPageFromOffset ( $offset )

给定Text属性中的字节偏移量,返回它在PDF文档中的页码。

页码从1开始。

HasFormData

$status 	=  $pdf -> HasFormData ( ) ;

如果PDF文件包含表单数据,则返回true。

MarkTextLike ( $regex, $mark_start, $mark_end )

有时在只想提取文本的一部分时,可能很有用,比如:“我想提取这个标题和这个标题之间的文本”。MarkTextLike()方法提供了一些支持此类任务的功能。想象一下,你有具有相同结构的文档,所有文档都以“引言”标题开始

Introduction
	...
	some text
	...
Some other title
	...

通过调用MarkTextLike()方法,例如以下示例中所示

$pdf -> MarkTextLike ( '/\bIntroduction\b/', '<M>', '</M' ) ;

然后你会得到以下输出

<M>Introduction</M>
	...
	some text
	...
<M>Some other title</M>

在输出中添加这样的标记,将允许您使用正则表达式轻松提取“引言”和“其他标题”之间的文本。

用于匹配指定正则表达式的第一个字符串的字体名称将在以后搜索以添加使用该字体标记的所有文本部分。

参数如下

  • $regex (字符串) : 匹配要匹配的文本的正则表达式。使用相同字体的后续文本部分将被标记的开始/结束字符串包围。

  • $marker_start$marker_end (字符串) : 当找到匹配时,用于包围字符串的标记。

SetCaptures

$pdf -> SetCaptures ( $xml_file ) ;

从指定的XML文件中加载捕获定义。

请注意,您必须使用PDFOPT_CAPTURE标志实例化PdfToText类,以便此函数正常工作。

有关从PDF文件中捕获文本的更多信息,请参阅 *Capturing Text 部分。

SetCapturesFromString

$pdf -> SetCaptures ( $xml_data ) ;

SetCaptures()方法相同,但加载捕获定义是从字符串而不是文件中。

请注意,您必须使用PDFOPT_CAPTURE标志实例化PdfToText类,以便此函数正常工作。

有关从PDF文件中捕获文本的更多信息,请参阅 *Capturing Text 部分。

text_strpos、text_stripos

$result		=  $pdf -> text_strpos  ( $search, $start = 0 ) ;
$result		=  $pdf -> text_stripos ( $search, $start = 0 ) ;

这些方法的行为类似于PHP中的strpos/stripos函数,除了

  • 它们在PDF文件的文本内容上操作(Text属性)
  • 它们返回一个包含页码和文本偏移的数组。$result [0]将设置为搜索文本的页码,$result [1]为其在Text属性中的偏移量。

参数如下

  • $search (字符串) : 要搜索的字符串。
  • $start (整数) : PDF文本内容中的起始偏移量。

如果找到搜索字符串,则该方法返回包含页码和文本偏移的两个值的数组,否则返回false

document_strpos、document_stripos

$result		=  $pdf -> document_strpos  ( $search, $group_by_page = false ) ;
$result		=  $pdf -> document_stripos ( $search, $group_by_page = false ) ;

在PDF文档中搜索给定字符串的所有出现。$group_by_page参数的值确定如何返回结果

  • 当为true时,返回值将是一个关联数组,其键将是页码,值是页内找到的字符串的偏移量数组
  • 当为false时,返回值将是一个数组数组,包含两个条目:页码和文本偏移量。

例如,如果PDF文档在页面1中字符偏移量100和200处包含字符串"here",在页面3中位置157处,则返回值将是

  • $group_by_page为false时

      [ [ 1, 100 ], [ 1, 200 ], [ 3, 157 ] ]
    
  • $group_by_page为true时

      [ 1 => [ 100, 200 ], 3 => [ 157 ]
    

参数如下

  • $search (字符串) : 要搜索的字符串。

  • *$group_by_page (布尔值) : 指示是否应按页码分组找到的偏移量。

该方法返回包含页码/字符偏移量的数组,如果指定的字符串未出现在文档中,则返回false

text_match、document_match

$status		=  $pdf -> text_match ( $pattern, &$match = null, $flags = 0, $offset = 0 ) ;
$status		=  $pdf -> document_match ( $pattern, &$match = null, $flags = 0, $offset = 0 ) ;

text_match()在PDF文本内容上调用preg_match() PHP函数,以定位与指定正则表达式匹配的文本的第一个出现。

document_match()调用preg_match_all()函数,以定位所有与指定正则表达式匹配的出现。

请注意,这两种方法在调用preg_match/preg_match_all时都会添加PREG_OFFSET_CAPTURE标志,因此您应该知道所有捕获的结果都是一个包含以下条目的数组

  • 条目[0]是捕获的字符串
  • 条目[1]是其文本偏移量
  • text_match()document_match()方法添加一个额外的数组项(索引2),它包含找到匹配文本的页码

参数如下

  • $pattern (字符串) : 要搜索的正则表达式。

  • $match (任何) : 输出捕获。请参阅preg_match/preg_match_all。

  • $flags (整数) : PCRE标志。请参阅preg_match/preg_match_all。

  • $offset (整数) : 起始偏移量。请参阅preg_match/preg_match_all。

与它们的PHP对应方法一样,这些方法返回匹配出现次数,如果指定的正则表达式无效,则返回false

属性

本节描述了在PdfTText对象中可用的属性。请注意,它们应被视为只读。

Author

作者姓名,如PDF文件中所记录。

自动保存的图片文件

当指定了PDFOPT_AUTOSAVE_IMAGES标志时,这个字符串数组将包含自动保存图片存放的文件名。

块分隔符

用于分隔文本块的字符串。主要目的是为了处理以表格形式显示的数据,以确保列内容不会被连接。然而,这并不在所有情况下都适用。

默认值是空字符串。

当指定了PDFOPT_BASIC_LAYOUT选项时,此属性用于分隔同一行上视觉上位于不同x轴的文本部分。在这种情况下,默认分隔符将是空白。

CID表目录

包含CID映射表的目录的路径。

创建日期

包含文档创建日期的字符串,格式为UTC。该值可以用作strtotime() PHP函数的参数。

创建者应用程序

用于创建原始文档的应用程序。

文档起始偏移量

PDF文档通常以以下形式的字符串开始:

%PDF-a.b[.c.d...]

其中“a”和“b”(以及随后的“c”,“d”...)代表PDF文件的版本号。

一些PDF文档可能在开头带有垃圾数据;这是“非法”的,当然,但Acrobat Reader能够处理这种情况。同样,PdfToText类也能处理...

此属性保存输入文件中找到起始“%PDF”字符串的字节偏移量。

加密算法

用于密码保护文件的算法。

Adobe文档中说明:

指定用于加密和解密文档的算法的代码

  • 0:一个未记录且不再受支持的备用算法,使用该算法被强烈反对。
  • 1:算法3.1。
  • 2 [PDF 1.4]:算法3.1,但允许密钥长度超过40位。
  • 3 [PDF 1.4]:一个未发表的算法,允许密钥长度最多为128位。此算法未发表,因为它是美国商务部出口要求的一部分。

加密算法修订版

解释此字典所需的标准安全处理程序的修订号。修订号是

  • 2:对于不需要PDF 1.4新加密功能的文档,这意味着加密算法值为1的文档,并使用EncryptionFlags位1-6。
  • 3:对于需要PDF 1.4新加密功能的文档,这意味着加密算法值为2或更高,或使用扩展的EncryptionFlags位17-21。

加密标志

一组PDFPERM_*常量,描述了在密码保护的PDF文件上允许执行哪些操作。

加密密钥长度

仅在EncryptionAlgorithm为2或3时定义。用于加密和解密的密钥长度,以位为单位。大小是8的倍数,最小值为40,最大值为128。

加密模式

一个PDFCRYPT_*常量。

如果PDF文件没有密码保护,则此值设置为*PDFCRYPT_NONE。

加密元数据

来自密码保护文件的标志,表示文档元数据是否也已加密。

EOL

用于行断开的字符串。默认值是PHP_EOL。

额外文本宽度

此属性以百分比表示;它给出要添加到由PdfTexterFont::GetStringWidth()方法计算出的值的额外百分比。

此值基本上用于计算文本位置和字符串长度时,使用*PDFOPT_BASIC_LAYOUT选项:计算出的字符串长度短于其实际长度(因为字体数据中字符间距等因素确定的额外间距)。

为了确定同一行上连续的两个文本块是否应以空格分隔,该类将经验性地向计算出的字符串长度添加此额外百分比。默认值是-5(百分比)。

文件名

包含已提取文本内容的文件名。如果调用的是 LoadFromString() 方法而不是 Load(),则此值将为空字符串。

ID,ID2

为文档生成的唯一ID对。ID的值用于解密受密码保护的文档。

第二个ID在PDF规范中描述不明确。

ImageAutoSaveFileTemplate

当指定了 PDFOPT_AUTOSAVE_IMAGES 标志时,用于生成文件名的模板字符串。

它可以包含以下类似 print 的格式

  • %p : 被输入PDF文件所在的目录替换。
  • %f : 被输入PDF文件的文件名部分(不带后缀)替换。
  • %s : 被由 ImageAutoSaveFormat 属性给出的图像格式适当的后缀替换。
  • %d : 被从1开始的连续值替换,该值给出了图像编号。

此属性的默认值是

$ImageAutoSaveFileTemplate	=   "%p/%f.%d.%s" ;

使用上述模板,如果您的输入文件名为"/tmp/test.pdf"且包含3个图像,则您将得到以下输出图像

  • /tmp/test.1.jpg
  • /tmp/test.2.jpg
  • /tmp/test.3.jpg

您也可以使用"%d"格式说明符指定宽度;然后,编号将用前导零填充。例如,以下使用与上述示例相同的PDF文件的模板

$pdf -> ImageAutoSaveFileTemplate	=   "%p/%f.%3d.%s" ;

将生成以下文件名

  • /tmp/test.001.jpg
  • /tmp/test.002.jpg
  • /tmp/test.003.jpg

ImageAutoSaveFormat

当指定了 PDFOPT_AUTOSAVE_IMAGES 标志时,表示用于保存PDF文件中找到的图像的格式。它可以是由 gd 库定义的任何图像格式常量。

		IMG_JPEG		=>  'jpg',
		IMG_JPG			=>  'jpg',
		IMG_GIF			=>  'gif',
		IMG_PNG			=>  'png',
		IMG_WBMP		=>  'wbmp',
		IMG_XPM			=>  'xpm'

请注意,常量与相应文件后缀之间的关联将由系统自动处理。

Images

PdfImage 类继承的对象数组。目前,仅实现了 PdfJpegIMage 类。

该类目前支持以下属性

  • ImageResource : 可以与Php imagexxx() 函数一起使用以处理图像内容的一个资源。

以下方法可用

  • SaveAs ( $output_file, $image_type = IMG_JPG ) : 将当前图像保存到指定的输出文件,使用指定的文件格式(预定义的PHP常量之一:IMG_JPG, IMG_GIF, IMG_PNG, IMG_XBMP 和 IMG_XBM)。
  • Output ( ) : 将图像内容输出到标准输出。

目前,存储在专有Adobe格式中的图像不进行处理,也不会出现在此数组中。

请注意,只有当启用PDFOPT_DECODE_IMAGE_DATA时,才会提取图像。

ImageCount

在提供的PDF文件中找到的图像数量。这个数字将只考虑由 PdfToText 类识别的图像格式。

ImageData

包含以下条目的关联数组数组

  • 'type' : 图像类型。可以是以下之一
    • 'jpeg' : Jpeg图像类型。注意,在当前版本中,仅处理jpeg图像。
  • 'data' : 原始图像数据。

请注意,只有当启用PDFOPT_GET_IMAGE_DATA时,才会提取图像数据。

IsEncrypted

如果PDF文件通过某种密码保护方案加密,则此属性设置为 true

Keywords

作者信息部分记录的关键词。

MaxExecutionTime

指定处理单个文件的最大执行时间(以秒为单位)。如果达到此限制,在PHP终止脚本之前,将抛出 PdfToTextTimeoutException 异常。这允许脚本优雅地处理错误,而不是PHP本身。

正数表示秒。负数从 max_execution_time PHP设置中减去以计算允许的最大执行时间。

如果计算的超时值超出范围,则保留的执行时间将是 max_execution_time 减去一秒。

只有当指定了 PDFOPT_ENFORCE_EXECUTION_TIME 选项时,才会考虑此属性的值。

MaxExtractedImages

要提取的最大图像数量。默认值为0,表示如果设置了 PDFOPT_GET_IMAGE_DATAPDFOPT_AUTOSAVE_IMAGES 选项,则将选择所有图像进行输出。

MaxGlobalExecutionTime

此静态属性与 MaxExecutionTime 相同,但它在全局范围内工作。如果您必须处理 x 个文件,则它将确保全局执行时间不超过此属性的值。

只有当指定了 PDFOPT_ENFORCE_GLOBAL_EXECUTION_TIME 选项时,才会考虑此属性的值。

MaxSelectedPages

要选择的最大页面数。默认值为0,表示将选择所有页面进行输出。

值为1时,仅提取第一页的内容,这在PDF文件很大且您仅对第一页的内容感兴趣时非常有用。

当此数字为负数时,选择从文件末尾开始:-1表示“提取最后一页”,-2表示“提取最后两页”,依此类推。

MemoryUsage, MemoryPeakUsage

报告 Load() 方法使用的内存(峰值)。

MinSpaceWidth

有时,字符(或字符块)之间由一个偏移量分隔,该偏移量以文本单位的千分之一计算。对于某些值范围,当在图形设备上显示时,这些连续字符看起来被一个空格(或更多)分隔。当然,当生成ascii输出时,我们希望有一些等效的空格。

这就是 MinSpaceWidth 属性的用途:当找到的偏移量超过 MinSpaceWidth 文本单位时,在生成的输出中插入一个ascii空格。

请注意,如果为 Options 属性设置了 PDFOPT_REPEAT_SEPARATOR 标志,则插入的空格数始终基于1000的倍数,即使 MinSpaceWidth 小于1000。这意味着如果 MinSpaceWidth 为200,并且 Options 属性设置了 PDFOPT_REPEAT_SEPARATOR 标志,并且两个字符块之间的偏移量为1000文本单位,则仅插入一个空格,而不是5(这是1000/200的结果)。

ModificationDate

包含最后修改日期的字符串,格式为UTC。此值可以用作 strtotime() PHP 函数的参数。

Options

以下标志的组合

  • PDFOPT_REPEAT_SEPARATOR:有时,字符组由一个整数值分隔,该值指定在绘制下一个字符组之前从当前位置减去的偏移量。这个数量以千个“文本单位”表示。《PdfToText》类认为如果此值小于-1000,则需要在下一个字符组之前将 Separator 属性指定的字符串附加到结果中。如果指定了此标志,则 Separator 字符串将附加(offset % 1000)次。

  • PDFOPT_GET_IMAGE_DATA:将PDF文件中的图像数据存储到 ImageData 数组属性中。

  • PDFOPT_DECODE_IMAGE_DATA:解码图像数据并将其放入 Images 数组属性中。

  • PDFOPT_AUTOSAVE_IMAGES:使用由 ImageAutoSaveFileTemplate 属性给出的文件名模板自动保存PDF文件中找到的图像。当生成图像文件时,ImageAutoSaveFormat 属性将定义要使用的图像格式。默认输出格式为 IMG_JPEG。请注意,Images 属性将保持为空。此标志已引入以节省内部内存,如果您只需要提取图像。

  • PDFOPT_IGNORE_TEXT_LEADING : 当您发现两个文本元素之间插入了很多不必要的空白行时,必须使用此选项。这是pdf文件仅包含相对定位指令并结合大值文本行距指令的症状。

  • PDFOPT_RAW_LAYOUT : 以PDF文件中的原始文本形式渲染文本。这可能会导致文本顺序混乱或字符串以不适当的方式连接,但如果您只需要索引内容或关注性能,则应首选此选项。这是默认选项。

  • PDFOPT_BASIC_LAYOUT : 尝试按照Acrobat Reader中看到的顺序渲染文本。请注意,输出中的元素不会与Acrobat Reader中显示的元素完全对应:在x轴上物理分离的元素将默认通过一个空格隔开。可以使用 BlockSeparator 属性来修改此分隔符。以下文本示例

      Company1							Company2
      address1							address2
      city1 								city2
    

将渲染为

	Company1 Company2
	address1 address2
	city1 city2

或者,如果您将 BlockSeparator 属性设置为 "#",输出将变为

	Company1#Company2
	address1#address2
	city1#city2
  • PDFOPT_NO_HYPHENATED_WORDS : 当指定时,尝试将分词的单词重新组合成一个单词。例如,以下文本

      this is a sam-
      ple text using hyphe-
      nated words that can split
      over seve-
      ral lines.
    

将渲染为

	this is a sample
	text using hyphenated
	words that can split
	over several lines.
  • PDFOPT_ENFORCE_EXECUTION_TIME : 当指定时,将 MaxExecutionTime 属性与PHP设置 max_execution_time 进行比较。如果处理单个文件所需的时间可能超过此属性中定义的秒数,则会在PHP尝试终止脚本执行之前抛出 PdfToTextTimeout 异常。
  • PDFOPT_ENFORCE_GLOBAL_EXECUTION_TIME : 当指定时,将 MaxGlobalExecutionTime 静态属性与PHP设置 max_execution_time 进行比较。如果自脚本开始以来处理所有PDF文件所需的时间可能超过此属性中定义的秒数,则会在PHP尝试终止脚本执行之前抛出 PdfToTextTimeout 异常。
  • PDFOPT_DEBUG_SHOW_COORDINATES : 在输出中显示文本每个部分的图形坐标。如果您想定义捕获区域,则此选项非常有用。
  • PDFOPT_CAPTURE : 根据XML定义文件或字符串启用基于文本区域的捕获。请注意,指定此选项将自动设置 PDFOPT_BASIC_LAYOUT 标志。
  • PDFOPT_NONE : 默认值。不应用任何特殊处理标志。

页面

包含单个页面内容的关联数组。数组键是从1开始的页面编号。

页面分隔符

在构建 Text 属性时用于分隔单个页面的字符串。默认值是换行符。

生产者应用程序

用于生成PDF文件内容的程序。

分隔符

当在两个指定为数组的字符序列之间指定一个小于-1000千字符的负偏移量时,用于分隔块的一个字符串。这个技巧经常用于包含表格数据的PDF文件中。

默认值是一个空格。

主题

在作者信息部分写入的主题。

统计信息

包含以下条目的关联数组

  • 'TextSize' : 包含通过类似Postscript的指令绘制的文档内容所表示的总字节数
  • 'OptimizedTextSize' : 并非所有用于绘制页面内容的类似Postscript的指令都是重要的;由于解析是在纯PHP中进行的,因此速度非常慢。这就是为什么在大多数情况下,使用preg_replace()内置函数删除无用的指令可以显着减小要解析的数据的大小。此条目给出了删除无用指令后实际要解析的数据的总大小。

文本

包含从底层PDF文件中提取的整个文本的字符串。请注意,页面之间用换页符分隔。

标题

文档标题,如作者信息对象中指定。

Utf8占位符

当无法正确识别Unicode字符时,将使用Utf8占位符属性作为替换。

字符串可以包含由sprintf()函数识别的格式说明符。传递给sprintf()的参数是无法识别的Unicode码点(一个整数值)。

默认值是空字符串,或者当启用调试模式时,字符串为'未知字符 0x%08X]'。

请注意,如果您在类首次实例化后更改PdfToText::$DEBUG*变量,那么您需要手动设置PdfToText::Utf8PlaceHolder静态属性的值。

常量

PDFOPT_*

PDFOPT_*常量是一组可以在实例化类或调用Load方法之前设置Options属性时组合的标志。它可以是由以下标志中的任何组合组成:

  • PDFOPT_REPEAT_SEPARATOR:有时,字符组由一个整数值分隔,该值指定在绘制下一个字符组之前从当前位置减去的偏移量。这个数量以千个“文本单位”表示。《PdfToText》类认为如果此值小于-1000,则需要在下一个字符组之前将 Separator 属性指定的字符串附加到结果中。如果指定了此标志,则 Separator 字符串将附加(offset % 1000)次。

  • PDFOPT_GET_IMAGE_DATA:将PDF文件中的图像数据存储到 ImageData 数组属性中。

  • PDFOPT_DECODE_IMAGE_DATA:解码图像数据并将其放入 Images 数组属性中。

  • PDFOPT_AUTOSAVE_IMAGES:使用由 ImageAutoSaveFileTemplate 属性给出的文件名模板自动保存PDF文件中找到的图像。当生成图像文件时,ImageAutoSaveFormat 属性将定义要使用的图像格式。默认输出格式为 IMG_JPEG。请注意,Images 属性将保持为空。此标志已引入以节省内部内存,如果您只需要提取图像。

  • PDFOPT_IGNORE_TEXT_LEADING : 当您发现两个文本元素之间插入了很多不必要的空白行时,必须使用此选项。这是pdf文件仅包含相对定位指令并结合大值文本行距指令的症状。

  • PDFOPT_RAW_LAYOUT : 以PDF文件中的原始文本形式渲染文本。这可能会导致文本顺序混乱或字符串以不适当的方式连接,但如果您只需要索引内容或关注性能,则应首选此选项。这是默认选项。

  • PDFOPT_BASIC_LAYOUT : 尝试按照Acrobat Reader中看到的顺序渲染文本。请注意,输出中的元素不会与Acrobat Reader中显示的元素完全对应:在x轴上物理分离的元素将默认通过一个空格隔开。可以使用 BlockSeparator 属性来修改此分隔符。以下文本示例

      Company1							Company2
      address1							address2
      city1 								city2
    

将渲染为

	Company1 Company2
	address1 address2
	city1 city2

或者,如果您将 BlockSeparator 属性设置为 "#",输出将变为

	Company1#Company2
	address1#address2
	city1#city2
  • PDFOPT_NO_HYPHENATED_WORDS : 当指定时,尝试将分词的单词重新组合成一个单词。例如,以下文本

      this is a sam-
      ple text using hyphe-
      nated words that can split
      over seve-
      ral lines.
    

将渲染为

	this is a sample
	text using hyphenated
	words that can split
	over several lines.
  • PDFOPT_ENFORCE_EXECUTION_TIME : 当指定时,将 MaxExecutionTime 属性与PHP设置 max_execution_time 进行比较。如果处理单个文件所需的时间可能超过此属性中定义的秒数,则会在PHP尝试终止脚本执行之前抛出 PdfToTextTimeout 异常。
  • PDFOPT_ENFORCE_GLOBAL_EXECUTION_TIME : 当指定时,将 MaxGlobalExecutionTime 静态属性与PHP设置 max_execution_time 进行比较。如果自脚本开始以来处理所有PDF文件所需的时间可能超过此属性中定义的秒数,则会在PHP尝试终止脚本执行之前抛出 PdfToTextTimeout 异常。
  • PDFOPT_NONE : 默认值。不应用任何特殊处理标志。

页面

包含单个页面内容的关联数组。数组键是从1开始的页面编号。

页面分隔符

在构建 Text 属性时用于分隔单个页面的字符串。默认值是换行符。

版本

PdfToText类的当前版本,作为包含主版本号、次版本号和发布版本号的字符串。例如:"1.2.19"。

异常

PdfToText类可以抛出以下任何异常

PdfToTextException

这是所有由PdfToText类抛出的异常的基类。

PdfToTextDecodingException

当解码PDF对象时发生错误时,将抛出此异常。通常,大多数此类异常仅在激活调试模式时抛出。

PdfToTextDecryptionException

当加密文件上的解密失败时发生。

PdfToTextTimeoutException

只有当以下条件之一发生时,才会抛出此异常

  • 已设置PDFOPT_ENFORCE_EXECUTION_TIME选项,并且处理一个文件所需的时间超过了从$MaxExecutionTime属性计算出的秒数
  • 已设置PDFOPT_ENFORCE_GLOBAL_EXECUTION_TIME选项,并且处理所有脚本必须处理的文件所需的时间超过了从静态属性$MaxGlobalExecutionTime计算出的秒数

PdfToTextFormTemplateException

当在解析用于检索表单数据的模板文件时检测到错误,或者检索表单数据时抛出。

表单数据提取

提取表单数据相当简单:使用GetFormData()方法,它将返回一个包含您的PDF文件中所有字段值的对象,无论它们是否已填写。

您有两种方法可以检索表单数据

  • 或者通过提供一个将实际表单字段名映射到更易读名称的XML模板。它提供了额外的功能,例如将字段值分组在一起的能力
  • 或者依赖于默认行为,它将返回PDF文件中定义的表单字段名。

两种方法都返回一个继承自PdfToTextFormData的新对象,它主要包含对调用者没有兴趣的辅助函数。

GetFormData()方法返回的派生类有一组属性,可以为您提供访问表单字段内容的方式。

以下各节中给出的示例基于位于"examples/formdata-extraction"目录中的文件"sample.pdf"。它来自美国一个非常常见的表格,位于此处

https://www.irs.gov/pub/irs-pdf/fw9.pdf

不使用模板获取表单数据

不使用模板获取表单数据非常简单;只需执行以下代码

$pdf 		=  new PdfToText ( 'sample.pdf' ) ;
$form_data	=  $pdf -> GetFormData ( null ) ;

当然,我们应该首先通过调用来检查PDF文件中是否存在表单数据

if  ( $pdf -> HasFormData ( ) )
	$form_data	=  $pdf -> GetFormData ( null ) ;

$form_data中返回的对象具有以下定义

$form_data = (object) PdfFormData
   {
        protected            $f1_1                            = (string[6]) "ZZNAME"
        protected            $f1_2                            = (string[14]) "ZZBUSINESSNAME"
        protected            $c1_1                            = (string[1]) "6"
        protected            $f1_3                            = (string[1]) "C"
        protected            $c1_7                            = (string[1]) "7"
        protected            $f1_4                            = (string[7]) "ZZOTHER"
        protected            $f1_5                            = (string[4]) "EX01"
        protected            $f1_6                            = (string[4]) "EX02"
        protected            $f1_7                            = (string[9]) "ZZADDRESS"
        protected            $f1_8                            = (string[6]) "ZZCITY"
        protected            $f1_9                            = (string[28]) "ZZREQUESTERNAME\naddress\ncity"
        protected            $f1_10                           = (string[16]) "ZZACCOUNTNUMBERS"
        protected            $f1_11                           = (string[3]) "123"
        protected            $f1_12                           = (string[2]) "45"
        protected            $f1_13                           = (string[4]) "6789"
        protected            $f1_14                           = (string[2]) "EI"
        protected            $f1_15                           = (string[5]) "ZZEMP"
    }

您可以通过打开文件sample.pdf来验证上述数据是否与PDF文件中的数据一致。

然而,您会发现字段名称不是很明确:$f1_1$f1_2等。此外,像社会保障号码这样的信息被分成三部分:$f1_11$f1_12$f1_13

因此,您可能需要花一些时间设计一个模板XML文件,将PDF字段名称映射到可读性强的名称...

使用模板获取表单数据

使用XML模板不需要对现有代码进行太多更改;您只需要在调用GetFormData()方法时提供XML模板的路径

$pdf 		=  new PdfToText ( 'sample.pdf' ) ;
$form_data	=  $pdf -> GetFormData ( 'sample.xml' ) ;

使用提供的示例,您的$form_data对象将如下所示

$form_data = (object) W9
   {
        const  TAXCLASS_INDIVIDUAL                 =  1 ;
        const  TAXCLASS_C_CORPORATION              =  2 ;
        const  TAXCLASS_S_CORPORATION              =  3 ;
        const  TAXCLASS_PARTNERSHIP                =  4 ;
        const  TAXCLASS_TRUST_ESTATE               =  5 ;
        const  TAXCLASS_LIMITED_LIABILITY_COMPANY  =  6 ;
        const  TAXCLASS_UNDEFINED                  = ""  ;
        const  TAXCLASS_OTHER                      =  7 ;

        protected            $Name                            = (string[6]) "ZZNAME"
        protected            $BusinessName                    = (string[14]) "ZZBUSINESSNAME"
        protected            $FederalTaxClassification        = (string[1]) "6"
        protected            $LLCClassification               = (string[1]) "C"
        protected            $OtherFederalTaxClassification   = (string[1]) "7"
        protected            $OtherFederalTaxInfo             = (string[7]) "ZZOTHER"
        protected            $ExemptPayeeCode                 = (string[4]) "EX01"
        protected            $FATCAExemptionCode              = (string[4]) "EX02"
        protected            $Address                         = (string[9]) "ZZADDRESS"
        protected            $City                            = (string[6]) "ZZCITY"
        protected            $RequesterCoordinates            = (string[28]) "ZZREQUESTERNAME\naddress\ncity"
        protected            $AccountNumbers                  = (string[16]) "ZZACCOUNTNUMBERS"
        protected            $SSN_1                           = (string[3]) "123"
        protected            $SSN_2                           = (string[2]) "45"
        protected            $SSN_3                           = (string[4]) "6789"
        protected            $EIN_1                           = (string[2]) "EI"
        protected            $EIN_2                           = (string[5]) "ZZEMP"
        protected            $SSN                             = (string[11]) "123-45-6789"
        protected            $EIN                             = (string[8]) "EI-ZZEMP"
    }

您会注意到与无模板版本的一些重要区别

  • 类名为W9而不是PdfFormData;此信息来自模板文件
  • 已定义了常量;它们也来自模板文件
  • 表单字段具有显式名称,例如BusinessName(而不是f1_2)或ExemptPayeeCode(而不是f1_5
  • 出现了两个新属性:SSNEIN。在原始表单中,社会保障号码被分成三部分:f1_11f1_12f1_13。这些字段被模板XML文件分别重命名为SSN_1SSN_2SSN_3。但是,它还定义了SSNEIN,它们是分组属性。SSN被定义为SSN_1SSN_2SSN_3属性的串联,而EINEIN_1EIN_2属性的串联。

所有这些都在模板文件中定义,父类PdfToTextFormData能够处理对任何涉及分组属性的属性的任何修改。例如,如果您修改了SSN_1属性,则SSN属性将相应地重建。同样,如果您修改了SSN属性,则SSN_1SSN_2SSN_3属性将相应地更改。

这项内部工作由PdfToTextFormData执行,任何由GetFormData()方法返回的类都继承自它。

现在,我们来看看什么是模板。这将在下一节中描述。

典型的表单模板

以下是我们为从我们的示例PDF文件中提取表单数据而设计的典型表单

<?xml version="1.0" encoding="utf-8" ?>

<forms class="W9">
	<form version="Form W-9 (Rev. December 2014)">
		<field name="Name"							form-field="f1_1"	type="string"/>
		<field name="BusinessName"					form-field="f1_2"	type="string"/>
		<field name="FederalTaxClassification"		form-field="c1_1"	type="choice">
			<case value="1"		constant="TAXCLASS_INDIVIDUAL"/>
			<case value="2"		constant="TAXCLASS_C_CORPORATION"/>
			<case value="3"		constant="TAXCLASS_S_CORPORATION"/>
			<case value="4"		constant="TAXCLASS_PARTNERSHIP"/>
			<case value="5"		constant="TAXCLASS_TRUST_ESTATE"/>
			<case value="6"		constant="TAXCLASS_LIMITED_LIABILITY_COMPANY"/>
			<default			constant="TAXCLASS_UNDEFINED"/>
		</field>
		<field name="LLCClassification"				form-field="f1_3"	type="string"/>
		<field name="OtherFederalTaxClassification"	form-field="c1_7"	type="choice">
			<case value="7"		constant="TAXCLASS_OTHER"/>
			<default			constant="TAXCLASS_UNDEFINED"/>
		</field>		
		<field name="OtherFederalTaxInfo"			form-field="f1_4"	type="string"/>
		<field name="ExemptPayeeCode"				form-field="f1_5"	type="string"/>
		<field name="FATCAExemptionCode"			form-field="f1_6"	type="string"/>
		<field name="Address"						form-field="f1_7"	type="string"/>
		<field name="City"							form-field="f1_8"	type="string"/>
		<field name="RequesterCoordinates"			form-field="f1_9"	type="string"/>
		<field name="AccountNumbers"				form-field="f1_10"	type="string"/>

		<field name="SSN_1"							form-field="f1_11"	type="string"/>
		<field name="SSN_2"							form-field="f1_12"	type="string"/>
		<field name="SSN_3"							form-field="f1_13"	type="string"/>

		<field name="EIN_1"							form-field="f1_14"	type="string"/>
		<field name="EIN_2"							form-field="f1_15"	type="string"/>

		<group name="SSN" separator="-" fields="SSN_1, SSN_2, SSN_3"/>
		<group name="EIN" separator="-" fields="EIN_1, EIN_2"/>
	</form>
</forms>

根标记由<forms>指定。子标记是<form>定义。

目前,已实现以下内容

  • <forms>标记的class属性给出了将由GetFormData()方法返回的类(继承自PdfToTextFormData)的名称,对于所有子<form>条目。
  • 每个<form>条目都有一个version属性;这将在将来用于处理相同PDF表单的不同版本。

请注意,上述信息可能在未来的版本中发生变化。

定义字段

表单字段目前可以是三种类型

  • 字符串字段
  • 选择字段。这通常用于类似于单选按钮的复选框,表示具有不同值的唯一字段。选择字段允许您将常量与每个单独的值关联起来。
  • 分组字段。分组字段是虚拟字段,是几个现有字段的串联结果。

了解哪个PDF表单字段包含哪些数据的一个(相当)繁琐的方法是

  • 打开您的PDF表单。填写值。当您面临复选框时,您可能需要测试单个值(在我们的上述示例中,属性FederalTaxClassificationOtherFederalTaxClassifications看起来像复选框,其值范围为1到7;然而,它们已被分成两个字段)
  • 对由GetFormData()方法返回的值进行print_r(),不指定任何模板
  • 上述步骤应有助于您识别哪些字段包含什么,并据此设计模板

字符串字段

表单中的字符串字段基本上使用以下XML field 构造来指定

	<field name="SSN_1"	form-field="f1_11"	type="string"/>

属性如下

  • name:属性名称,它将作为GetFormData()方法返回的类中的属性存在。
  • form-field:PDF表单定义中相应的名称。
  • typestring

选择字段

选择字段通常用于一组像单选按钮一样行为的复选框

		<field name="FederalTaxClassification"		form-field="c1_1"	type="choice">
			<case value="1"		constant="TAXCLASS_INDIVIDUAL"/>
			<case value="2"		constant="TAXCLASS_C_CORPORATION"/>
			<case value="3"		constant="TAXCLASS_S_CORPORATION"/>
			<case value="4"		constant="TAXCLASS_PARTNERSHIP"/>
			<case value="5"		constant="TAXCLASS_TRUST_ESTATE"/>
			<case value="6"		constant="TAXCLASS_LIMITED_LIABILITY_COMPANY"/>
			<default			constant="TAXCLASS_UNDEFINED"/>
		</field>

它们基本上包含与string字段相同的信息,只是将type属性设置为choice

它们可以与通过<case><default>标签定义的常量集配对

  • <case>标签允许您将一个命名的常量与一个特定的值关联
  • <default>允许您在定义的任何<case>标签与表单值不匹配时关联一个命名的常量。

请注意,每个定义的常量,如上述示例中的TAXCLASS_INDIVIDUAL,都将被定义为GetFormData()方法返回的对象中的类常量。

分组字段

分组字段允许您创建新的属性,这些属性来自现有字段的连接。一个典型的定义如下

	<group name="SSN" separator="-" fields="SSN_1, SSN_2, SSN_3"/>

在我们的模板示例中,我们已经看到社会保险号(SSN)被分割成三个部分:SSN_1SSN_2SSN_3。上述示例创建了一个SSN属性,它是指定字段连接的结果。

必需的属性如下

  • name:分组属性的名称
  • fields:应分组在一起的现有字段名称的逗号分隔列表
  • separator:用于分隔分组字段中每个组件的分隔符字符串。

请注意,修改分组字段引用的属性值将修改相应的分组字段值。同样,修改分组字段值将修改其关联的属性。

捕获文本

有时,告诉PdfToText类您要从哪一页(些)获取哪一区域(些)的文本,比通过正则表达式来隔离您想要的信息要容易得多。这在您想从表格报告中检索数据时尤其如此。

捕获是解决此类需求的方法;它们允许您定义以下类型的形状

  • 矩形:矩形用于包围您在处理后想要提取内容的文本区域。
  • 线条:当您必须处理以表格格式呈现的报告时,允许您捕获行(以及行内的列)。

要捕获的区域使用XML格式的捕获定义文件或字符串指定。

逐步概述

捕获PDF文档的区域需要您进行一些初步步骤,这些步骤涉及一些额外的工作。这就是为什么您必须在使用捕获或对提取的文本使用正则表达式之间做出选择的原因。

  • 如果您有多个同类型的文档(例如报告),那么花费一些时间设计一个捕获定义文件来轻松检索行/列值是有意义的。
  • 如果您有很多不同呈现的文档,那么正则表达式将更有助于您。

以下逐步概述引用了位于examples/text-captures目录中的文件。

确定要捕获的内容

PDF文件使用一个坐标系统,其值大致用“相对单位”表示。坐标(0,0)的点位于页面的左下角;坐标(x,y)的点,其中“x”是页面宽度,“y”是页面高度,位于页面的右上角。

这很好,但如何找到包含您要捕获的文本的矩形的坐标(x,y,宽度和高)呢?当然,如果您有允许您修改PDF文件的Adobe产品,它可能会提供此类信息。但如果您没有这样的工具,您可能就陷入了困境。

PdfToText类有一个标志,PDFOPT_DEBUG_SHOW_COORDINATES,它在文本输出中包含在PDF流中找到的每个文本块的(x,y)坐标、宽度和高度。这可能是有机会使用此选项的唯一场合。以下示例脚本说明了如何生成此包中examples/text-capture目录下的sample-report.txt文件。

<?php
		include ( 'path/to/PdfToText.phpclass' ) ;

		$pdf	=  new PdfToText ( 'sample-report.pdf', PDFOPT_DEBUG_SHOW_COORDINATES ) ;
		file_put_contents ( 'sample-report.txt', $pdf -> Text ) ;

sample-report.txt文件的前几行现在包含如下内容

[Page : 1, width = 596, height = 843]

[x:248.76, y:760.4, w: 79.895, h:12]REPORT HEADER 

[x:70.695, y:746.6, w: 2.381, h:12] 

[x:84.495, y:722.6, w: 2.381, h:12] 
[x:84.495, y:734.6, w: 2.381, h:12] 

[x:0, y:708.08, w: 124.619, h:12]Column1  Column2  Column3 

[x:70.8, y:690.32, w: 76.99, h:12]L1C1  L1C2  L1C3 

[x:70.8, y:676.04, w: 76.99, h:12]L2C1  L2C2  L2C3 

[x:70.8, y:661.76, w: 76.99, h:12]L3C1  L3C2  L3C3 

[x:70.8, y:647.48, w: 76.99, h:12]L4C1  L4C2  L4C3 

[x:70.8, y:633.2, w: 2.381, h:12] 
[Page : 2, width = 596, height = 843]

[x:70.8, y:760.4, w: 2.381, h:12] 

[x:0, y:745.88, w: 124.619, h:12]Column1  Column2  Column3 

[x:70.8, y:731.72, w: 178.533, h:12]L1C1 (page 2)  L1C2 (page 2)  L1C3 (page 2) 

[x:70.8, y:717.44, w: 178.533, h:12]L2C1 (page 2)  L2C2 (page 2)  L2C3 (page 2) 

[x:70.8, y:703.16, w: 178.533, h:12]L3C1 (page 2)  L3C2 (page 2)  L3C3 (page 2) 

[x:70.8, y:688.88, w: 178.533, h:12]L4C1 (page 2)  L4C2 (page 2)  L4C3 (page 2) 

[x:70.8, y:674.6, w: 2.381, h:12]

而不是没有特定选项的默认输出(参见文件sample-report.pdf以获取其图形对应物)

REPORT HEADER 
 
Column1  Column2  Column3 
L1C1  L1C2  L1C3 
L2C1  L2C2  L2C3 
L3C1  L3C2  L3C3 
L4C1  L4C2  L4C3 
 
Column1  Column2  Column3 
L1C1 (page 2)  L1C2 (page 2)  L1C3 (page 2) 
L2C1 (page 2)  L2C2 (page 2)  L2C3 (page 2) 
L3C1 (page 2)  L3C2 (page 2)  L3C3 (page 2) 
L4C1 (page 2)  L4C2 (page 2)  L4C3 (page 2) 

使用PDFOPT_DEBUG_SHOW_COORDINATES选项生成的输出中,您会在方括号之间注意到一些额外的信息

  • 第一个出现在每一页的开始处

      [Page : 1, width = 596, height = 843]
      ...
      [Page : 2, width = 596, height = 843]
      ...
    

它提供了页码以及以图形坐标表示的宽度和高度。

  • 方括号之间出现的第二种类型的信息提供了紧随其后的文本块的尺寸信息

      [x:248.76, y:760.4, w: 79.895, h:12]REPORT HEADER 
    

设计捕获定义文件(或字符串)

使用PDFOPT_DEBUG_SHOW_COORDINATES选项添加到输出中的各种信息应该给您足够的数据来设计捕获定义文件。

以下是一个示例,它将捕获sample-report.pdf文件的一些内容;关于此XML格式的更详细解释见《XML捕获定义》部分

<captures>
	<rectangle name="Title">
		<page number="1" left="245" right="360" top="760" bottom="740"/>
	</rectangle>

	<lines name="ReportLines" default="*UNDEFINED*" separator="\n">
		<page number="1"	top="690" height="16" bottom="0"/>
		<page number="2..$"	top="731" height="16" bottom="0"/>

		<column name="Column1"		left="70"	width="600"/>
	</lines>
</captures>

基本上,上面的数据表明它想捕获两件事

  • 一个位于第1页并命名为“标题”的<rectangle>。指定的坐标(左、上、右、下 - 但您也可以指定宽或高而不是右和下)定义了包含PDF文件中“标题”字符串的矩形
  • 一个名为“ReportLines”的<lines>标签,它定义了要从输入PDF流中捕获哪些列。这是通过使用<column>标签来完成的,这些标签提供了您想要捕获的每个列的x位置和单个宽度。

您可能已经注意到,<rectangle><lines>都包含名为<page>的子标签;它们定义了输入PDF文件的哪些页适用于父标签。例如

  • 我们要捕获的矩形仅在PDF文件的第1页上定义
  • 我们要捕获的行组从y位置690开始,第2页为731,直到最后一页($)。两种情况下行高均为16。在此行组内部,您可以指定您想要捕获的列数(在我们的示例中,只定义了一个列;其名称为“Column1”)。

在此定义中列出的所有坐标、宽度和高度都取自前一步骤(上述部分)中生成的sample-report.txt文件中的信息。

设置PdfToText以处理捕获

捕获是独立于文本提取处理的;您需要做的只有两件事

  • 在选项标志中指定PDFOPT_CAPTURE

  • 调用SetCaptures()SetCapturesFromString()方法来加载捕获定义,如下例所示

    $pdf = new PdfToText ( 'sample-report.pdf', PdfToText::PDFOPT_CAPTURE ) ; $pdf -> SetCaptures ( 'sample-report.xml' ) ;

检索捕获数据

检索捕获数据相当简单

$captures 	=  $pdf -> GetCaptures ( ) ;

这将返回一个类型为 PdfToTextCaptureLines 的对象。

在 XML 定义文件(例如我们的示例中的 sample-report.txt)中定义的每个标签都有一个名称;例如,我们的矩形形状名称为 "Title",线条组名称为 "ReportLines"(每条报告线有一个名为 "Column1" 的列)。

在捕获定义文件中指定的每个捕获名称都可以作为 GetCaptures() 方法返回的对象的属性名称访问

$capture -> Title
$capture -> ReportLines

然而,这些各种信息的处理方式取决于捕获类型

  • 对于矩形捕获,属性将是一个数组,因为相同的捕获可以在不同页面的不同位置定义。为了检索位于我们 PDF 文件第一页上的标题,我们将必须编写

      $capture -> Title [1] -> Text 
    

	( string ) $capture -> Title [1] 

矩形捕获可以通过其页码访问。对于文档的每一页都将有一个捕获,即使它们不在适用页码列表中。

  • 对于线条捕获,情况略有不同:捕获报告行(以及它们的列数据)的目的是能够一次性处理它们。这就是为什么它们被组合在一个单独的集合中,您可以使用此类循环来显示或处理它们

      foreach ( $captures -> ReportLines  as  $line )
         {
      		foreach  ( $line  as  $column )
      			// do something with $column -> Text
          }
    

在行内,您还可以通过名称引用列

	$line -> Column1 -> Text

XML 捕获定义

本节描述了 XML 捕获定义的格式。

<captures> 标签

<captures> 标签是 XML 捕获定义的最高级节点。它没有特定属性,可以包含以下子标签之一

  • <rectangle>
  • <lines>

<rectangle> 标签

<rectangle> 标签定义了一个用于捕获其内文本的矩形区域。它具有以下属性

  • name : 形状的名称。必须是一个有效的 PHP 标识符,将用于从 GetCaptures() 方法返回的对象中访问此属性信息。此名称必须是唯一的。

<rectangle> 标签必须指定至少一个 <page> 子标签,以指示 PdfToText 类应该在哪些页的文档中搜索捕获的文本。

<page> 子标签

<rectangle> 标签的 <page> 子标签具有以下属性

  • number : 页码。这可以是一个以逗号分隔的页码列表或以省略号分隔的页码范围,如下例所示

      "1,2"
      "1,2..10"
    

特殊字符 "$" 表示 "最后一页"。

允许表达式;以下示例

	"1, $-9..$"

表示矩形区域适用于第 1 页和文档的最后一 10 页。

<page> 标签的其他属性如下

  • left : 矩形的左 x 坐标。
  • right : 矩形的右 x 坐标。
  • top : 矩形的上 y 坐标。
  • bottom : 矩形的下 y 坐标。
  • height : 指定矩形的高度。必须指定以下属性组合之一:($top, $height),($bottom, $height) 或 ($top, $bottom)
  • width : 指定矩形的宽度。必须指定以下属性组合之一:($left, $width),($right, $width) 或 ($left, $right)

<lines> 标签

<lines> 标签允许定义要捕获的线条组。它定义了适用页,并为要捕获的每个列提供定义。它主要用于捕获报告行,并具有以下属性

  • 名称 : 元素名称。必须是一个有效的PHP标识符,用于从GetCaptures()方法返回的对象中访问此属性信息。此名称必须是唯一的。
  • 默认值 : 为PDF文档中没有捕获文本的列提供默认值。
  • 分隔符 : 每行包含一个Text属性,该属性表示该行的所有列,这些列由该属性的值分隔。

<lines>标签可以包含任意数量的<page><column>子标签。

<page> 子标签

<page>子标签的<lines>标签与<rectangle>标签的<page>子标签大致具有相同的目标,尽管有一些不同。它不是用来定义矩形形状的,而是用来定义页面上第一行和最后一行的顶部和底部坐标,以及行高。

以下属性可用:

  • number : 页码。它具有与<rectangle>标签的<page>子标签相同的语法。
  • top : 页面上要捕获的第一行的y坐标。
  • bottom : 页面上要捕获的最后一行的y坐标。默认值为0。
  • height : 行高。

<column>子标签

<column>子标签标识行内的列位置。它具有以下属性:

  • name : 列名称。此名称必须是一个有效的PHP标识符,用于从GetCaptures()方法返回的对象中访问此属性信息。列名称必须在<lines>标签内唯一。
  • default : 如果在此行/列组合中没有找到信息,则在捕获中放入的默认值。如果没有指定,则使用<lines>的"default"属性的值。如果没有指定,则默认值将为空字符串。
  • left : 列的第一个x位置。
  • width : 列宽度。
  • right : 列的最后一个x位置。

必须指定widthright属性之一。

捕获类参考

GetCaptures()方法返回的对象需要一些解释;在捕获的内容如何访问它之间做出了区分。

GetCaptures()方法返回一个对象层次结构,回答了如何;这个层次结构中的每个对象都包含回答内容的对象。该方法返回一个类PdfToTextCaptures的对象,如下所述。

PdfToTextCaptures类

此类动态创建属性,这些属性直接来自捕获XML定义中的<rectangle>和<lines>标签指定的名称。

因此,如果我们使用示例定义文件并调用GetCaptures()方法

 $captures 	=  $pdf -> GetCaptures ( ) ;

那么我们将能够以下这种方式访问我们的捕获文件中定义的属性

$captures -> Title

$captures -> ReportLines

以下是捕获XML定义与在此对象中找到的属性之间的对应关系

  • <rectangle>标签将提供一个与标签中指定的name属性相同的名称的属性。此属性是类型为PdfToTextRectangleCapture的类型。这是一个数组属性,其索引包含捕获的页面编号。请注意,对于文档的每一页,即使为空或未在<rectangle>条目的<page>标签中指定,也将有一个条目。
  • 相同的原理适用于<lines<,它给出一个类型为PdfToTextLinesCapture的属性。

PdfToTextRectangleCapture类

此类允许根据<rectangle>定义检索捕获文本的信息。它表现得像包含类型为PdfToTextCapturedRectangle的元素的数组。

这个类是一个数组的原因在于,矩形形状可以用于捕获不同页面上不同位置的文字。

PdfToTextLinesCapture类

此类也表现为一个数组,提供类型为PdfToTextCaptureLine的元素。它包含了整个文档中捕获的行。

PdfToTextCapturedText类

这是文档中捕获的各种形状的基抽象类(如PdfToTextCapturedRectangle等)。它具有以下属性

  • 名称 : 捕获名称
  • 页面 : 捕获文字所在的页码
  • 文字 : 捕获的文字
  • : 捕获文字的坐标

PdfToTextCapturedRectangle类

继承自PfToTextCapturedText类。目前它没有添加任何新的属性或方法。

PdfToTextCapturedLine类

继承自PfToTextCapturedText类。添加了以下属性

  • : 一组PdfToTextCapturedColumn对象。

此类的一个对象可以像数组一样迭代。它还提供了通过指定名称作为属性来直接访问一个PdfToTextCapturedColumn对象的方式;例如

$captured_line -> Column1 

当然,列名已在捕获XML定义中指定,使用column标签的name属性。