2012-02-22 2 views
1

젠드 프레임 워크를 처음 접했기 때문에 뭔가 간단한 것이 누락되면 사과드립니다. 그러나 나는 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은 가장 유사하며 내 문제를 해결하지 못합니다. 필자가 여기에서 읽은 것과 다른 출처에서 볼 때 PDF 버전 1.4 이상은 정상적으로 작동 할 것입니다.하지만 여기서는 그렇지 않습니다. 내 PDF 버전은 모두 1.4이므로, 어쨌든 그 게시물이 얼마나 정확한지조차 모르겠습니다. 이 코드는 데모에 포함 된 PDF에는 적용되지만 사용하려는 기존 PDF에는 사용할 수 없습니다. 나는 PDF를 업로드 하겠지만 모두 기밀입니다.

메타 데이터를 가져 오려고하는데 문서를로드 할 수 없습니다. 프레임 워크를 사용하기 시작 했으므로 필자는 자체 파서를 만들 필요가 없었습니다. 이것을하는 더 쉬운 방법이 있거나, 누군가가 이것에 관해 밝힐 수 있다면, 나는 많은 의무가있을 것이다.

편집 : 설명을 위해 링크 된 문서 페이지에서 두 가지 방법을 시도했습니다. 둘 다 작동하지 않습니다.

+0

설정에 대해 더 자세히 알려 주시고 실제 코드를 게시하십시오. – markus

+0

나는 [documentation] (http://framework.zend.com/manual/en/zend.pdf.info.html)에서 그대로 복사하여 실제 코드를 게시했다. 설정에 대해 어떤 정보를 원하십니까? – mseancole

답변

4

나는 이것을 위해 내 자신의 파서를 만들어야 만했다. 누구든지 이걸 발견하고 내가 한 일에 대해 더 이상의 제안이나 질문이 있으면 그냥 코멘트를 추가하십시오.

나는 그것, 정말 긴 매우 지저분하고 비효율적으로 전체 코드를 업로드하지 않을거야

솔루션. 나는 초기 게시물 이후 개발자로서 조금 성장해 왔으며 돌아가서 다른 스윙을 취하는 것을 의미했습니다. 그래서 나는이 글을 가지고 내가 가지고있는 것을 설명하고 내가 발견 한 몇 가지 문제와 해결책을 지적 할뿐만 아니라 그것을보다 효율적으로 만드는 방법에 대한 의견을 피력 할 것이다. 다행히도 이것은 당신을 쉽게 해줄 것입니다. 바라건대 이것은 약간의 변화를 불러 일으킬 것입니다. 면책 조항 :이 코드를 마지막으로 살펴본 지 몇 달이 지났으므로 모든 것을 기억할 것으로 기대하지 마십시오. 그러나, 나는 나의 코드와 결과를 문서화하는 것에 대해 꽤 잘했다. 그래서 나는 기억하지 못하는 것이 거의 없다.

내가 말할 수있는 가장 중요한 것은 원시 XML을보고 메모를하고 파일 몇 개를 비교하는 것입니다. 어도비는 메타 데이터 구문을 만들 때 분명히 마음을 정할 수 없으므로 모든 다른 개정에 대해 여러 개의 체크를 추가해야 할 것입니다. 실제로 문서에서 메타 데이터를 찾는 것은 매우 쉽습니다. Adobe는 시작/끝 태그를 제공하므로 문서를 찾을 때까지 문서를 반복 할 수 있습니다. 다음은 내가 분석하고있는 PDF 중 하나에서 정리되고 일반화 된 샘플입니다. (당신이 프로그램과 같은 어떤 메모장을 사용할 수 있지만) 그에서 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 데이터를 볼 수있는 가장 좋은 방법은 ++ 메모장을 다운로드하는 것입니다. 가장 먼저 볼 수있는 것은 PDF 버전 인 "% PDF-1.4"입니다.이 경우에는 혼란스러워 보이는 문자가 많이 나옵니다. 무시하고 PDF 버전을 메모하십시오. 위의 샘플에서 "xpacket"태그를 확인하십시오. 메타 태그를 찾으려 할 때마다 찾아야합니다. Ctrl + F를 눌러 "xmpmeta"를 찾으면 첫 번째 항목은 메타 데이터 여야합니다. 주의 : 암호로 보호 된 문서를 사용하지 마십시오. meta를 포함하여 모든 것이 모호하다. 이것은 또한 PHP가 그것을 읽을 수 없다는 것을 의미한다. 패스워드로 보호 된 PDF 파일에서 메타를 읽을 수있는 옵션이 있다고 믿는다. 그러나 확실히 기억할 수는 없으며 실제로 PHP에서 작동하는지도 알 수 없다.

Ctrl + F로 메모장 + +에서 메타를 찾을 수있는 것처럼 PHP에서 fgets()과 while 루프를 사용하여 동일한 작업을 수행 할 수 있습니다. 내가하지는 않았지만 구현하는 것이 좋은 아이디어 일 것입니다. 문서의 어느 끝 부분부터 시작할지 결정하는 것입니다. 이것은 모든 PDF 버전간에 보편적이지는 않지만 동일한 버전이 유사하게 배치 된 것 같습니다. 예를 들어, PDF 1.4에서는 PDF 1.6에서 맨 위에 더 가깝게 보입니다. 다시 말하지만 첫 번째 줄에서 PDF 버전을 확인할 수 있습니다. PHP를 사용하여 문서를 읽는 것은 매우 간단해야하므로이 코드는 건너 뛸 것입니다. 그렇지만 전체 메타 데이터를 찾으면 루프를 종료하는 것이 좋습니다. 이는 매우 처리 강도가 높은 작업이므로 최대한 시간을 절약하고 싶을 것입니다. 또한 한 번에 10-20 개의 파일 그룹으로 만 실행하는 것이 좋습니다. 캐싱 시스템을 설정하면 타임 아웃 오류로 인해 상당한 도움이되었습니다.

문자열에서 메타 데이터를 얻은 후에는 약간 정리해야합니다. 가장 먼저해야 할 일은 XML 파서가 읽을 수 있도록 메타 데이터가 단일 루트 노드에 잘 정리되어 있는지 확인하는 것입니다. 그들이 없었던 몇 가지 사례가있었습니다. 가장 좋은 방법은 일반적인 래퍼를 추가하는 것입니다. 내가 사용할 수있는 가장 일반적인 것을 사용하는 것이 좋습니다. 저에게는 내부 "rdf"래퍼가있는 "xmpmeta"태그가있었습니다. 각 메타 데이터가 동일하게 시작하는지 확인하는 것은 문서를 탐색하는 데 중요합니다. 이 작업을 수행하는 더 좋은 방법이있을 수 있지만이 작동하고 너무 비효율적이지 않습니다 (적어도 두 개의 루프를 제거한 후 적어도 지금은).

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은 약간 다를 수 있습니다. 그것들은 제가 만난 모든 네임 스페이스입니다. 참고 : 노드를 제거하는 순서가 중요한 문제가있었습니다. 이유는 모르겠지만 "xmpMM"에서 "xmp"를 제거하면 "MM"네임 스페이스가 붙어 있습니다. 위의 코드는 그 문제가있는 것으로 보이지 않으므로 여전히 문제인지는 확실하지 않지만 다만주의해야합니다. 어느 쪽이든, 그것은 고치기가 너무 어렵지 않습니다, 그냥 PHP는 다음 그것을 반전 정렬합니다. REGEX는 기본 네임 스페이스 선언을 제거합니다. 이 문제를 해결하기 위해 여러 가지 방법을 시도했지만 이것이 일관되게 효과가 있다는 것을 알 수있는 유일한 방법이었습니다. 이 두 가지 REGEX 기능을 결합하는 방법이있을 수 있지만 REGEX와 관련해서는 완전히 잃어 버렸습니다. 왜 XML을 사용하여 네임 스페이스를 다시 제거 할 것인지 확신 할 수 없습니다. 이것은 좀 더 최근의 시도 중 하나 인 것처럼 보입니다. 그러나 이것이 작동하는 해결책에서 나온 것이기 때문에, 적어도 (기능은 아닐지라도) 상처를주지는 않습니다. REGEX 외에도 첫 번째 비트는 아마도 이것을 제거하지 않고 XML 솔루션으로 대체 될 수 있습니다. XML 구문 분석기가 "xmlns"속성을 실제 속성으로 간주하지 않기 때문에 문자열을 XML에로드하기 전에 기본 네임 스페이스를 제거해야합니다. 이름 공간 버전 "xmlns:$prefix"이 작동하는 유일한 이유는 "xmlns"속성으로 간주되지 않고 "xmlns:$prefix"속성으로 간주되기 때문입니다. 미묘함.

나를 좋아하지 마라. 모든 PDF 버전을 구현하려고 시도하지 마십시오. 그것은 할 수 없다. 글쎄 ... 가능할 수도 있지만 그 가치보다 더 번거 롭습니다. 운 좋게도, 이것들은 모두 사내 문서 였기 때문에, 내가 한계에 이르렀을 때, 다른 것을 끊거나, 이전에 가지고 있던 호환성을 잃어 버리는 것에 지쳤을 때, 나는 그 마지막 몇 개의 문서를 변환 시켰을뿐입니다. 가장 일반적인 버전을 찾아 이들을 처리 한 다음 그 다음으로 가장 일반적인 버전을 설정합니다.몇 가지만 남았을 때, 업데이트하거나,이 버전을 지원하지 않는다고 발표하면됩니다. 특히 나이가 많은 경우. 몇 가지 문서에 대해서만 사용할 수있는 기능을 추가 할 필요가 없습니다. 내가 기억할 수있는 큰 것 중 하나는 "x 패킷"이 항상 그 자체의 라인에 있지 않은 상황이다. 때로는 몇 개의 메타 데이터 태그가있는 공간을 공유합니다. "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

OpenOffice Writer의 PDF로 내보내기 기능으로 생성 된 PDF와 동일한 문제가 발생했습니다. Acrobat 또는 다른 PDF 리더에서는 문제없이 열리지 만 ZF에서는 처리 할 수 ​​없습니다. OpenOffice 파일을 .docs로 저장하고 MS Word로 .pdf로 내 보냈습니다. 이제 그들은 표시됩니다 ...

+0

몇 년 동안 다른 프로그램을 사용하여 제작했지만, 이제는 주요 제작자가 Framemaker 인 것으로 보입니다.나는 OO가 사용 된 적이 있다고 생각하지 않는다. – mseancole

0

나는 어도비와 함께 만든 PDF 문서와 동일한 문제가있었습니다.

이번에는 adobe의 표준 저장 옵션이 아니라 이번에 문서를 다시 저장했습니다. 이번에는 "최적화 된 PDF"(다른 Adobe 사전 설정으로 저장)로 문서로 저장했습니다.

이제 zend에서 파일을 열 수 있으며 정상적으로 작동합니다.

어떤 옵션이 프리셋에서 다른지는 잘 모르겠지만 zend에서 처리 할 수없는 일종의 스트리밍/분리 된 웹 버전이라고 생각합니다.

관련 문제