版本記錄
版本號 | 時間 |
---|---|
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文件的兩個圖像 - 公雞的文本和圖像。
您可以通過訪問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ù)流的所有解密和解碼仙畦。
Quartz
提供了許多函數(shù),您可以使用這些函數(shù)來獲取PDF元數(shù)據(jù)中項目的各個值音婶。 您使用函數(shù)CGPDFObjectGetValue
慨畸,傳遞CGPDFObjectRef
,PDF對象類型(kCGPDFObjectTypeBoolean
衣式,kCGPDFObjectTypeInteger
等)以及值的存儲寸士。 返回時,存儲空間將填充該值碴卧。
您可以使用許多其他函數(shù)遍歷PDF文件的層次結(jié)構(gòu)弱卡,以訪問各種節(jié)點及其子節(jié)點。 例如住册,CGPDFArray
函數(shù)(CGPDFArrayGetBoolean
婶博,CGPDFArrayGetDictionary
,CGPDFArrayGetInteger
等)允許您訪問值數(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)容流:
- 1)Write Callbacks for Operators讥邻。您只需要為要處理的運算符編寫回調(diào)迫靖。
- 2) Create and Set Up the Operator Table
- 3) Open the PDF Document
- 4) Scan the Content Stream for Each Page
在適當?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)注~~~