Pull to refresh

Текст любой ценой: DOCX и ODT

Reading time 4 min
Views 62K
Недавно возникла задача получения чистого текста из различных форматов документооборота — будь-то документы Microsoft Word или PDF. Задача была выполнена даже с чуть более широким списком возможных входных данных. Итак, этой статьёй я открываю список публикаций о чтении текста из следующих типов файлов: DOC, DOCX, RTF, ODT и PDF — с помощью PHP без использования сторонних утилит.

Для начала отвечу на вполне разумный вопрос: «Зачем это, собственно, надо?» Правильно, чистый текст, полученный из, к примеру, документа Word представляет собой достаточно перемешанную кашу. Но этого «бардака» вполне достаточно для построения, например, индекса для поиска по обширному хранилищу офисных документов.

Другой вполне разумный вопрос: «Почему не использовать сторонние утилиты, например, antiword или xpdf, ну или в крайнем случае OLE под Windows?» Таковы уж были поставленные условия, да и OLE работает люто-бешено медленно, даже если задачу можно решить с помощью этой технологии.

Сегодня, в качестве «затравки», я расскажу о достаточно простых для поставленной задачи форматах — это Office Open XML, больше известный как DOCX от Microsoft и OpenDocument Format, он же ODT от ODF Aliance.

Для начала заглянем вовнутрь парочки файлов и увидим буквально следующее (сзади docx, спереди odt):



Самое важное, что мы здесь видим, это первые два символа PK в начале данных. Это значит, что оба файла представляют собой переименованный в .docx/.odt zip-архив. Открываем, например, по Ctrl+PageDown в Total Commander и лицезреем вполне приемлемую структуру (слева odt, справа docx):



Итак, нужные нам файлы с данными — это content.xml в ODT и word/document.xml в DOCX. Чтобы прочитать текстовые данные из них напишем несложный код:

  1. function odt2text($filename) {
  2.     return getTextFromZippedXML($filename, "content.xml");
  3. }
  4. function docx2text($filename) {
  5.     return getTextFromZippedXML($filename, "word/document.xml");
  6. }
  7. function getTextFromZippedXML($archiveFile, $contentFile) {
  8.     // Создаёт "реинкарнацию" zip-архива...
  9.     $zip = new ZipArchive;
  10.     // И пытаемся открыть переданный zip-файл
  11.     if ($zip->open($archiveFile)) {
  12.         // В случае успеха ищем в архиве файл с данными
  13.         if (($index = $zip->locateName($contentFile)) !== false) {
  14.             // Если находим, то читаем его в строку
  15.             $content = $zip->getFromIndex($index);
  16.             // Закрываем zip-архив, он нам больше не нужен
  17.             $zip->close();
  18.  
  19.             // После этого подгружаем все entity и по возможности include'ы других файлов
  20.             // Проглатываем ошибки и предупреждения
  21.             $xml = DOMDocument::loadXML($content, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING);
  22.             // После чего возвращаем данные без XML-тегов форматирования
  23.  
  24.             return strip_tags($xml->saveXML());
  25.         }
  26.         $zip->close();
  27.     }
  28.     // Если что-то пошло не так, возвращаем пустую строку
  29.     return "";
  30. }
Всего каких-то 30 строк, и мы получаем текстовые данные из файла. Код работает под PHP 5.2+ и требует php_zip.dll под Windows или ключика --enable-zip под Linux. При отсутствии возможности использования ZipArchive (старая версия PHP или отсутствие библиотек) вполне может сгодиться библиотека PclZip, реализующая чтение zip-файлов без соответствующих средств в системе.

Отмечу, что данный код является лишь заготовкой для решения задач чтения текста. После череды статей под лозунгом «Текст любой ценой», я постараюсь описать принципы и реализацию чтения форматированного текста.

По теме:В следующий раз я расскажу о чтении текста из PDF без помощи xpdf. Более сложной, но вполне посильной для PHP задачи.
Tags:
Hubs:
+83
Comments 60
Comments Comments 60

Articles