Zend Lucene和PDF文档第2部分:PDF数据提取

上次我们使用Zend Framework查看和保存元数据并将其保存到PDF文档中。在尝试使用Zend Lucene对其进行索引之前,下一步是从文档本身中提取数据。在这里我应该指出,我们不能从每个PDF文档中完美地提取数据,我们当然不能从PDF中提取任何图像或表格到任何可识别的文本中。提取文本存在一个小问题,因为我们实质上是在查看压缩数据。文本不会保存到文档中,而是使用字体呈现到文档中。因此,我们需要做的是将这些数据提取为Lucene可以标记化的某种格式。因为我们只是从文档中获取文本作为搜索索引,所以我们可以采取一些捷径以便从文档中获取尽可能多的文本数据。所有这些数据可能都不是完全可读的,并且我们肯定会丢失所有格式和图像,但是出于我们实际使用它的目的,我们实际上并不需要它。我们的想法是,我们可以检索尽可能多的相关内容和可索引内容,以供Zend Lucene标记化。另外,也无法从加密的PDF文档中提取数据。

我们首先需要设置一些项目,以便我们可以简单地使用PDF提取服务来为我们完成艰苦的工作。这确实意味着对Zend Framework的理解要比最后要求的要多。我们要做的是向Zend_Loader_Autoloader注册一个名称空间。这将使我们能够创建可以保留在整洁的文件夹结构中的类,并在需要时自动将其包括在内。如果还没有,请_initAutoload()在Bootstrap.php文件中创建一个称为或类似函数。然后输入以下代码(为清楚起见,此处包括整个类)。您可能已经在Zend Framework项目中完成了此操作,因此,在这种情况下,您可以跳过此步骤。

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap

{

 protected function _initAutoload()

 {

  $autoloader = Zend_Loader_Autoloader::getInstance();

  $autoloader->registerNamespace(array('App_'));

 }

}

这是要注册一个名为App的文件夹,该文件夹位于我们的库文件夹中,作为Zend Framework自动加载功能的一部分。创建一个名为App_Search_Helper_PdfParser的类,并将其放入文件夹\ library \ App \ Search \ Helper \中,如下所示:

--application

--library

----App 

------Search

--------Helper

----------PdfParser.php

----Zend

现在,我们可以实例化该对象,而不必担心是否包含该对象,Zend Framework自动加载器将通过查看类名并为我们包含它,简单地在文件的正确位置查找该文件。我们将在应用程序的其余部分中使用此文件夹结构,并在添加类时以此为基础。

我们现在需要做的是创建将在我们的PDF文档上运行的代码并挑选出文本。我必须承认,我自己并没有完全写这篇文章,这是几个小时从示例和应用程序中挑选一些零碎代码的结果,以便我可以做我想做的事情。我已经用许多不同的PDF文档示例(从不同资源中抽取了大约50个)测试了此代码,因此它应该能够从大多数PDF类型中提取数据。这段代码本质上的作用是将文档分成多个不同的部分,然后尝试解压缩每个具有FlateDecode过滤器类型的部分。如果解压缩有效(即,我们有一些数据),则将其添加到字符串中并继续,并在文档末尾将其返回一次。我还在此代码中添加了一些字符串操作,该操作将去除我们不需要的任何奇数字符或空格。这是完整的类,这里又有很多代码,因此我对其进行了注释以使其更加清晰。

另外,由于使用了gzuncompress,您将需要服务器上存在一个zip库,此库才能正常运行。

class App_Search_Helper_PdfParser

{

    /**

     * Convert a PDF into text.

     *

     * @param string $filename The filename to extract the data from.

     * @return string The extracted text from the PDF

     */

    public function pdf2txt($data)

    {

        /**

         * Split apart the PDF document into sections. We will address each

         * section separately.

         */

        $a_obj = $this->getDataArray($data, "obj", "endobj");

        $j     = 0;

 

        /**

         * Attempt to extract each part of the PDF document into a "filter"

         * element and a "data" element. This can then be used to decode the

         * data.

         */

        foreach ($a_obj as $obj) {

            $a_filter = $this->getDataArray($obj, "<<", ">>");

            if (is_array($a_filter) && isset($a_filter[0])) {

                $a_chunks[$j]["filter"] = $a_filter[0];

                $a_data = $this->getDataArray($obj, "stream", "endstream");

                if (is_array($a_data) && isset($a_data[0])) {

                    $a_chunks[$j]["data"] = trim(substr($a_data[0], strlen("stream"), strlen($a_data[0]) - strlen("stream") - strlen("endstream")));

                }

                $j++;

            }

        }

 

        $result_data = NULL;

 

        // 解码块

        foreach ($a_chunks as $chunk) {

            // 查看每个块,通过查看过滤器的内容来决定是否可以对其进行解码

            if (isset($chunk["data"])) {

                // 查看过滤器以找出使用了哪种编码

                if (strpos($chunk["filter"], "FlateDecode") !== false) {

                    // 使用gzuncompress但抑制错误消息。

                    $data [email protected] gzuncompress($chunk["data"]);

                    if (trim($data) != "") {

                        // 如果我们得到数据,则尝试提取它。

                        $result_data .= ' ' . $this->ps2txt($data);

                    }

                }

            }

        }

        /**

         * Make sure we don't have large blocks of white space before and after

         * our string. Also extract alphanumerical information to reduce

         * redundant data.

         */

        $result_data = trim(preg_replace('/([^a-z0-9 ])/i', ' ', $result_data));

 

        // 返回从文档中提取的数据。

        if ($result_data == "") {

            return NULL;

        } else {

            return $result_data;

        }

    }

 

    /**

     * Strip out the text from a small chunk of data.

     *

     * @param  string $ps_data The chunk of data to convert.

     * @return string          The string extracted from the data.

     */

    public function ps2txt($ps_data)

    {

        // 停止此函数从非数据字符串返回假信息。

        if (ord($ps_data[0]) < 10) {

            return $ps_data;

        }

        if (substr($ps_data, 0, 8 ) == '/CIDInit') {

            return '';

        }

 

        $result = "";

 

        $a_data = $this->getDataArray($ps_data, "[", "]");

 

        // 提取数据。

        if (is_array($a_data)) {

            foreach ($a_data as $ps_text) {

                $a_text = $this->getDataArray($ps_text, "(", ")");

                if (is_array($a_text)) {

                    foreach ($a_text as $text) {

                        $result .= substr($text, 1, strlen($text) - 2);

                    }

                }

            }

        }

 

        // 没发现任何东西,尝试另一种提取数据的方式

        if (trim($result) == "") {

            // 数据可能只是原始格式(在[]标签之外)

            $a_text = $this->getDataArray($ps_data, "(", ")");

            if (is_array($a_text)) {

                foreach ($a_text as $text) {

                    $result .= substr($text, 1, strlen($text) - 2);

                }

            }

        }

 

        // 删除所有遗留的流浪字符。

        $result = preg_replace('/\b([^a|i])\b/i', ' ', $result);

        return trim($result);

    }

 

    /**

     * Convert a section of data into an array, separated by the start and end words.

     *

     * @param  string $data       The data.

     * @param  string $start_word The start of each section of data.

     * @param  string $end_word   The end of each section of data.

     * @return array              The array of data.

     */

    public function getDataArray($data, $start_word, $end_word)

    {

        $start    = 0;

        $end      = 0;

        $a_result = array();

 

        while ($start !== false && $end !== false) {

            $start = strpos($data, $start_word, $end);

            $end   = strpos($data, $end_word, $start);

            if ($end !== false && $start !== false) {

                // 数据在开始和结束之间

                $a_result[] = substr($data, $start, $end - $start + strlen($end_word));

            }

        }

 

        return $a_result;

    }

}

要在您的应用程序中使用此txt()方法,只需实例化该对象并调用pdf2方法,并将呈现的PDF字符串作为参数传递。我决定让Zend_Pdf对象将数据传输到类中,而不是让该对象第二次打开文件(在第一次打开以检查PDF数据之后)。以下代码显示了如何使用Zend_Pdf加载PDF并将呈现的字符串传递给pdf2txt()方法。

$pdf = Zend_Pdf::load($pdfPath);

$pdfParse = new App_Search_Helper_PdfParser();

$contents = $pdfParse->pdf2txt($pdf->render());

在此过程之后,我们应该剩下的是可以在搜索索引中使用的文本块。

在下一篇文章中,我将把元数据和内容检索结合在一起,并使用它们使用Zend Lucene索引我们的PDF文档。再次,我将在最后一期中提供该项目的所有源代码,如果需要,请继续关注。

  • Zend Lucene和PDF文档第1部分:PDF元数据

  • Zend Lucene和PDF文档第2部分:PDF数据提取

  • Zend Lucene和PDF文档第3部分:为文档建立索引

  • Zend Lucene和PDF文档第4部分:搜索

  • Zend Lucene和PDF文档第5部分:结论

以上是 Zend Lucene和PDF文档第2部分:PDF数据提取 的全部内容, 来源链接: utcz.com/z/331874.html

回到顶部