Quartz 2D編程指南 (十七) —— PDF文件解析(一)

版本記錄

版本號 時間
V1.0 2018.09.08

前言

Quartz 2D框架相信大家都知道季惩,也都一直在使用。Quartz 2D的API是純C語言的,它是一個二維繪圖引擎敬鬓,同時支持iOS和Mac系統(tǒng)。Quartz 2D的API來自于Core Graphics框架笙各,數(shù)據(jù)類型和函數(shù)基本都以CG作為前綴钉答,接下來幾篇我們就一起來看一下這個框架。感興趣可以看上面幾篇文章杈抢。
1. Quartz 2D編程指南 (一) —— 簡介(一)
2. Quartz 2D編程指南 (二) —— Quartz 2D概覽(二)
3. Quartz 2D編程指南 (三) —— 圖形上下文(三)
4. Quartz 2D編程指南 (四) —— Paths路徑(一)
5. Quartz 2D編程指南 (五) —— Paths路徑(二)
6. Quartz 2D編程指南 (六) —— 顏色和顏色空間(一)
7. Quartz 2D編程指南 (七) —— 變換(一)
8. Quartz 2D編程指南 (八) —— Patterns圖案樣式(一)
9. Quartz 2D編程指南 (九) —— 陰影(一)
10. Quartz 2D編程指南 (十) —— 漸變(一)
11. Quartz 2D編程指南 (十一) —— 透明層(一)
12. Quartz 2D編程指南 (十二) —— Quartz 2D中的數(shù)據(jù)管理(一)
13. Quartz 2D編程指南 (十三) —— 位圖圖像和圖像蒙版(一)
14. Quartz 2D編程指南 (十四) —— 位圖圖像和圖像蒙版(二)
15. Quartz 2D編程指南 (十五) —— Core Graphics圖層繪制(一)
16. Quartz 2D編程指南 (十六) —— PDF文檔創(chuàng)建数尿,查看和轉(zhuǎn)換(一)

PDF Document Parsing - PDF文件解析

Quartz提供的函數(shù)可以讓您檢查PDF文檔結(jié)構(gòu)和內(nèi)容流。 通過檢查文檔結(jié)構(gòu)惶楼,您可以讀取文檔目錄中的條目以及與每個條目關(guān)聯(lián)的內(nèi)容右蹦。 通過遞歸遍歷目錄诊杆,您可以檢查整個文檔。

PDF內(nèi)容流正如其名稱所暗示的那樣 - 連續(xù)的數(shù)據(jù)流何陆,例如BT 12 /F71 Tf (draw this text) Tj . . .晨汹,PDF操作符及其描述符與實際PDF內(nèi)容混合在一起。 檢查內(nèi)容流需要您按順序訪問它贷盲。

本章介紹如何檢查PDF文檔的結(jié)構(gòu)并解析PDF文檔的內(nèi)容淘这。


Inspecting PDF Document Structure - 檢查PDF文檔結(jié)構(gòu)

PDF文件可能包含多頁圖像和文本。 您可以使用Quartz訪問文檔和頁面級別的元數(shù)據(jù)以及PDF頁面上的對象巩剖。 本節(jié)簡要介紹了您可以訪問的元數(shù)據(jù)铝穷。

PDF文檔對象(CGPDFDocument)包含與PDF文檔相關(guān)的所有信息,包括其目錄和內(nèi)容佳魔。 目錄中的條目以遞歸方式描述PDF文檔的內(nèi)容曙聂。 您可以通過調(diào)用函數(shù)CGPDFDocumentGetCatalog來訪問PDF文檔目錄的內(nèi)容。

PDF頁面對象(CGPDFPage)表示PDF文檔中的頁面鞠鲜,包含與特定頁面相關(guān)的信息筹陵,包括頁面字典和頁面內(nèi)容。 您可以通過調(diào)用函數(shù)CGPDFPageGetDictionary來獲取頁面字典镊尺。

圖14-1顯示了一些元數(shù)據(jù)朦佩,這些元數(shù)據(jù)描述了構(gòu)成圖13-2中顯示的PDF文件的兩個圖像 - 公雞的文本和圖像。

Figure 14-1 Metadata for two images in a PDF file

您可以通過訪問PDF元數(shù)據(jù)獲得更多有用的信息庐氮。 圖14-1中的項目只是一個示例语稠。 例如,您可以使用Listing 14-1中所示的代碼檢查PDF是否包含縮略圖圖像(如圖14-2所示)弄砍。

// Listing 14-1  Getting a thumbnail view of a PDF

CGPDFDictionaryRef d;
CGPDFStreamRef stream; // represents a sequence of bytes
d = CGPDFPageGetDictionary(page);
// check for thumbnail data
if (CGPDFDictionaryGetStream (d, “Thumb”, &stream)){
    // get the data if it exists
    data = CGPDFStreamCopyData (stream, &format);

Quartz為您執(zhí)行數(shù)據(jù)流的所有解密和解碼仙畦。

Figure 14-2 Thumbnail images

Quartz提供了許多函數(shù),您可以使用這些函數(shù)來獲取PDF元數(shù)據(jù)中項目的各個值音婶。 您使用函數(shù)CGPDFObjectGetValue慨畸,傳遞CGPDFObjectRef,PDF對象類型(kCGPDFObjectTypeBoolean衣式,kCGPDFObjectTypeInteger等)以及值的存儲寸士。 返回時,存儲空間將填充該值碴卧。

您可以使用許多其他函數(shù)遍歷PDF文件的層次結(jié)構(gòu)弱卡,以訪問各種節(jié)點及其子節(jié)點。 例如住册,CGPDFArray函數(shù)(CGPDFArrayGetBoolean婶博,CGPDFArrayGetDictionaryCGPDFArrayGetInteger等)允許您訪問值數(shù)組以檢索特定類型的值荧飞。 您可以通過閱讀PDF規(guī)范了解有關(guān)如何使用這些函數(shù)的更多信息凡人。


Parsing PDF Content - 解析PDF內(nèi)容

PDF內(nèi)容流包含表示應(yīng)用程序可能感興趣的PDF內(nèi)容流部分的運算符名党。操作符標記單個點或序列。將運算符指定為具有屬性列表或與其關(guān)聯(lián)的對象的標記挠轴。標簽指定點或內(nèi)容序列表示的內(nèi)容兑巾。屬性列表是包含由PDF內(nèi)容創(chuàng)建者指定的鍵值對的字典。解析PDF內(nèi)容流時忠荞,應(yīng)用程序會查找任何感興趣的標記蒋歌,檢查標記,屬性列表或與標記關(guān)聯(lián)的對象委煤,然后執(zhí)行適當?shù)娜魏芜M一步處理堂油。有關(guān)PDF運算符的完整列表,請參閱PDF Reference碧绞。

您使用CGPDFScanner對象(CGPDFScannerRef數(shù)據(jù)類型)來解析PDF內(nèi)容流府框。 CGPDFScanner對象為已注冊回調(diào)的流中的任何運算符調(diào)用回調(diào)。

您執(zhí)行以下部分中描述的任務(wù)來解析內(nèi)容流:

在適當?shù)那闆r下,您需要確保釋放scanner兴使,內(nèi)容流和操作符號表系宜。

以下部分顯示如何解析內(nèi)容流以查找標記內(nèi)容運算符(請參閱表14-1)。標記的內(nèi)容運算符僅代表PDF內(nèi)容中使用的一些PDF運算符发魄。編寫自己的代碼時盹牧,您將查找適合您的應(yīng)用程序的PDF運算符。

Table 14-1 Marked content operators represent some of the PDF operators that you can parse

1. Write Callbacks for Operators - 為操作符編寫回調(diào)

當Quartz調(diào)用PDF操作符的回調(diào)時励幼,它會傳遞一個CGPDFScanner對象和一個指向回調(diào)所需信息的指針汰寓。 通常,您的回調(diào)會檢索與該運算符關(guān)聯(lián)的所有項苹粟。 例如有滑,Listing 14-2中顯示的MP運算符的回調(diào)調(diào)用函數(shù)CGPDFScannerPopName以從堆棧中檢索與運算符關(guān)聯(lián)的字符串。 如果列表中的代碼成功從scanner堆棧中檢索名稱嵌削,則會打印該名稱毛好。

Quartz有各種各樣的CGPDFScannerPop函數(shù),用于檢索對象掷贾,布爾值睛榄,名稱,數(shù)字想帅,字符串,數(shù)組啡莉,字典和流港准。 每個函數(shù)返回一個布爾值旨剥,以指示是否成功檢索到該項。

// Listing 14-2  A callback for the MP operator

static void
op_MP (CGPDFScannerRef s, void *info)
{
    const char *name;
 
    if (!CGPDFScannerPopName(s, &name))
        return;
 
    printf("MP /%s\n", name);
}

2. Create and Set Up the Operator Table - 創(chuàng)建并設(shè)置操作符表

CGPDFOperatorTable對象存儲您編寫的PDF運算符回調(diào)函數(shù)浅缸。 函數(shù)CGPDFOperatorTableCreate創(chuàng)建一個運算符表轨帜,如Listing 14-3所示。 創(chuàng)建運算符表后衩椒,為要添加到表中的每個回調(diào)調(diào)用函數(shù)CGPDFOperatorTableSetCallback蚌父。 您傳遞表,指定PDF運算符的字符串毛萌,以及指向您處理該運算符的回調(diào)函數(shù)的指針苟弛。 您可以根據(jù)需要為回調(diào)命名。 只需確保傳遞給函數(shù)CGPDFOperatorTableSetCallback的回調(diào)名稱沒有拼寫錯誤阁将。

Listing 14-3中的代碼為表14-1中列出的每個標記內(nèi)容運算符設(shè)置了一個回調(diào)膏秫。 您的應(yīng)用程序?qū)H為感興趣的運算符設(shè)置回調(diào)。 PDF操作符字符串在PDF Reference from Adobe中定義做盅。

// Listing 14-3  Setting callbacks for an operator table

CGPDFOperatorTableRef myTable;
 
myTable = CGPDFOperatorTableCreate();
 
CGPDFOperatorTableSetCallback (myTable, "MP", &op_MP);
CGPDFOperatorTableSetCallback (myTable, "DP", &op_DP);
CGPDFOperatorTableSetCallback (myTable, "BMC", &op_BMC);
CGPDFOperatorTableSetCallback (myTable, "BDC", &op_BDC);
CGPDFOperatorTableSetCallback (myTable, "EMC", &op_EMC);

3. Open the PDF Document - 打開PDF文檔

在掃描PDF文檔的內(nèi)容之前缤削,您需要打開它。 Listing 14-4顯示了一個代碼片段吹榴,它從提供給代碼的URL創(chuàng)建CGPDFDocument對象亭敢。 請注意,列表是一個代碼片段图筹,因此并非所有變量都被聲明吨拗。 列表后面會顯示每個編號行代碼的詳細說明。

// Listing 14-4  Opening a PDF document from a URL

CGPDFDocumentRef myDocument;
myDocument = CGPDFDocumentCreateWithURL(url);// 1
if (myDocument == NULL) {// 2
        error ("can't open `%s'.", filename);
        CFRelease (url);
        return EXIT_FAILURE;
}
CFRelease (url);
if (CGPDFDocumentIsEncrypted (myDocument)) {// 3
    if (!CGPDFDocumentUnlockWithPassword (myDocument, "")) {
        printf ("Enter password: ");
        fflush (stdout);
        password = fgets(buffer, sizeof(buffer), stdin);
        if (password != NULL) {
            buffer[strlen(buffer) - 1] = '\0';
            if (!CGPDFDocumentUnlockWithPassword (myDocument, password))
                error("invalid password.");
        }
    }
}
if (!CGPDFDocumentIsUnlocked (myDocument)) {// 4
        error("can't unlock `%s'.", filename);
        CGPDFDocumentRelease(myDocument);
        return EXIT_FAILURE;
    }
}
 if (CGPDFDocumentGetNumberOfPages(myDocument) == 0) {// 5
        CGPDFDocumentRelease(myDocument);
        return EXIT_FAILURE;
}

這是代碼的作用:

  • 1) 從提供給代碼的URL創(chuàng)建CGPDFDocument對象婿斥。
  • 2) 檢查以確保創(chuàng)建了CGPDFDocument對象劝篷。 如果沒有,代碼退出是因為沒有文檔就沒有意義民宿。
  • 3) 檢查文檔是否已加密娇妓。 如果文檔已加密,則嘗試打開的代碼使用空白密碼活鹰。 如果失敗哈恰,代碼會詢問用戶密碼并嘗試使用密碼解鎖文檔。
  • 4) 檢查文檔是否已解鎖志群。 如果不是着绷,代碼退出。
  • 5) 檢查以確保文檔至少有一頁锌云。 否則荠医,代碼退出。

4. Scan the Content Stream for Each Page - 掃描每個頁面的內(nèi)容流

Listing 14-5中的代碼片段掃描文檔中的每個頁面。 當scanner遇到您為其注冊回調(diào)的PDF運算符之一時彬向,Quartz會調(diào)用您的回調(diào)兼贡。 列表后面的每個編號行代碼的詳細說明。

// Listing 14-5  Scanning each page of a document

int k;
CGPDFPageRef myPage;
CGPDFScannerRef myScanner;
CGPDFContentStreamRef myContentStream;
 
numOfPages = CGPDFDocumentGetNumberOfPages (myDocument);// 1
for (k = 0; k < numOfPages; k++) {
    myPage = CGPDFDocumentGetPage (myDocument, k + 1 );// 2
    myContentStream = CGPDFContentStreamCreateWithPage (myPage);// 3
    myScanner = CGPDFScannerCreate (myContentStream, myTable, NULL);// 4
    CGPDFScannerScan (myScanner);// 5
    CGPDFPageRelease (myPage);// 6
    CGPDFScannerRelease (myScanner);// 7
    CGPDFContentStreamRelease (myContentStream);// 8
 }
 CGPDFOperatorTableRelease(myTable);// 9

這是代碼的作用:

  • 1) 獲取先前打開的文檔中的頁數(shù)娃胆。 請參閱Open the PDF Document遍希。
  • 2) 檢索要掃描的頁面。 頁碼從1開始里烦。
  • 3) 為頁面創(chuàng)建內(nèi)容流凿蒜。
  • 4) 為內(nèi)容流創(chuàng)建掃描程序scanner。 您必須傳遞先前創(chuàng)建并使用回調(diào)設(shè)置的內(nèi)容流和操作符號表胁黑。 請參閱Create and Set Up the Operator Table废封。 您還可以傳遞回調(diào)所需的任何數(shù)據(jù)。
  • 5) 解析與scanner關(guān)聯(lián)的內(nèi)容流别厘。 每次遇到您提供回調(diào)的運算符之一時虱饿,Quartz都會調(diào)用您的回調(diào)。
  • 6) 釋放頁面触趴。
  • 7) 釋放scanner氮发。
  • 8) 釋放內(nèi)容流。
  • 9) 掃描PDF中的所有頁面后釋放操作符號表冗懦。

后記

本篇主要講述了PDF文件解析爽冕,感興趣的給個贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市披蕉,隨后出現(xiàn)的幾起案子颈畸,更是在濱河造成了極大的恐慌,老刑警劉巖没讲,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眯娱,死亡現(xiàn)場離奇詭異,居然都是意外死亡爬凑,警方通過查閱死者的電腦和手機徙缴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘁信,“玉大人于样,你說我怎么就攤上這事∨司福” “怎么了穿剖?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卦溢。 經(jīng)常有香客問我糊余,道長秀又,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任啄刹,我火速辦了婚禮涮坐,結(jié)果婚禮上凄贩,老公的妹妹穿的比我還像新娘誓军。我一直安慰自己,他們只是感情好疲扎,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布昵时。 她就那樣靜靜地躺著,像睡著了一般椒丧。 火紅的嫁衣襯著肌膚如雪壹甥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天壶熏,我揣著相機與錄音句柠,去河邊找鬼。 笑死棒假,一個胖子當著我的面吹牛溯职,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播帽哑,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼谜酒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了妻枕?” 一聲冷哼從身側(cè)響起僻族,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎屡谐,沒想到半個月后述么,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡愕掏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年度秘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亭珍。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡敷钾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肄梨,到底是詐尸還是另有隱情阻荒,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布众羡,位于F島的核電站侨赡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜羊壹,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一蓖宦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧油猫,春花似錦稠茂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至毡证,卻和暖如春电爹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背料睛。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工丐箩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人恤煞。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓屎勘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親阱州。 傳聞我的和親對象是個殘疾皇子挑秉,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容