从 pdf 文件中读取注释
Posted
技术标签:
【中文标题】从 pdf 文件中读取注释【英文标题】:Read annotation from pdf file 【发布时间】:2019-12-06 11:06:21 【问题描述】:我有一个 PDF 文件,其中包含注释和注释建议,这些注释会在鼠标悬停在带注释的单词上时出现。
例如,考虑上面的图像,其中您将使用的单词是删除线(表示不正确的单词),并且在鼠标悬停时会显示弹出窗口,其中出现了正确的单词。同样,还有另一个插入符号也是如此。
我想提取两个单词的列表,这将显示文件中正确和不正确的单词。
【问题讨论】:
我们有一个商业产品的演示(用 php 编写),它对高亮注释做同样的事情。将其调整为其他注释类型应该不难。但我只是想知道您希望插入符号的数据是什么? 插入符号类似于其他注释,鼠标悬停在插入符号上时会出现一个弹出模型,显示一些文本,如上图所示。我也对商业产品感兴趣。 查看此链接***.com/questions/1106098/…。它使用 python,但可能会为您指明正确的方向。如果您可以提取数据,您也许可以解析信息并过滤出您需要的内容。 你需要明确你想用什么语言来完成这个。这个问题被标记为 PHP 和 JS。 【参考方案1】:我刚刚使用我们的SetaPDF-Extractor 组件(我们的商业产品)做了一个简单的 POC,结果如下:
遗憾的是,PDF 中的 cmets“树”并不是那么微不足道。 POC 只是迭代注释并创建过滤器,然后提取器组件使用这些过滤器。 Here 是另一个提取 cmets 树的演示,它可能是排序/更合乎逻辑的结果的基础。
这是我用于给定输出的代码:
<?php
// load and register the autoload function
require_once('library/SetaPDF/Autoload.php');
// create a document instance
$document = SetaPDF_Core_Document::loadByFilename('camtown/Terms-and-Conditions - revised.pdf');
// initate an extractor instance
$extractor = new SetaPDF_Extractor($document);
// get page documents pages object
$pages = $document->getCatalog()->getPages();
// we are going to save the extracted text in this variable
$results = [];
// map pages and filternames to annotation instances
$annotationsByPageAndFilterName = [];
// iterate over all pages
for ($pageNo = 1, $pageCount = $pages->count(); $pageNo <= $pageCount; $pageNo++)
// get the page object
$page = $pages->getPage($pageNo);
// get the annotations
$annotations = array_filter($page->getAnnotations()->getAll(), function(SetaPDF_Core_Document_Page_Annotation $annotation)
switch ($annotation->getType())
case SetaPDF_Core_Document_Page_Annotation::TYPE_HIGHLIGHT:
case SetaPDF_Core_Document_Page_Annotation::TYPE_STRIKE_OUT:
case SetaPDF_Core_Document_Page_Annotation::TYPE_CARET:
case SetaPDF_Core_Document_Page_Annotation::TYPE_UNDERLINE:
return true;
return false;
);
// create a strategy instance
$strategy = new SetaPDF_Extractor_Strategy_ExactPlain();
// create a multi filter instance
$filter = new SetaPDF_Extractor_Filter_Multi();
// and pass it to the strategy
$strategy->setFilter($filter);
// iterate over all highlight annotations
foreach ($annotations AS $tmpId => $annotation)
/**
* @var SetaPDF_Core_Document_Page_Annotation_Highlight $annotation
*/
$name = 'P#' . $pageNo . '/HA#' . $tmpId;
if ($annotation->getName())
$name .= ' (' . $annotation->getName() . ')';
if ($annotation instanceof SetaPDF_Core_Document_Page_Annotation_TextMarkup)
// iterate over the quad points to setup our filter instances
$quadpoints = $annotation->getQuadPoints();
for ($pos = 0, $c = count($quadpoints); $pos < $c; $pos += 8)
$llx = min($quadpoints[$pos + 0], $quadpoints[$pos + 2], $quadpoints[$pos + 4], $quadpoints[$pos + 6]) - 1;
$urx = max($quadpoints[$pos + 0], $quadpoints[$pos + 2], $quadpoints[$pos + 4], $quadpoints[$pos + 6]) + 1;
$lly = min($quadpoints[$pos + 1], $quadpoints[$pos + 3], $quadpoints[$pos + 5], $quadpoints[$pos + 7]) - 1;
$ury = max($quadpoints[$pos + 1], $quadpoints[$pos + 3], $quadpoints[$pos + 5], $quadpoints[$pos + 7]) + 1;
// reduze it to a small line
$diff = ($ury - $lly) / 2;
$lly = $lly + $diff - 1;
$ury = $ury - $diff - 1;
// Add a new rectangle filter to the multi filter instance
$filter->addFilter(
new SetaPDF_Extractor_Filter_Rectangle(
new SetaPDF_Core_Geometry_Rectangle($llx, $lly, $urx, $ury),
SetaPDF_Extractor_Filter_Rectangle::MODE_CONTACT,
$name
)
);
$annotationsByPageAndFilterName[$pageNo][$name] = $annotation;
// if no filters for this page defined, ignore it
if (count($filter->getFilters()) === 0)
continue;
// pass the strategy to the extractor instance
$extractor->setStrategy($strategy);
// and get the results by the current page number
$result = $extractor->getResultByPageNumber($pageNo);
if ($result === '')
continue;
$results[$pageNo] = $result;
// debug output
foreach ($annotationsByPageAndFilterName AS $pageNo => $annotations)
echo '<h1>Page No #' . $pageNo . '</h1>';
echo '<table border="1"><tr><th>Name</th><th>Text</th><th>Subject</th><th>Comment</th></tr>';
foreach ($annotations AS $name => $annotation)
echo '<tr>';
echo '<td>' . $name . '</td>';
echo '<td><pre>' . ($results[$pageNo][$name] ?? '') . '</pre></td>';
echo '<td><pre>' . $annotation->getSubject() . '</pre></td>';
echo '<td><pre>' . $annotation->getContents() . '</pre></td>';
echo '</tr>';
echo '</table>';
【讨论】:
这个插件听起来不错,但我看到的是它没有提取插入符号。可以提取插入符号吗? 我还需要从头到尾提取单词的正确顺序。 实际上插入符号在表中作为“插入文本”。正如我已经在 PDF 中编写的注释处理不是微不足道的,并且插入符号例如就是这样一个特例。 “Cross-Out”是对“Inserted Text”注释的回复,在标准 PDF 查看器中形成单个注释。如果您将这样的文档传递给this demo,您将看到这一点。在任何情况下,我都建议请求组件here 的评估版本,以便您可以自己使用它。【参考方案2】:您需要提取有关页面上存在的标记注释及其关联的子弹出窗口(您称为“建议”)注释内容的信息。您可以使用标记注释的位置与页面上该位置显示的文本进行协调。然后,您将获得所需的两条信息。
【讨论】:
【参考方案3】:你试过这个解析器吗?
特点
加载和解析对象和标题 提取元数据(作者、描述、关键字等) 从有序页面中提取文本 支持压缩 pdf(和不支持) 支持字符集编码(WinAnsi、MacRoman) 处理六进制和八进制内容编码 符合 PSR-0 标准(自动加载器) 与作曲家兼容 符合 PSR-1(代码样式)https://pdfparser.org/demo
【讨论】:
是的,我试过 pdf Parser,它只是从 pdf 文件中提取内容。不是注释。 哦,我明白了,该死!以上是关于从 pdf 文件中读取注释的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 swift xcuitest 从 pdf 文件中读取数据