2012-02-22 4 views
1

Я новичок в Zend Framework, поэтому приношу свои извинения, если мне не хватает чего-то простого. Однако я бы подумал, что код, взятый непосредственно из documentation, будет работать. Вместо этого я получаю исключение.Перекрестные ссылки не поддерживаются

Fatal error: Uncaught exception 'Zend_Pdf_Exception' with message 'Cross-reference streams are not supported yet.' in C:\xampp\php\zend\library\Zend\Pdf\Parser.php:318 
Stack trace: 
#0 C:\xampp\php\zend\library\Zend\Pdf\Parser.php(460): Zend_Pdf_Parser->_loadXRefTable('116') 
#1 C:\xampp\php\zend\library\Zend\Pdf.php(318): Zend_Pdf_Parser->__construct('PDF/Current...', Object(Zend_Pdf_ElementFactory_Proxy), true) 
#2 C:\xampp\php\zend\library\Zend\Pdf.php(267): Zend_Pdf->__construct('PDF/Current...', NULL, true) 
#3 C:\xampp\htdocs\test\test.php(7): Zend_Pdf::load('PDF/Current...') 
#4 {main} 
    thrown in C:\xampp\php\zend\library\Zend\Pdf\Parser.php on line 318 

Я читал об этом в поисках возможного решения, но мало повезло. This является самым похожим, и это не решает мою проблему. Из того, что я там читал, и из других источников, версии 1.4 и старше PDF должны работать нормально, но здесь это не так, и его лет. Мои версии PDF - это все 1.4, поэтому я даже не уверен, насколько точна эта публикация. Код работает для PDF, включенного в демонстрационную версию, но не для любого из существующих, которые я пытаюсь использовать. Я бы загрузил PDF-файл, но все они конфиденциальны.

Я только пытаюсь получить метаданные, но я даже не могу загрузить документ. Я начал использовать фреймворк, поэтому мне не пришлось бы создавать собственный парсер. Если есть более простой способ сделать это, или если кто-то может пролить свет на это, я был бы очень обязан.

Редактировать: для уточнения, я пробовал оба метода со связанной страницы документации. Ничего не работает.

+0

Пожалуйста, расскажите нам больше о вашей настройке и опубликуйте фактический код. – markus

+0

Я действительно опубликовал фактический код, его стенографию из [документации] (http://framework.zend.com/manual/en/zend.pdf.info.html), скопированного непосредственно оттуда. Какую информацию вы хотели бы узнать о настройке? – mseancole

ответ

4

Для этого мне пришлось создать свой собственный парсер. Если кто-нибудь найдет это и у вас есть дополнительные предложения или вопросы о том, как я это сделал, просто добавьте комментарий.

Решение

Я не буду загружать весь код, как его очень долго, очень грязный, и неэффективно. Я вырос как разработчик с самого начала и имел смысл вернуться назад и еще раз поднять его. Поэтому я буду использовать этот пост, чтобы объяснить, что у меня есть, указать на некоторые проблемы и решения, которые я нашел, а также высказать некоторые замечания о том, как сделать его более эффективным. Надеюсь, это облегчит вам задачу, и, надеюсь, это вдохновит меня на внесение некоторых изменений. Отказ от ответственности: Прошло несколько месяцев с тех пор, как я последний раз посмотрел на этот код, поэтому не ожидайте, что я все вспомню. Тем не менее, я был довольно хорошо документировал свой код и результаты (на этот раз), поэтому то, что я не помню, в основном незначительно.

Самое главное, что я могу вам сказать, это посмотреть на необработанный XML-документ, сделать заметки и сравнить несколько ваших файлов. Adobe, по-видимому, не могла решить, создавая синтаксис метаданных, поэтому вам придется добавить несколько проверок для всех разных версий (я приведу пример позже). Фактически найти метаданные в документе довольно просто. Adobe дает вам хороший набор тегов начала и конца, поэтому вы просто перебираете документ, пока не найдете их. Вот очищенный и обобщенный образец из одного из файлов PDF, которые я разбираю.

<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> 
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04  "> 
    <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> 
     <rdf:Description rdf:about="" 
      xmlns:dc="http://purl.org/dc/elements/1.1/"> 
      <dc:format>application/pdf</dc:format> 
      <dc:title> 
       <rdf:Alt> 
        <rdf:li xml:lang="x-default">Title of Document</rdf:li> 
       </rdf:Alt> 
      </dc:title> 
      <dc:creator> 
       <rdf:Seq> 
        <rdf:li>Creator of Document (Not author)</rdf:li> 
       </rdf:Seq> 
      </dc:creator> 
      <dc:description> 
       <rdf:Alt> 
        <rdf:li xml:lang="x-default">Short description</rdf:li> 
       </rdf:Alt> 
      </dc:description> 
     </rdf:Description> 
     <rdf:Description rdf:about="" 
      xmlns:xmp="http://ns.adobe.com/xap/1.0/"> 
      <xmp:CreateDate>2004-01-27T16:36:09Z</xmp:CreateDate> 
      <xmp:CreatorTool>FrameMaker 7.0</xmp:CreatorTool> 
      <xmp:ModifyDate>2012-02-20T15:55:19Z</xmp:ModifyDate> 
     </rdf:Description> 
     <rdf:Description rdf:about="" 
      xmlns:pdf="http://ns.adobe.com/pdf/1.3/"> 
      <pdf:Producer>Acrobat Distiller 9.4.5 (Windows)</pdf:Producer> 
     </rdf:Description> 
     <rdf:Description rdf:about="" 
      xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"> 
      <xmpMM:DocumentID>uuid:4eae0fcf-f493-4773-9473-f81c7491e8aa</xmpMM:DocumentID> 
      <xmpMM:InstanceID>uuid:98209926-ba98-4ac7-a5f7-050050048f5d</xmpMM:InstanceID> 
     </rdf:Description> 
    </rdf:RDF> 
</x:xmpmeta> 
<?xpacket end="w"?> 

Лучший способ для просмотра исходных данных XML является загрузка Notepad ++ (хотя вы можете использовать любой блокнота, как программа) и открыть в PDF-х в этом. Первое, что вы увидите, это версия PDF, «% PDF-1.4» в этом случае, а затем много запутывающих выглядящих символов. Игнорируйте это, но обратите внимание на версию PDF. Обратите внимание на теги «xpacket» в приведенном выше примере, это то, что вам нужно будет искать каждый раз, когда вы захотите найти метаданные. Просто Ctrl + F, чтобы найти «xmpmeta», первое вхождение должно быть вашим метаданным. Осторожно: Не пытайтесь использовать защищенные паролем документы. Все запутывается, включая мета, это также означает, что PHP тоже не может его прочитать. Я считаю, что есть возможность разрешить чтение мета в защищенных паролем PDF-файлах, но я точно не помню, и не знаю, действительно ли это работает для PHP.

Так же, как вы можете Ctrl + F, чтобы найти мета в блокноте ++, вы можете сделать то же самое в PHP с помощью fgets() и цикла while. Что-то, чего я не делал, но, вероятно, было бы хорошей идеей для реализации, заключается в том, чтобы определить, с какого конца документа начать работу. Это не универсально для всех версий PDF, но одинаковые версии, похоже, аналогично размещены. Например, в PDF 1.4 они кажутся ближе к нижней части документа, в то время как в PDF 1.6 они ближе к вершине. Опять же, вы можете проверить версию PDF из первой строки. Чтение документа с помощью PHP должно быть довольно простым в настройке, поэтому я пропущу этот бит кода. Хотя, я укажу, что это хорошая идея, чтобы выйти из цикла, как только вы нашли все метаданные, так как это очень интенсивная операция обработки, поэтому вы хотите сэкономить время, где сможете. Я также предлагаю только запускать это по группам по 10-20 файлов за раз, меньше, если больше документов. Настройка системы кеширования помогла мне немного с ошибками тайм-аута.

После того, как вы получили метаданные в строке, вам нужно немного почистить ее. Первое, что вы захотите сделать, это убедиться, что ваши метаданные хорошо обернуты в одном корневом узле, так что синтаксический анализатор XML может его прочитать. Было несколько случаев, когда их не было. Лучший/самый простой способ исправить это - добавить общую оболочку. Я бы предложил использовать наиболее распространенный доступный вам. Для меня это был тег «xmpmeta» с внутренней оберткой «rdf». Обеспечение того, чтобы все метаданные начинались одинаково, важно для навигации по документу. Возможно, это лучший способ сделать это, но это работает и не слишком неэффективно (по крайней мере сейчас, после того, как я удалил две петли).

if(strpos($xmlstr, 'xmpmeta') === FALSE) { 
    if(strpos($xmlstr, 'rdf:rdf') === FALSE) { $xmlstr = "<rdf>$xmlstr</rdf>"; } 
    $xmlstr = "<xmpmeta>$xmlstr</xmpmeta>"; 
} 

После этого вы захотите удалить пространства имен. Я попытался использовать их, но это трудно сделать, когда URL-адреса продолжают меняться в каждой реализации, и вы точно не знаете, какие у вас есть. Кроме того, он уже начал работать медленно и добавил, что дополнительный синтаксический анализ XML только усугубил бы его. Их было намного проще удалить.

$nodesToRemove = array('rdf', 'pdf', 'xap', 'xapMM', 'xmp', 'xmpMM', 'dc', 'x'); 
foreach($nodesToRemove as $remove) { $xmlstr = str_replace("$remove:", '', $xmlstr); } 
$xmlstr = preg_replace('/xmlns[^=]*="[^"]*"/i', '', $xmlstr); 
$xmlstr = preg_replace("/xmlns[^=]*='[^']*'/i", '', $xmlstr); 

$dom = new DOMDocument(); 
$dom->loadXML($xmlstr); 
$sxe = simplexml_import_dom($dom); 
$root = $dom->documentElement; 
$namespaces = $sxe->getDocNamespaces(TRUE); 

foreach($namespaces as $prefix => $uri) { 
    $root->removeAttributeNS($uri, $prefix); 
    $root->removeAttribute("xmlns:$prefix"); 
} 

if($root->hasChildNodes()) { 
    foreach($root->childNodes as $element) { 
     if ($element->nodeType != XML_TEXT_NODE) { 
      $this->_removeNS($element, $namespaces); 
     } 
    } 
} 

$nodesToRemove может быть немного отличающимся для вас. Это всего лишь пространства имен, с которыми я сталкивался. Примечание: У меня были проблемы с тем, что порядок, в котором вы удаляете узлы, важен. Я не уверен, почему, но он удалит «xmp» из «xmpMM», и я застрял бы с пространством имен «ММ». Вышеприведенный код не имеет такой проблемы, поэтому я не уверен, что это проблема, но на всякий случай, будьте осторожны. В любом случае, это не так сложно исправить, просто попробуйте PHP, а затем отмените его. REGEX удаляет объявления пространства имен по умолчанию. Я попробовал несколько разных способов, но это был единственный, который я смог найти, что последовательно работал. Вероятно, есть способ объединить эти две функции REGEX, но я полностью потерял, когда дело доходит до REGEX, и мои попытки просто оставили его сломанным. Я не уверен, почему я снова удалю пространства имен с помощью XML. Это, по-видимому, одна из моих недавних попыток немного почистить, но это от рабочего решения, так что это не повредит (по крайней мере, не функциональность). Первый бит, помимо REGEX, возможно, может быть удален и заменен XML-решением, хотя я этого не проверял. По-прежнему необходимо удалить пространства имен по умолчанию перед загрузкой строки в XML, поскольку синтаксические анализаторы XML не считают атрибут «xmlns» фактическим атрибутом. Единственная причина, по которой работает версия с именами «xmlns:$prefix», заключается в том, что они не считаются атрибутами «xmlns», но атрибутами «xmlns:$prefix». Тонкости.

Не будьте похожими на меня. Не пытайтесь внедрять каждую версию PDF, когда-либо создаваемую. Это НЕ МОЖЕТ быть сделано. Ну ... это, вероятно, может, но это больше хлопот, чем его ценность. К счастью для меня, это были все внутренние документы, поэтому, когда я достиг своего предела и устал от настройки, просто чтобы сломать что-то еще или потерять совместимость, которую я ранее имел, у меня просто были конвертированы эти последние несколько документов. Найдите наиболее распространенные версии и обработайте их, затем следующие наиболее распространенные и настроенные условия для них и т. Д.Как только вы дойдете до того, что у вас осталось всего несколько левых, обновите их или просто сообщите, что вы не поддерживаете эту версию. Особенно, если они старше. Нет смысла добавлять функциональность для чего-то, что только когда-либо будет использоваться только для нескольких документов. Один из самых больших, который я помню, - это ситуация, когда «xpacket» не всегда был на собственной линии. Иногда это разделяло пространство с несколькими тегами метаданных. Это вызвало «отсутствующие» данные, потому что я не начал записывать мета до тех пор, пока не будет найден «xpacket». Это похоже на простое исправление, но у него появилось много проблем, поэтому я просто отказался от этой версии и обновил ее. К счастью, это были последние 3-4 файла.

После того как вы очистили метаданные, вы готовы проанализировать его как XML. Например, вот как я получаю описание.

function getDescription($xml) { 
    $return = 'Error: Metadata could not be retrieved';//Return value if metadata can not be parsed 

    $sxe = new SimpleXMLElement($xml); 

    $xpath = array(
     '//description/Alt/li', 
     '//Description/Alt/li', 
     '//xmpmeta/RDF/*[last()]', 
     //'//Description/description', 
    ); 
    foreach($xpath as $pattern) { 
     $temp = $sxe->xpath($pattern); 

     if(! empty($temp)) { 
      $return = isset($temp[0]->description) ? $temp[0]->description : $temp[0]; 
      break; 
     } 
    } 

    //Return value if description was not found in metadata 
    return empty($return) ? 'Error: Metadata "description" could not be retrieved' : strval($return); 
} 

Здесь есть несколько замечаний. Первый - это массив XPATH. Это те несколько условий, о которых я говорил раньше. Вы также можете заметить, что прокомментировал XPATH. Это то, что я либо все еще работаю над совместимостью, либо отказался. Я не помню, прошло какое-то время, так как мне пришлось смотреть на это, и никто не жаловался на ошибки. Поэтому я предполагаю, что это не проблема. Еще одно замечание - это количество отклонений только для этого ОДНОГО поля. Метаданные сильно изменились, а иногда и вернулись. Поэтому вам нужно проверить каждый случай, убедиться, что никаких других отклонений не было, а затем добавить любые другие условия, которые могли произойти. Что-то, на что нужно обратить внимание, было бы сохранение отдельных парсеров на основе версии, а затем загрузка соответствующего анализатора, может сократить неэффективность. Оглядываясь назад на это сейчас, возможно, проще было бы искать документы стандартизации для каждой ревизии, но вместо этого я делал это в основном посредством проб и ошибок. Итак, хотя это работает для меня, могут быть некоторые вещи, которые я пропустил, потому что это не было проблемой ни в одном из моих документов. Другое замечание - насколько сходны теги между версиями. Я не был, и все еще не так уж и хорош с продвинутым XPATH, так что, возможно, есть лучший способ сделать это, я не знаю.

Надеюсь, это поможет. Я знаю, что это дало мне несколько идей. Если у вас есть какие-то другие вопросы, дайте мне знать.

+0

Цените, если вы можете поделиться своим решением. –

+0

@PrasadRajapaksha: Обновленный ответ. Не полный код, но хорошее начало и объяснение. Причина отсутствия полного кода связана с размером, эффективностью и вероятностью быть слишком специфичными для моих потребностей. – mseancole

+0

Спасибо за сообщение об обновлении. –

2

Я столкнулся с той же проблемой с файлами PDF, сгенерированными экспортом OpenOffice Writer, в функцию PDF. В Acrobat или других PDF-читателях они открываются без проблем, но ZF не справляется с ними. Я сохранил файлы OpenOffice как .docs и экспортировал их в .pdf с помощью MS Word. Теперь они отображаются ...

+0

На протяжении многих лет для их создания использовалось несколько различных программ, но главным из них теперь является Framemaker.Я не думаю, что OO когда-либо использовалось. – mseancole

0

У меня была такая же проблема с pdf-документом, созданным с помощью Adobe.

Я повторно разместил документ на этот раз не со стандартными вариантами экономии. На этот раз я сохранил документ как «Оптимизированный PDF» (еще один предварительный набор adobe в разделе save as).

Теперь zend может открыть файл, и он отлично работает.

Я не совсем уверен, какие варианты отличаются в пресетов, но я думаю, что это какая-то потоковая/раздробленная веб-версия, которую zend не может обрабатывать.