我有以下pdf文件Marsheet PDF m试图提取示例中显示的数据,我已经尝试过PDFParse,PDFtoText等....但是没有正常工作是否有任何解决方案或示例?
<?php
//Output something like this or suggest me if u have any better option
$data_array = array(
array( "name" => "Mr Andrew Smee",
"medicine_name" => "FLUOXETINE 20MG CAPS",
"description" => "TAKE ONE ONCE DAILY FOR LOW MOOD. CAUTION:YOUR DRIVING REACTIONS MAY BE IMPAIRED",
"Dose" => '9000',
"StartDate" => '28/09/15',
"period" => '28',
"Quantity" => '28'
),
array( "name" => "Mr Andrew Smee",
"medicine_name" => "SINEMET PLUS 125MG TAB",
"description" => "TAKE ONE TABLET FIVE TIMES A DAY FOR PD
(8am,11am,2pm,5pm,8pm)
THIS MEDICINE MAY COLOUR THE URINE. THIS IS
HARMLESS. CAUTION:REACTIONS MAY BE IMPAIRED
WHILST DRIVING OR USING TOOLS OR MACHINES.",
"Dose" => '0800,1100,1400,1700,2000',
"StartDate" => '28/09/15',
"period" => '28',
"Quantity" => '140'
), etc...
);
?>
Run Code Online (Sandbox Code Playgroud)
TL; DR 你几乎肯定不会单独使用库.
更新:下面编写了一个工作解决方案(不是一个完美的解决方案!),参见"实践中".这个需要:
- 定义文本所在的区域;
- 安装和运行命令行工具的可能性,
pdf2json.
PDF文件包含排版基元,而不是可提取文本; 有时差异很小,你可以通过,但通常只有可提取的文本,易于访问的格式,意味着文档在美学上看起来"略有错误",因此创建"最佳"PDF文本提取的生成器是也越少使用.
存在一些嵌入排版图层和不可见文本图层的生成器,允许查看漂亮的文本并获得好的文本.您猜对了PDF大小的代价.
在您的示例中,您只在文件中包含漂亮的文本,并且网格的存在意味着文本需要正确排版.
所以,在里面,实际上要阅读的是这个.注意圆括号内的字母:
/R8 12 Tf
0.99941 0 0 1 66 765.2 Tm
[(M)2.51003(r)2.805( )-2.16558(A)-3.39556(n)
-4.33056(d)-4.33056(r)2.805(e)-4.33056(w)11.5803
( )-2.16558(S)-3.39556(m)-7.49588(e)-4.33117(e)556]TJ
ET
Run Code Online (Sandbox Code Playgroud)
如果你组装里面的(S)(I)(N)(G)(L)(E)的信件,你得到"黄宏史密",但你需要知道在那里,这些信件是相关页面,和数据网格.你也需要注意空间.上面,在"先生"和"安德鲁"之间有一个明确的空格字符,括号括起来; 但如果您删除了这些空格并修复了以下所有字母的偏移量,您仍然会读到"Mr Andrew Smee"并保存两个字符.一些PDF"优化器"会尝试这样做,而不考虑偏移量,该实体的"文本"字符串将只是"MrAndrewSmee".
这就是为什么大多数文本提取库,它们不能轻易地管理字符偏移(它们使用"文本行",大体上它们不关心网格)会给你类似的东西
Mr Andrew Smee 505738 12/04/54 (61
Run Code Online (Sandbox Code Playgroud)
或者,在"优化"文本的情况下,
MrAndrewSmee50573812/04/54(61
Run Code Online (Sandbox Code Playgroud)
(它仍然提供了一个可以用正则表达式进行解析的危险错觉 - 有时它是,有时它不是,大多数时候它在95%的时间内工作,所以剩下的5%变成来自地狱的维护噩梦),但更重要的是,他们将无法获得药物详细时间表除以细胞的内容.
任何与空间相关的信息(例如,如果名称在左侧"从"或"右侧"框中写入,则名称具有不同的含义)将丢失,或者可能难以重建.
有PDF"保护"方案利用抵消文本的能力,并将扰乱字符串.有了偏移量,你可以写:
9 l 10 d 4 l 5 1 H 2 e 3 l o 6 W 7 o 8 r
Run Code Online (Sandbox Code Playgroud)
PDF查看器会显示"Hello World"; 但直接阅读文字,你得到"ldlHeloWor",或更糟.您可以添加恶意文本并将其放在页面外部,或者以透明颜色书写,以恶作剧成功删除PDF文件的轻松删除的可选复制粘贴保护.大多数图书馆会愉快地将恶作剧文本与好文本一起吸收.
诸如XPDF(及其包装器phpxpdf,pdf2html等)之类的库将为您提供一个简单的调用,例如
// open PDF
$pdfToText->open('PDF-book.pdf');
// PDF text is now in the $text variable
$text = $pdfToText->getText();
$pdfToText->close();
Run Code Online (Sandbox Code Playgroud)
而你的"文本"将包含所有内容,并且类似于:
...
START DATE START DAY
WEEK 1 WEEK 2 WEEK 3 WEEK 4
DATE 28 29 30 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
19/10/15
Medication Details
Commencing
D.O.B
Doctor
Hour:Dose 1 2 3 4 5 6 7 1 2 3 4 5 6 7 1 2 3 4 5 6 7 1 2 3 4 5 6 7
Patient
Number
Period
MEDICATION ADMINISTRATION RECORD SHEETS Pharmacy No.
Document No.
02392 731680
28
0900 1
TAKE ONE ONCE DAILY FOR LOW MOOD.
CAUTION:YOUR DRIVING REACTIONS MAY BE IMPAIRED.
28
FLUOXETINE 20MG CAPS
Received Quantity returned quant. by destroyed quant. by
Run Code Online (Sandbox Code Playgroud)
所以,在上面阅读,问问自己 - 那是第二个28?您能否在不查看PDF的情况下判断是收货数量,退货数量,销毁数量?当然,如果只有一个号码,那么它很可能是收到的数量.这成了赌注.
和02392 731680的文件号码?它看起来像是(它不是).
另请注意,在PDF中,药品名称位于注释之前.在提取的文本中,它是在之后.通过查看PDF中的偏移量,您可以理解为什么,这甚至是一个很好的决定 - 但是查看提取的文本,并不是那么容易.
因此,自动分析看起来非常诱人,就像我说的那样,这是一个非常冒险的业务.它很脆弱:有人在文档的某个地方输入了错误的文字,有时甚至不按顺序填写字段,这将导致PDF在视觉上是正确的,同时,无法解释为无法解析.你打算告诉你的用户什么?
有时,可用信息的一部分足够稳定,可以让您完成工作.在那种情况下,XPDF或PDF2HTML,一堆正则表达式,你在半天内免费回家.你呢!请记住,项目的任何"少量"添加都可能是不可能的.添加了两个在PDF中分开的数字; 他们是128和361,或12和8361,或1283和61?你得到的$text只是128361.
因此,如果您采用这种方式,请将其清楚地记录下来,避免可能难以维护的期望.你的初始项目可能运作得如此之快,如此之快,如此之快,而不是你不知道的加入 - 然后你需要做不可能的事情.解释为什么前95%很容易,后续5%非常努力可能比你的工作更值得.
但你能"手工"做同样的事吗?毕竟,通过查看 PDF,您知道您所看到的内容.机器可以完成同样的事情吗?(这仍然适用).当然,在这个 - 毕竟 - 明确划分的计算机视觉问题,你很可能.它不会快速而简单.你需要:
pdftk).您需要使用坐标恢复文本."住院"的"C"毫无价值."C,495.2,882.7"以及您网格的坐标告诉您2015年10月13日的住院治疗 - 这就是您所追求的信息!请注意,您可以通过编程方式计算这些值中的很多值:一旦您有左上角并知道一个单元格的大小,其他值或多或少可以计算,并且有一个非常小的错误.你不需要输入六个四周的网格,每个网站每周六天,每周七天.
您可以使用相同的结构创建一个带有红色区域的PNG,以指示您已覆盖的单元格.这将有助于目视检查你没有忘记任何事情.
此时,您解析PDF,每次在坐标(x1,y1)处找到文本时,都会扫描所有单元格并确定文本的位置(使用XY二叉搜索树有更快的方法).如果您在66,765.2找到'Mr Andrew S',则将其添加到PatientName.然后你在109.2,765.2找到'mee',你也可以将它添加到PatientName.现在写着'Andrew Smee先生'.
如果水平距离高于某个阈值,则添加一个空格(或多个空格).
(对于非常小的文本,PDF驱动程序输出的字母有很小的风险,并通过字距调整校正,但通常这不是问题).
在整个周期结束时,您将被留下
[ 'PatientName', 60, 760, 300, 790, 'Mr Andrew Smee' ],
[ 'PatientNumber', 310, 760, 470, 790, '505738' ],
Run Code Online (Sandbox Code Playgroud)
等等.
几年前我为一个大型PDF导入项目做了这种工作,它就像一个魅力.如今,我认为大部分繁重的工作都可以用TcLibPDF完成.
痛苦的部分是手工录制,第一次是网格的信息; 可能有可能有工具,或者可以使用画布鞭打HTML5/AJAX编辑器.
大部分工作已经由优秀的pdf2json工具完成,该工具使用'Andrew Smee'PDF,输出如下内容:
[
{
"height" : 1263,
"width" : 892
"number" : 1,
"pages" : 1,
"fonts" : [
{
"color" : "#000000",
"family" : "Times",
"fontspec" : "0",
"size" : "15"
},
...
],
"text" : [
{ "data" : "12/04/54",
"font" : 0,
"height" : 17,
"left" : 628,
"top" : 103,
"width" : 70
},
{ "data" : "28/09/15",
"font" : 0,
"height" : 17,
"left" : 105,
"top" : 206,
"width" : 70
},
{ "data" : "AQUARIUS",
"font" : 0,
"height" : 17,
"left" : 99,
"top" : 170,
"width" : 94
},
{ "data" : " ",
"font" : 0,
"height" : 17,
"left" : 193,
"top" : 170,
"width" : 5
},
{ "data" : "NURSING",
"font" : 0,
"height" : 17,
"left" : 198,
"top" : 170,
"width" : 83
},
...
Run Code Online (Sandbox Code Playgroud)
为了简单起见,我将Andrew Smee PDF转换为PNG并将其重新取样为892 x 1263像素(任何尺寸都可以,只要你跟踪尺寸.下面,它们保存在'宽度'和'高度').这样我就可以直接从旧的PaintShop Pro的状态栏中读取像素坐标:-).
"地址"字段为73,161至837,193.
我的样本"模板",只有三个字段,因此在PHP 5.7中(使用短数组语法,[]代替Array())
<?php
function template() {
$template = [
'Address' => [ 'x1' => 73, 'y1' => 161, 'x2' => 837, 'y2' => 193 ],
'Medicine1' => [ 'x1' => 1, 'y1' => 283, 'x2' => 251, 'y2' => 299 ],
'Details1' => [ 'x1' => 1, 'y1' => 302, 'x2' => 251, 'y2' => 403 ],
];
foreach ($template as $fieldName => $candidate) {
$template[$fieldName]['elements'] = [ ];
}
return $template;
}
// shell_exec('/usr/local/bin/pdf2json "Andrew-Smee.pdf" andrew-smee.json');
$parsed = json_decode(file_get_contents('ann-underdown.json'), true);
$pout = [ ];
foreach ($parsed as $page) {
$template = template();
foreach ($page['text'] as $text) {
// Will it blend?
foreach ($template as $fieldName => $candidate) {
if ($text['top'] > $candidate['y2']) {
continue; // Too low.
}
if (($text['top']+$text['height']) < $candidate['y1']) {
continue; // Too high.
}
if ($text['left'] > $candidate['x2']) {
continue;
}
if (($text['left']+$text['width']) < $candidate['x1']) {
continue;
}
$template[$fieldName]['elements'][] = $text;
}
}
// Now I must reassemble all my fields
foreach ($template as $fieldName => $data) {
$list = $data['elements'];
usort($list, function($txt1, $txt2) {
for ($r = 8; $r >= 1; $r /= 2) {
if (($txt1['top']/$r) < ($txt2['top']/$r)) {
return -1;
}
if (($txt1['top']/$r) > ($txt2['top']/$r)) {
return 1;
}
if (($txt1['left']/$r) < ($txt2['left']/$r)) {
return -1;
}
if (($txt1['left']/$r) > ($txt2['left']/$r)) {
return 1;
}
}
return 0;
});
$text = '';
$starty = false;
foreach ($list as $data) {
if ($data['top'] > $starty + 5) {
if ($starty > 0) {
$text .= "\n";
}
} else {
// Add space
// $text .= ' ';
}
$starty = $data['top'];
// Add text to current line
$text .= $data['data'];
}
// Remove extra spaces
$text = preg_replace('# +#', ' ', $text);
$template[$fieldName] = $text;
}
$paged[] = $template;
}
print_r($paged);
Run Code Online (Sandbox Code Playgroud)
结果(在多页PDF上)
Array
(
[0] => Array
(
[Address] => AQUARIUS NURSING HOME 4-6 SPENCER ROAD, SOUTHSEA PO4 9RN
[Medicine1] => ATORVASTATIN 40MG TABS
[Details1] => take ONE tablet at NIGHT
)
[1] => Array
(
[Address] => AQUARIUS NURSING HOME 4-6 SPENCER ROAD, SOUTHSEA PO4 9RN
[Medicine1] => SOTALOL 80MG TABS
[Details1] => take ONE tablet TWICE each day
DO NOT STOP TAKING UNLESS YOUR DOCTOR TELLS
YOU TO STOP.
)
[2] => Array
(
[Address] => AQUARIUS NURSING HOME 4-6 SPENCER ROAD, SOUTHSEA PO4 9RN
[Medicine1] => LAXIDO ORANGE SF 13.8G SACHETS
[Details1] => ONE to TWO when required
DISSOLVE OR MIX WITH WATER BEFORE TAKING.
NOT IN CASSETTE
)
Run Code Online (Sandbox Code Playgroud)
)
有时很难使用某些库或工具直接将 pdf 提取为所需的格式/输出。最近我也遇到了同样的问题,我有 1600 多个 pdf 文件,我需要提取这些数据并将其存储在数据库中。我尝试了几乎所有的库、工具,但没有一个对我有帮助。因此,我尝试手动查找模式并使用 php 处理它们。为此,我使用了这个 php 库PDF TO HTML。
安装 PDF TO HTML 库
composer require gufy/pdftohtml-php:~2
Run Code Online (Sandbox Code Playgroud)
这会将您的pdf转换为html代码,其中每个<div>标记代表页面,<p>标记代表标题及其值。现在,如果您可以识别常见模式,则使用p标签,并且不难将其放入处理所有 pdf 并将其转换为 csv/xls 或其他任何内容的逻辑中。因为在我的例子中,每11 个 <p>标签之后,该模式就会重复,所以我使用了这个。
$pdf = new Gufy\PdfToHtml\Pdf('<PDF_FILE_PATH>');
// get total no pages
$total_pages = $pdf->getPages();
// Iterate through each page and extract the p tags
for($i = 1; $i <= $total_pages; $i++){
// This will convert pdf to html
$html = $pdf->html($i);
// Create a dom document
$domOb = new DOMDocument();
// load html code in the dom document
$domOb->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
// Get SimpleXMLElement from Dom Node
$sxml = simplexml_import_dom($domOb);
// here you have the p tags
foreach ($sxml->body->div->p as $pTag) {
// your logic
}
}
Run Code Online (Sandbox Code Playgroud)
希望这对你有帮助,因为它对我帮助很大