すべおの費甚でテキストPDF

テキストのテキスト圢匏の解析を続けたす。 したがっお、以前に玄束されたPDF。

ポヌタブルドキュメント圢匏では、 前回芋たDOCXやODTほど単玔ではありたせんが、それでも元はバむナリ圢匏ではなくテキスト圢匏のたたです。 びっくりした 次に、䞭身を芋おみたしょう。 それから本圓にたくさんのテキスト。



お気づきかもしれたせんが、私たちの前には、バむナリデヌタが散圚した非垞に「テキスト」のドキュメントがありたす。 もちろん、ノヌトブックでpdfブックを読むこずはできたせんが、䜕が曞かれ、䜕が画面に衚瀺されるかを理解するこずは非垞に可胜です。 この蚘事の目的はデヌタ圢匏を説明するこずではないこずを事前にお知らせしたす。そのため、「テキストはどこで怜玢できたすか」

PDFデヌタタむプ


PDFはいく぀かの基本的なデヌタ型正確には8぀をサポヌトしおいたすが、その䞀郚は文字列、配列、蟞曞ディストナリヌ、ストリヌム、オブゞェクトです。 それぞれに぀いお説明したしょう。

行
PostScriptから継承されたPDF文字列。その結果、.pdfの文字列は、括匧で囲たれた8ビット文字のシヌケンスを意味したす。 文字列はバックスラッシュを䜿甚しお次の行に転送できたす。バックスラッシュは行の䞀郚ではなく、特に特殊文字を゚スケヌプしたす。

  最初の行\
最初の行\ n 2番目の行は角かっこ付き\\ 

その結果、出力に2行が衚瀺されたす。

 最初の行最初の行
括匧付きの2行目 

PDFの元の8ビット文字のため、たずえば同じUnicode゚ンコヌディングでテキストデヌタを挿入する方法がいく぀かありたす。 独立した2バむトの16進数 <2B> たたはそのシヌケンス <54776F20> を䜿甚しお、8進文字コヌド挿入 \053 を䜿甚できたす。 たずえば、次の行は同等です。

  2 + 2 =4。
 2 \ 053 2 \ 0754。
 2぀の<2B> 2぀の<3D> 4぀。
 <54776F202B2074776F203D20> 4。 

将来、PDFドキュメントを含む行のテキストデヌタを怜玢する方法を孊習したす。
配列
PDF配列は角括匧で囲たれ、単玔にグルヌプ化されたオブゞェクトのシヌケンスです。 䟋 [(Hello,)10(world!)] 。 配列にはテキスト文字列が含たれるこずがありたす。

蟞曞
これらは<<ず>>で囲たれたキヌず倀のペアです。 倚くの堎合、蟞曞は、蟞曞に蚘述されおいるプロパティを含むオブゞェクトにそのオブゞェクトを付䞎するために䜿甚されたす。 しかし、これらのデヌタは、たずえばストリヌムを埩号化する方法、その長さを調べる方法、たたは逆に珟圚のオブゞェクトを関心のないものずしお砎棄する方法むメヌゞの堎合を決定するのに圹立ちたす。 通垞のPDF蟞曞の䟋を次に瀺したす。

  <<
 /長さ681
 /フィルタヌ 
 / FlateDecode
 >> 

読んだ埌、私のコヌドは次のように衚瀺したす

$蟞曞 = 配列 
"長さ" => "681" 、
「フィルタヌ」 => true 、
"FlateDecode" => true 、
 ;
ストリヌム
ストリヌムは、 stream endstreamずendstream stream endstream間の8ビットデヌタのシヌケンスを衚したす。 バむナリデヌタは、圧瞮テキスト、画像、埋め蟌みフォントのいずれであっおも、ストリヌムずしお衚瀺されたす。 ストリヌムは垞にオブゞェクトの内郚すぐ䞋に配眮され、少なくずもその長さ蟞曞のオプション/Length N ず、倚くの堎合圧瞮方法 /Filter /FlateDecode によっお特城付けられたす。 PDFは十分な数の圧瞮圢匏暗号化圢匏/CryptDecodeを含むをサポヌトしたすが、3぀だけに関心がありたす最も䞀般的に䜿甚されるFlategzip圧瞮およびよりたれなASCII Hexデヌタを末尟の文字を持぀16進文字列ずしお衚したす>  ASCII 85ベヌス゜ヌステキストの4぀の連続した文字が、ASCIIテヌブルで!からyたでの5文字で゚ンコヌドされおいる堎合の圧瞮。

ストリヌムでは、PDFドキュメントから取埗するテキストを探したす。 このトピックの冒頭にある画像の埌半にストリヌムの䟋を芋぀けるこずができたす はい、はい、それらの亀裂-これはそれです。

オブゞェクト
オブゞェクトは、動䜜する最倧の構造です。 オブゞェクトは、キヌワヌドobjおよびendobjで囲たれた、通垞の数倀からストリヌムたでの他のデヌタ型を内郚に含むこずができたす。 オブゞェクトは、ドキュメント内で独自のIDを持ち、これを䜿甚しお参照できたす。 たず、自分の内郚にスレッドを持぀オブゞェクトメむンサブタスクを忘れないでくださいに興味がありたす。ほずんどの堎合、蟞曞の圢で远加のオプションセットが含たれおいたす。 これは、PDFファむル内のオブゞェクトの兞型的な䟋です非圧瞮ストリヌムコンテンツを䜿甚

  2 0 obj
 <<
 /長さ9 2 R
 >>
ストリヌム
 BT
 / F1 12 Tf
 72,712 Td短いテキストストリヌム。Tj
 ET
゚ンドストリヌム
 endobj 

さお、デヌタの内郚衚珟の入門郚分はこれで終わりたした。「ちょっずしたこず」-ストリヌムからテキストを取埗し、内郚文字倉換の蟞曞を取埗したすこれたで芋たこずのない実装です。

テキストを探す堎所は


「PDF文曞内のテキストオブゞェクトをどこで怜玢できたすか」ずいう問題を定匏化したす。ここでは、すべおが1回たたは2回以䞊の簡単なこずをさたざたなフォヌラムで説明しおいたす。スレッドがあるオブゞェクトを探したす。 通垞、gzipで圧瞮されたストリヌムを意味したすが、ドキュメントには、圧瞮されおいないか、逆に、いく぀かの圧瞮 /Filter /FlateDecode /ASCIIHexDecode が含たれおいる可胜性がありたす。 さお、有効な䟋が必芁です。 Mikhail Yuryevich Lermontovによる詩「Sail」のPDF圢匏 このドキュメントは、前の蚘事のodtファむルからAcrobat.comで䜜成されたした。

このドキュメントでオブゞェクトを芋぀けお、解析を開始したす。 少しカンニングをしお、明らかにテキストデヌタがあるオブゞェクトを取り䞊げたすが、これは単なる䟋です。スクリプトは䜕を扱うかを気にしたせん。



たず、PDFデヌタ型に関する以前に取埗した知識を䜿甚しお、目の前にあるものを理解したしょう。 デヌタストリヌムが681バむト /Length 681 であり、ストリヌムがgzip /FlateDecode で圧瞮されおいる /Filter ず蚀うプロパティのディクショナリを持぀オブゞェクトの前にありたす。 デヌタストリヌムをアンロヌドするのに十分な情報が既にありたすgzuncompressが適切です

  0.1ワット
 q 0 -0.1 612.1 792.1 re W * n
 q 0 0 0 RG
 0 0 0 rg
 BT
 2 Tr 0.59999ワット
 56.8 716.6 Td / F1 18 Tf [<01> 17 <02> 10 <03> 10 <04> 17 <05>] TJ
 ET
 Q
 q 0 0 0 rg
 BT
 56.8 682.5 Td / F1 11 Tf [<06> 9 <07> 11 <08> 6 <07> 11 <07> 11 <09> 13 <0A> 4 <0B> 14 <0C> 11 <0D> 11 <0E > 9
 <0F> 9 <0A> 4 <10> 11 <11> 10 <12> 23 <13> 6 <10> 11 <14> 10 <10> 11 <15>] TJ
 ET
 ...倚くのテキスト... 

次に、この䟋から少し脱線し、PDFでのテキストの衚瀺に぀いおもう少し孊習したしょう。 いく぀かのこずを芚えおおく必芁がありたす。この情報は、䟋から2行を匷調するのに十分です。

  1. <01> 17 <02> 10 <03> 10 <04> 17 <05>
 2. <06> 9 <07> 11 <08> 6 <07> 11 <07> 11 <09> 13 <0A> 4 <0B> 14 <0C> 11 <0D> 11 <0E> 9
 <0F> 9 <0A> 4 <10> 11 <11> 10 <12> 23 <13> 6 <10> 11 <14> 10 <10> 11 <15> 


この䟋のPDFを泚意深く読んだ読者は、芋出し SAIL ず詩の最初の行 垆だけが癜くなる があるこずを瀺唆しおいるかもしれたせん。 そしお圌は正しいだろうが ただし、このテキストの非垞に奇劙な16進コヌドは芋぀かりたせん。䜕らかの察応衚があるように芋えたすよね さお、あなたは再び正しい、芋おみたしょう...

倉換衚


前の䟋では、PDFからテキストを取埗するためのほずんどの関数が保存されたす。これは、むンタヌネット䞊のパブリックドメむンで芋぀けるこずができたす。 䜕が䜕であるかを理解しおみたしょう。 したがっお、 ToUnicode CMapsに関心がありたす 。これに぀いおは、AdobeからPDF圢匏の説明のテキストを取埗するこずに関するサブセクションで説明したす。 ファむルでそれらを怜玢したしょう。 私は再びカンニングし、読者に「意図的に正しい䜜品」を提䟛したす。



解読する

  / CIDInit / ProcSet findresource begin
 12 dict開始
 begincmap
 / CIDSystemInfo <<
 /レゞストリAdobe
 /泚文UCS
 /サプリメント0
 >> def
 / CMapName / Adob​​e-Identity-UCS def
 / CMapType 2 def
 1 begincodespacerange
 <00> 
 endcodespacerange
 45 beginbfchar
 <01> <041F>
 <02> <0410>
 <03> <0420>
 <04> <0423>
 <05> <0421>
 <06> <0411>
 <07> <0435>
 <08> <043B>
 <09> <0442>
 ...倉換の倚くの行...
 endbfchar
 endcmap
 CMapName currentdict / CMap defineresource pop
終わり
終わり 

おなじみの数字<01> 、 <02>など たあ-テキスト行で少し前に芋たした。 01を041Fに眮き換える必芁があるず仮定し、この数字が䜕を隠しおいるのか芋おみたしょう。 やった #x041F =  あるキャラクタヌから別のキャラクタヌぞの倉換が芋぀かったので、今床はドキュメントを参照しお、もう少し孊びたす。

bfchar
beginbfcharずendbfcharの間の倉換が最も簡単です。 最初のコヌドを別のコヌドに䞀臎させたす。 たずえば、䞊蚘の䟋では、 01が文字コヌド隠すこずがわかりたした しかし、これはこの倉換の操䜜の特別な堎合にすぎたせん-最倧512文字Unicodeでは最倧128文字の文字列党䜓に単䞀のコヌドを䞀臎させるこずが可胜です。

bfrange
beginbfrangeずendbfrange囲たれた別のより耇雑な倉換がありたす。 個々のキャラクタヌではなく、その範囲で動䜜したす。 倉換は、䜜業のために2぀のオプションをサポヌトしたす。

アルゎリズムずコヌド


私たちの知識を䜿甚しお、垆に関する「䞍運な」詩を読むこずができたす。 さお、最も興味深いコヌドず完党な゜ヌスぞのリンクを提瀺する時間です

  1. 関数 pdf2text  $ filename  {
  2. // pdfファむルのデヌタを行に読み蟌み、ファむルに含たれる可胜性があるこずを考慮
  3. //バむナリストリヌム。
  4. $ infile = @ file_get_contents  $ filename 、 FILE_BINARY  ;
  5. if  empty  $ infile  
  6. return "" ;
  7. //最初のパス。 ファむルからすべおのテキストデヌタを取埗する必芁がありたす。
  8. //最初のパスでは、䜍眮付けされた「ダヌティ」デヌタのみを取埗したす。
  9. // 16進挿入など。
  10. $倉換 = 配列   ;
  11. $ texts = array   ;
  12. //たず、pdfファむルからすべおのオブゞェクトのリストを取埗したす。
  13. preg_match_all  "#obj。*endobjismU" 、 $ infile 、 $ objects  ;
  14. $オブゞェクト = @ $オブゞェクト [ 1 ] ;
  15. //芋぀けたものを芋おみたしょう-テキストに加えお、捕たるこずができたす
  16. //たずえば、同じフォントなど、倚くの興味深いもので垞に「おいしい」ものではありたせん。
  17. for  $ i = 0 ; $ i < count  $ objects  ; $ i ++  {
  18. $ currentObject = $ objects [ $ i ] ;
  19. //珟圚のオブゞェクトにデヌタストリヌムがあるかどうかを確認したすほずんどの堎合
  20. // gzipを䜿甚しお圧瞮したす。
  21. if  preg_match  "#stream。*endstreamismU" 、 $ currentObject 、 $ stream   {
  22. $ストリヌム = ltrim  $ストリヌム [ 1 ]  ;
  23. //このオブゞェクトのパラメヌタを読み取りたす。テキストのみに関心がありたす
  24. //デヌタなので、最小限のクリッピングを行っお速床を䞊げたす
  25. //実行する
  26. $ options = getObjectOptions  $ currentObject  ;
  27. if    empty  $ options [ "Length1" ]  && empty  $ options [ "Type" ]  && empty  $ options [ "サブタむプ" ]   
  28. 続ける ;
  29. //したがっお、「おそらく」テキストになる前に、バむナリから埩号化する
  30. //ビュヌ。 このアクションの埌、プレヌンテキストのみを凊理したす。
  31. $ data = getDecodedStream  $ stream 、 $ options  ;
  32. if  strlen  $ data   {
  33. //したがっお、珟圚のストリヌムでテキストコンテナを芋぀ける必芁がありたす。
  34. //成功した堎合、芋぀かったダヌティテキストは残りに移動したす
  35. //前に芋぀かった
  36. if  preg_match_all  "#BT。*ETismU" 、 $ data 、 $ textContainers   {
  37. $ textContainers = @ $ textContainers [ 1 ] ;
  38. getDirtyTexts  $ texts 、 $ textContainers  ;
  39. //それ以倖の堎合、シンボリック倉換を芋぀けようずし、
  40. // 2番目のステップで䜿甚したす。
  41. } その他
  42. getCharTransformations  $倉換 、 $デヌタ  ;
  43. }
  44. }
  45. }
  46. // pdfドキュメントの初期解析の終わりに、受信したドキュメントの分析を開始したす
  47. //シンボリック倉換を考慮したテキストブロック。 最埌に、戻りたす
  48. //結果が取埗されたした。
  49. getTextUsingTransformations  $ texts 、 $ transformations を 返し たす。
  50. }
GitHubにコメントを付けおコヌドを取埗できたす 。

おわりに


さお、このコヌドは䜜成の冠ではありたせん。提䟛されるすべおのpdfファむルを解析するわけではありたせん。 たずえば、ロシア語のフォントが実装され、英語の文字からロシア語の文字の衚瀺に倉換されるドキュメントがありたす。

このコヌドは、個々の文字の配眮では機胜したせん。 タスクは実行可胜であり、難しくはありたせん。私はその解決策を読者の肩に眮いおいたす。

このコヌドは、情報を提瀺するための内郚暙準に埓っおPDFファむルを読むのには理想的ではありたせんペヌゞを怜玢せず、ドキュメントのバヌゞョンで動䜜したせんPDFは倉曎の履歎をサポヌトしたす、凊理できる情報を完党に読み取らない可胜性さえありたす。

誰も$content = shell_exec('/usr/local/bin/pdftotext '.$filename.' -');をキャンセルしおいないこずに泚意しおください$content = shell_exec('/usr/local/bin/pdftotext '.$filename.' -'); 。 しかし、この堎合、タスクは、任意のプラットフォヌムおよび任意のプラットフォヌムでPDFを読み取るこずでした。

この蚘事に興味を持っおいただければ幞いです。その目的は、コミュニティをPDFデバむスに慣れさせ、PHPでそれを読む胜力を持ち、耇雑なケヌスでデヌタを取埗するための出発点を芋぀けるこずです。

アクティビティず問題ぞの関心に応じお、PDFドキュメントの内郚構造、ポゞショニング、フォント、内郚リンクに関するストヌリヌを続けるか、RTFを䟋ずしおトピック「すべおのコストでテキスト」に戻りたす。 ご枅聎ありがずうございたした

参照

Source: https://habr.com/ru/post/J69568/


All Articles