程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 用C#來解析PDF文件

用C#來解析PDF文件

編輯:C#入門知識

用C#來解析PDF文件。本站提示廣大學習愛好者:(用C#來解析PDF文件)文章只能為提供參考,不一定能成為您想要的結果。以下是用C#來解析PDF文件正文


2015712151420057.png (680×475)

1. 引見

這個項目讓你可以去讀取並解析一個PDF文件,並將其外部構造展現出來. PDF文件的格局尺度文檔可以從Adobe那兒獲得到. 這個項目基於“PDF指南,第六版,Adobe便攜文檔格局1.7 2006年11月”. 它是一個生怕有1310頁的年夜部頭. 本文供給了對這份文檔的簡練概述. 與此相干的項目界說了用來讀取息爭析PDF文件的C#類. 為了測試這些類,附帶的測試法式PdfFileAnalyzer讓你可以去讀取一個PDF文件,剖析它並展現和保留成果. 法式將PDF文件朋分成零丁每頁的描寫,字體,圖片和其它對象. 有兩品種型的PDF文件不受此法式的支撐: 加密文件和多代文件.

這個法式的1.1版本許可世界各地應用點符號作為小數分隔符的法式員來編譯和運轉法式.

1.2版本則修復了一個有關應用跨多個援用流來讀取PDF文檔的成績. 1.2之前的版本對此場景只會以一個對象數字反復的毛病而終止運轉.

2. 概要

PDF格局的文件,借助Adobe Acrobat軟件,可以在各類屏幕上顯示檢查,應用各類打印機打印。然則,假如應用二進制文件編纂器翻開PDF文件,你會發明文件的年夜部門是弗成讀的,有小部門是可讀的,以下:

 

1 0 obj
<</Lang(en-CA)/MarkInfo<</Marked true>>/Pages 2 0 R
/StructTreeRoot 10 0 R/Type/Catalog>>
endobj
2 0 obj
<</Count 1/Kids[4 0 R]/Type/Pages>>
endobj 
4 0 obj
<</Contents 5 0 R/Group <</CS/DeviceRGB /S/Transparency /Type/Group>>
/MediaBox[0 0 612 792] /Parent 2 0 R
/Resources <</Font <</F1 6 0 R /F2 8 0 R>>
/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>
/StructParents 0/Tabs/S/Type/Page>>
endobj
5 0 obj
<</Filter/FlateDecode/Length 2319>>
stream
. . .
endstream
endobj

看上去,該文件是由嵌套在“n 0 OBJ ”和“ endobj ”症結詞之間的對象構成的,術語PDF也就是直接對象的意思。 “obj”後面的數字是對象編號和第幾代對象標識, 雙尖括號中的內容表現數據字典對象,中括號中的內容表現數組對象, 以斜槓/ 開端的內容表現參數稱號 (例如: /Pages)。上例中的第一項 “1 0 obj” 表現文檔的目次或許文檔的根對象。文檔目次的字典對象 “/Pages 2 0 R”,指向界說頁碼樹對象的援用。依照如許推算,編號為2的對象包括指向 “/Kids[4 0 R]”的頁面的援用,是一個頁面文檔。 編號為4的對象是獨一的一個頁面界說, 頁面年夜小為612*792點, 換句話說,也就是8.5” * 11” (1” 代表72 點)點。該頁面應用了兩種字體F1和F2,這兩種字體分離在編號為6和8的對象中界說。該頁面的內容在編號為5的對象中描寫,該對象中包括頁面畫圖的流信息,示例中的 “. . .”代表這部門流信息。假如應用二進制文件編纂器翻開PDF文件,會發明這部門流信息看起來是一長串弗成讀的隨機數,緣由是那是緊縮數據。流數據采取Zlib辦法緊縮,緊縮方法由字典對象“/Filter /FlateDecode”描寫,被緊縮流的年夜小為2319字節。解壓這部門流信息,後面幾行內容以下所示:
 

q
37.08 56.424 537.84 679.18 re
W* n
/P <</MCID 0>> BDC 0.753 g
36.6 465.43 537.96 24.84 re
f*
EMC /P <</MCID 1/Lang (x-none)>> BDC BT
/F1 18 Tf
1 0 0 1 39.6 718.8 Tm
0 g
0 G
[(GRA)29(NOTECH LI)-3(MIT)-4(ED)] TJ
ET

這是頁面描寫說話的一個小例子。 示例中, “re” 代表矩形,“re” 後面的4個數字代表矩形的地位和年夜小,順次為:終點橫坐標、終點縱坐標、寬度、高度。


這個簡略的例子演示了PDF文件外部完成的整體思緒。從頁面條理構造的根對象開端, 每頁都界說了諸如字體、圖片、內容流的資本,內容流由操作符和繪制頁面所須要的參數組成。PDF文件剖析器會發生一個對象匯總文件,該文件包括非流對象的其他一切對象。每一個數據流會被解碼並保留為一個零丁的文件, 頁面描寫流保留為文本格局的文件, 圖片流保留為.jpg或.bmp格局的文件,字體流保留為.ttf格局的文件,其他二進制流保留為.bin 格局的文件,文本流保留為.txt格局的文件。經由過程另外一個解析進程,艱澀難明的頁面描寫會被轉換為偽C#代碼,如上例中的頁面描寫被轉為:
 

SaveGraphicsState(); // q
Rectangle(37.08, 56.424, 537.84, 679.18); // re
ClippingPathEvenOddRule(); // W*
NoPaint(); // n
BeginMarkedContentPropList("/P", "<</MCID 0>>"); // BDC
GrayLevelForNonStroking(0.753); // g
Rectangle(36.6, 465.43, 537.96, 24.84); // re
FillEvenOddRule(); // f*
EndMarkedContent(); // EMC
BeginMarkedContentPropList("/P", "<</Lang(x-none)/MCID 1>>"); // BDC
BeginText(); // BT
SelectFontAndSize("/F1", 18); // Tf
TextMatrix(1, 0, 0, 1, 39.6, 718.8); // Tm
GrayLevelForNonStroking(0); // g
GrayLevelForStroking(0); // G
ShowTextWithGlyphPos("[(GRA)29(NOTECH LI)-3(MIT)-4(ED)]"); // TJ
EndTextObject(); // ET

文章接上去的部門將對PDF文件的構造息爭析進程停止更加具體的描寫,接上去的章節包含:對象界說,文件構造,文件解析,文件讀取,和應用PDF文件剖析器編程。

3. 免責聲明

pdf 文件剖析器能處置年夜量的文件,這是我在本身的體系上掃描浩瀚PDF文件的經歷。不外,該法式不支撐加密文件或許多個代文件(在對象不為零之前的第二個數字)。在PDF規格文件當中可用功效的數目長短常明顯的。這其實不能夠為一個單的個開辟者體系地測試一切的功效。假如在全部文件剖析時代該法式拋出一個異常,將顯示一條毛病信息,該信息顯示源代碼模塊名和行號。
4.對象界說

PDF文件生成多個對象。在PDF文件剖析器項目中每一個PDF對象都有一個對應的類。一切這些對象類都派生於PDFbase類。對象類界說源代碼是BasicObjects.cs.確卻地PDF對象界說在Adobe pdf文件 規格第三章當中是有效的


4.1. 基本的對象

    Boolean對象是靠PdfBoolean類來完成的. Boolean在PDF上的界說同C#上的是雷同的.

    Integer 對象是靠PdfInt類來完成的. PDF上的界說同C#上Int32的界說是雷同的.

    實數對象是靠PdfReal類來完成的. PDF上的界說同C#上的Single界說雷同.

    String 對象是靠PdfStr類來完成的. PDF上的界說同C#比擬有所分歧. String 是用字節結構出來的,而不是字符. 它被包在圓括號()外面. PdfFileAnalyzer會把包括在圓括號中的C#字符串保留成PDF的字符串. PDF的字符串關於ASCII編碼異常有效.

    十六進制字符串獨享是靠PdfHex類來完成的. 它是由每字節兩個十六進制數界說,並包在尖括號外面的字符串. PdfFileAnalyzer 將包括在尖括號中的C#字符串保留成PDF十六進制字符串. 關於 PDF 讀取器,字符串和十六進制字符串對象可用於同種目標. 字符串 (AB) 同等於<4142>. PDF 十六進制字符串關於隨意率性編碼的場景異常有效.

    Name 對象是靠PdfName類來完成的. Name 對象是由打頭的正斜槓前面隨著一些字符構成的. 例如 /Width. Named 對象用作參數稱號. PdfFileAnalyzer 將正斜槓開首的C#字符串保留成Name對象.

    Null 對象是靠PdfNull類來完成的. PDF 關於null的界說根本上同C#中的是一樣的.

4.2. 復合的對象

    Array 對象是靠 PdfArray 類來完成的. PDF 數組是一個封裝在一堆中括號中的對象的聚集. 一個數組的對象可所以除流以外的任何對象.PdfFileAnalyzer 將一個C#數組中的對象保留成PdfBase類

    . 由於一切的對象都繼續自PdfBase,一切在這個數組中保留多品種型的對象沒有啥成績. 當數組對象被轉換成一個字符串時(應用ToString()辦法), 法式會在首位添加中括號. 數組可所以空的. 上面是一個有六個對象的數組示例: [120 9.56 true null (string) <414243>].

    Dictionary 對象是靠PdfDict類完成的. PDF 字典是一組被包入一對雙尖括號中的鍵值對聚集. Dictionary 的鍵是一個對象的稱號,而值則可所以除流以外的任何對象.  PdfFileAnalyzer 將一個鍵值對保留到PdfPair類中. 鍵是一個C#字符串,而值則是一個PdfBase.PdfDict 類有一個PdfPair類的數組. Dictionary 可以用鍵來拜訪. 因此鍵值對的次序沒有啥意義. PdfFileAnalyzer 用鍵來對鍵值對停止排序. 上面是一個有三個鍵值對的字典: <</CropBox [0 0 612 792] /Rotate 0 /Type /Page>>.

    Stream 對象是靠PdfStream來完成的. Streams 被用來處置面善說話,圖形和字體. PDF Stream 由一個字典和一個字撙節構成. 字典中界說了流的參數. 好比流對象中字典的一個鍵值對 /Filter. PDF 文檔界說了10品種型的過濾器. PdfFileAnalyzer 支撐了4種. 這是我發明在現實場景中只會被用到那4種. 緊縮過濾器 FlateDecode 是如今的PDF寫入器最長被用到的過濾器. FlateDecode支撐ZLib解緊縮. LZWDecode 緊縮過濾器在曩昔些年用的比擬多. 為了能讀取比擬老的PDF文件, 我們的法式支撐這個過濾器. ASCII85Decode 過濾器將可被打印的ASCII轉換成二進制位. DCTDecode 用於JPEG圖象的緊縮.PdfFileAnalyzer 為前三種完成懂得緊縮. DCTDecode 流則以文件擴大名.jpg保留. 它是一個可以被展現的圖片文件.

    Object 流在PDF 1.5中被引入. 它是一個包括多個直接對象(鄙人面會描寫道)的流. 下面描寫的Stream 對象一次只緊縮一個流. Object 流會將一切包括出去的流緊縮到一個緊縮域中.

    多援用流在PDF 1.5中被引入. 它是一個包括多援用表格的流,下文會描寫到.

    內聯圖片對象是靠 PdfInlineImage來完成的. 它是一個帶有一個流的流. 內聯圖片是頁面描寫說話的一部門. 它由BI-開首圖形, ID-圖形數據和EI-開頭圖形這三個操作符構成. BI 和 ID 之間的區域是一個圖形字典,而ID 和 EI 之間的區域則包括圖形數據.

4.3. 直接對象

    直接對象是靠 PdfIndirectObject完成的. 它是一個PDF文檔的重要結構塊. 直接對象是任何被包在 “n 0 obj” 和 “endobj”之間的對象. 其它對象可以經由過程設定“n 0 R”來援用直接對象. “n”代表對象編號. “0”代表生成編號. 這個法式不支撐0以外的生成編號. PDF 標准許可其它的編號. 多代生成的理念許可PDF的修正操作是在保存原有文件的基本上追加變革.

    對象援用時一種援用直接對象的辦法. 例如 /Pages 2 0 R 是目次對象中的字典裡的一項. 它是一個指向 /Pages 對象的指針. pages對象是編號為2的直接對象.

4.4. 操作符和症結詞

    操作符和症結詞不被以為是PDF對象. 而PdfFileAnalyzer 法式有一個PdfOp 和一個PdfKeyword 類可以從中獲得 PdfBase 的類. 在轉換進程中,轉換器為每個可用的字符序列創立了一個 PdfOp 或許PdfKeyword . Pdf文件標准的附錄A-操作符總結中列出了一切的操作符. 列表中有73個操作符. 上面是一些操作符的示例: BT-打頭的文本對象, G-用於做記號的設置灰度操作, m-挪動到, re-矩形和Tc-設置字符間距. 上面是症結詞的示例: stream, obj, endobj, xref.

5. 文件構造

PDF文件由四個部門組成: 頭部Header , 主體body, 多援用cross-reference 和附帶簽名 trailer signature.

  •     Header: 頭部是文件的簽名. 它必需是 %PDF-1.x , x 從 0 到 7.
  •     Body: 主體區域包括一切的直接對象.
  •     Cross-reference: 多援用是一個指向一切直接對象的文件地位指針列表. 有兩品種型的多援用表格. 原始的類型有ASCII字符構成. 舊式的是一個包括一個直接對象的流. 信息以二進制數字編碼. 在多援用表格的停止部門有一個附件字典. 一個文件可以有跨越一個的多援用區域.
  •     Trailer signature: 附帶簽名由症結詞“startxref”, 最初一個多援用表格的偏移位, 和停止簽名 %%EOF 構成. 請留意: 附帶簽名是多援用區域的一部門.

6. 文件轉換

PDF 文件是一個字節的序列. 一些字節有特別的意義.

空格被界說成: null, tab, 換行, 換頁, 回車和距離.

分隔符被界說成: (, ), <, >, [, ], {, }, /, %, 和空格字符.

文件轉換是由PdfParser 類來完成的. 開端停止轉換進程是,法式會設置文件須要被轉換區域的地位. ParseNextItem() 是提取下一個對象的辦法.


解析器跳過空格符和正文。假如下一個字節是“(”,斷定對象為一個字符串。假如下一個字節是“[”,斷定對象是一個數組。假如接上去的兩個字節是“<<”,斷定對象是一個字典。假如下一個字節是“<”,斷定對象是一個十六進制字符串。假如下一個字節是“/”,斷定對象是一個稱號。假如下一個字節不是上述任何一種,解析器會收集隨後的字節直到發明定界符。定界符不是以後標志符的一部門。標志符可所以整數,實數,操作符或症結詞。在整數的情形下,法式將進一步搜刮對象援用“n 0 R”或直接對象“n 0 obj”中 n 為該整數的對象。從 ParseNextItem() 前往的值是第4節“對象的界說”中所述的恰當對象。對象的類作為 PdfBase 類前往。

在數組或字典的情形下,法式將履行遞歸挪用 ParseNextItem() 來解析數組或字典的外部對象。

7. 文件讀取

PdfDocument 類是 PDF 文件剖析的重要類。進口辦法是 ReadPdfFile(String FileName)。法式以二進制讀取的方法翻開 PDF 文件(一次一個字節)。

文件剖析開端於檢討頭部簽名 %PDF-1.x(x為0到7)和開頭簽名%%EOF。有人會以為,一切的 PDF 生成器會把頭部簽名放在文件的零地位,開頭簽名放在文件的最初。不幸的是,現實並不是如斯。法式必需在文件的兩頭搜刮這兩個簽名。假如頭部簽名不在零地位,一切直接對象的文件地位的指針也必需調劑。

就在開頭簽名的後面有一個指向最初一個穿插援用表開端地位的指針。

解析器為多援用表設置文件地位. 假如下一個對象是“xref” 症結詞,我們就有了本來類型的多援用. 不然,它就是新的基於流的多援用. 文件可以有多個多援用表. 文件也能夠同時具有新的和舊的作風的表. 每個表都有一個對象數量和指向直接援用開首的指針的列表. 關於每個運動對象法式都邑創立一個PdfIndirectObject 對象並將其保留在 ObjectArray中. 除對象的數字和地位,這個對象的其它器械都是空的. 關於本來的多援用表,其地位是絕對於文件而言的. 關於流類型的多援用,地位是絕對於一個父直接對象流而言的.

在處置進程中,假如直接對象生成了0以外的數字, 法式的履行就會被終止. PdfFileAnalyzer 不支撐多代的情勢.


附件字典在穿插援用表的末尾處。剖析PDF文件的時刻,我們創立了一個帶負對象號的虛擬直接對象用於保留附件字典。

法式在附件字典中尋覓四個特定的進口。假如找到/Encrypt進口,表現PDF文件是被加密的,法式的將停止剖析,由於法式不支撐剖析加密格局的PDF文件。接著法式尋覓/Root目次對象的對象號。假如找到/XRefStm進口,我們就有了兩種穿插援用的類型。最初假如存在/Prev進口,我們有了另外一個用於處置的穿插援用表。

穿插援用的處置完成後,我們具有一切的直接對象的數組。  在處置階段,可用信息是對象號和對象地位。下一步,法式遍歷數組,讀取並解析每個直接對象,並設置對象的值。假如對象是流,僅字典部門被解析,由於在這個時刻還不曉得流的長度。除上述對象,假如字典和流對象的對象類型和子類型成員是可用的,體系將為字典和流對象設置這兩個值。


接上去法式遍歷一切的對象,並處置流對象。流對象的對象類型是"/ObjStm"。法式讀取和對象相干聯的流,並分化流到多個直接對象上。

接上去法式搜刮一切的字典對象和流對象援用的對象字典對象。法式查找鍵值對,例如“/name n 0 R”。參加鍵值對被找到,法式檢討對象類型。假如再對象解析階段沒有設置對象類型,對象類型將設置為/name值。

下一步,讀取一切後面沒有讀取的流。體系讀取從文件讀取流。流被解碼並保留到對應的文件中。PdfFileAnalyzer支撐以下的過濾:/FlateDecode,/LZWDecode, /ASCII85Decode和/DCTDecode。文本文件的擴大名是.txt,二進制文件的擴大名是.bin,圖片文件的擴大名是.jpg和.bmp,字體文件的擴大名是.ttf,穿插援用文件的擴大名是.xref。/FlateDecode是ZLib Deflate緊縮算法。
下一步是構建頁的內容。法式追隨從根開端的頁面樹。頁對象不是流對象。換句話說,頁描寫敕令是不克不及直接在也對象中的。頁對象字典有/Contents的鍵值對。假如不存在這個鍵值對,那末頁面就是空的。內容進口值可所以一個零丁的援用或許是一個運用數組。法式將為來自於一個或多個內容流的頁面創立虛擬的內容流。頁內容虛擬流保留在PageObj_xx.txt和PageSource_xx.txt中。PageObj_xx.txt是頁面的現實描寫內容。PageSource_xx.txt是將頁面的描寫內容轉換為偽C#源代碼。在第二節概要中,有這兩個文件的例子。

頁內容流是由參數和操作符構成的。例如矩形由四個實數描寫的,內嵌的圖片不遵守這個規矩。它的描寫是在第三節對象界說中。

最初,法式發生對象匯總文件ObjectSummary.txt。文件顯示一切簡介對象的信息不包括流。

8. PdfFileAnalyzer 法式

開辟運用法式 PdfFileAnalyzer 的目標是用來測試這個 PDF 文件解析類。假如你想在開辟情況以外測試它的可履行法式,需創立一個名為 PdfFileAnalyzer 的目次並復制 PdfFileAnalyzer.exe 到這個目次中,然後運轉這個法式。假如你想從 Visual C# 開辟情況中運轉這個項目,請確保你在“項目屬性”的“Debug”標簽欄中界說了一個任務目次。此法式是應用 Microsoft Visual C# 2012 開辟的。

運轉法式,可用的操作項有: Open, Setup 和 Exit.

法式初次履行時你必需應用 Setup 界說工程目次。這個目次盛放一切被剖析的 PDF 文件所發生的對應子目次。

Open 按鈕會顯示一個尺度的文件選擇對話框,你可以在個中找到你要停止剖析的 PDF 文件。


PDF文件剖析器界面將切換到類的匯總界面:

https://www.aspphp.online/bianchen/UploadFiles_4619/201707/2017072810123796.png (600×581)

每行代表一個直接的PDF對象。每列是:

  •     Object No. 直接對象號。關於附件字典來講dummy號,對象號是一個,對象號是正數時,在界面上顯示為TRn
  •     Ojbect 在第4節中界說的對象類型
  •     Type 假如對象是字典或許流,類型是/Type字典的值。假如類型不是字典或許字典不包括/Type,顯示值來自於對這個對象的直接援用
  •     Subtype 假如對象是字典或許流,或許字典包括/Subtype,將顯示在這一列
  •     Parent Object No. 假如直接對象是對象流的一部門(見4.2節復合對象),這一列顯示流對象的對象號
  •     Parent Index 假如直接對象是對象流的一部門,索引號是父對象流的號
  •     File Name 流對象和頁面臨象存在文件名。File Name是文件存儲在流對象內的名字。文件有以下的擴大名:.txt是文本文件,.bin是二進制文件,.bmp是圖片,.jpg是圖片,.ttf是字體,.xref是多援用流。假如剖析MyFile.PDF的流文件,工程目次的子目次MyFile將被指定在啟動界面上。頁面臨象不是流。文件表現這一頁一切對象的聯系關系關系
  •     Ojbect Position 假如直接對象文件不是對象流類型,這是對象在PDF文件內的地位。假如直接對象是對象流的一部門,這對象在父對象內的地位。地位依照十進制和十六進制數字顯示,便於法式員再二進制編纂器中檢查PDF文件
  •     Stream Position 和 Stream Length 流的地位和長度。流的地位是絕對於文件或許父對象的,同對象的地位應用雷同的盤算辦法


點擊Summary按鈕,檢查ObjectSummary.txt 文件。

選擇一行並點擊View按鈕或許雙擊一行後將顯示對象剖析界面,用於檢查直接對象的概況。

https://www.aspphp.online/bianchen/UploadFiles_4619/201707/2017072810123802.png (600×657)

關於一切的非流對象,後面的三個按鈕是不克不及點擊的。僅僅顯示對象本身的信息。你能用文本方法或許十六進制格局檢查這些信息。

關於流對象,第一個按鈕的名字是object type。前兩個按鈕object type和Stream許可你在檢查對象和流之間切換。Hex和Text按鈕許可你采取二進制格局或許文本格局檢查。假如是圖片流,文本格局顯示為四列:(1) 對象號,(2) 類型 (0-未應用,1-通俗對象,2-流對象),(3)通俗對象的地位和流對象的父對象,(4) 父對象的索引號。假如是二進制流(例如:字體),則僅能用十六進制格局檢查。

頁面臨象依照流對象來處置。一切內容對象的文本顯示是聯系關系的。別的,Source按鈕許可你檢查頁面在C#代碼中的描寫說話。

JPG圖片和BMP圖片可以扭轉偏向和調劑年夜小。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved