版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.09.08 |
前言
Quartz 2D
框架相信大家都知道,也都一直在使用。Quartz 2D
的API是纯C语言的,它是一个二维绘图引擎,同时支持iOS和Mac系统。Quartz 2D
的API来自于Core Graphics
框架,数据类型和函数基本都以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中的数据管理(一)
13. Quartz 2D编程指南 (十三) —— 位图图像和图像蒙版(一)
14. Quartz 2D编程指南 (十四) —— 位图图像和图像蒙版(二)
15. Quartz 2D编程指南 (十五) —— Core Graphics图层绘制(一)
16. Quartz 2D编程指南 (十六) —— PDF文档创建,查看和转换(一)
PDF Document Parsing - PDF文件解析
Quartz提供的函数可以让您检查PDF文档结构和内容流。 通过检查文档结构,您可以读取文档目录中的条目以及与每个条目关联的内容。 通过递归遍历目录,您可以检查整个文档。
PDF内容流正如其名称所暗示的那样 - 连续的数据流,例如BT 12 /F71 Tf (draw this text) Tj . . .
,PDF操作符及其描述符与实际PDF内容混合在一起。 检查内容流需要您按顺序访问它。
本章介绍如何检查PDF文档的结构并解析PDF文档的内容。
Inspecting PDF Document Structure - 检查PDF文档结构
PDF文件可能包含多页图像和文本。 您可以使用Quartz访问文档和页面级别的元数据以及PDF页面上的对象。 本节简要介绍了您可以访问的元数据。
PDF文档对象(CGPDFDocument)
包含与PDF文档相关的所有信息,包括其目录和内容。 目录中的条目以递归方式描述PDF文档的内容。 您可以通过调用函数CGPDFDocumentGetCatalog
来访问PDF文档目录的内容。
PDF页面对象(CGPDFPage)
表示PDF文档中的页面,包含与特定页面相关的信息,包括页面字典和页面内容。 您可以通过调用函数CGPDFPageGetDictionary
来获取页面字典。
图14-1显示了一些元数据,这些元数据描述了构成图13-2中显示的PDF文件的两个图像 - 公鸡的文本和图像。
您可以通过访问PDF元数据获得更多有用的信息。 图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为您执行数据流的所有解密和解码。
Quartz
提供了许多函数,您可以使用这些函数来获取PDF元数据中项目的各个值。 您使用函数CGPDFObjectGetValue
,传递CGPDFObjectRef
,PDF对象类型(kCGPDFObjectTypeBoolean
,kCGPDFObjectTypeInteger
等)以及值的存储。 返回时,存储空间将填充该值。
您可以使用许多其他函数遍历PDF文件的层次结构,以访问各种节点及其子节点。 例如,CGPDFArray
函数(CGPDFArrayGetBoolean
,CGPDFArrayGetDictionary
,CGPDFArrayGetInteger
等)允许您访问值数组以检索特定类型的值。 您可以通过阅读PDF规范了解有关如何使用这些函数的更多信息。
Parsing PDF Content - 解析PDF内容
PDF内容流包含表示应用程序可能感兴趣的PDF内容流部分的运算符。操作符标记单个点或序列。将运算符指定为具有属性列表或与其关联的对象的标记。标签指定点或内容序列表示的内容。属性列表是包含由PDF内容创建者指定的键值对的字典。解析PDF内容流时,应用程序会查找任何感兴趣的标记,检查标记,属性列表或与标记关联的对象,然后执行适当的任何进一步处理。有关PDF运算符的完整列表,请参阅PDF Reference
。
您使用CGPDFScanner
对象(CGPDFScannerRef数据类型)来解析PDF内容流。 CGPDFScanner
对象为已注册回调的流中的任何运算符调用回调。
您执行以下部分中描述的任务来解析内容流:
- 1)Write Callbacks for Operators。您只需要为要处理的运算符编写回调。
- 2) Create and Set Up the Operator Table
- 3) Open the PDF Document
- 4) Scan the Content Stream for Each Page
在适当的情况下,您需要确保释放scanner
,内容流和操作符号表。
以下部分显示如何解析内容流以查找标记内容运算符(请参阅表14-1)。标记的内容运算符仅代表PDF内容中使用的一些PDF运算符。编写自己的代码时,您将查找适合您的应用程序的PDF运算符。
Table 14-1 Marked content operators represent some of the PDF operators that you can parse
1. Write Callbacks for Operators - 为操作符编写回调
当Quartz调用PDF操作符的回调时,它会传递一个CGPDFScanner
对象和一个指向回调所需信息的指针。 通常,您的回调会检索与该运算符关联的所有项。 例如,Listing 14-2
中显示的MP
运算符的回调调用函数CGPDFScannerPopName
以从堆栈中检索与运算符关联的字符串。 如果列表中的代码成功从scanner
堆栈中检索名称,则会打印该名称。
Quartz有各种各样的CGPDFScannerPop
函数,用于检索对象,布尔值,名称,数字,字符串,数组,字典和流。 每个函数返回一个布尔值,以指示是否成功检索到该项。
// 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 - 创建并设置操作符表
CGPDFOperatorTable
对象存储您编写的PDF运算符回调函数。 函数CGPDFOperatorTableCreate
创建一个运算符表,如Listing 14-3
所示。 创建运算符表后,为要添加到表中的每个回调调用函数CGPDFOperatorTableSetCallback
。 您传递表,指定PDF运算符的字符串,以及指向您处理该运算符的回调函数的指针。 您可以根据需要为回调命名。 只需确保传递给函数CGPDFOperatorTableSetCallback
的回调名称没有拼写错误。
Listing 14-3
中的代码为表14-1中列出的每个标记内容运算符设置了一个回调。 您的应用程序将仅为感兴趣的运算符设置回调。 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文档的内容之前,您需要打开它。 Listing 14-4
显示了一个代码片段,它从提供给代码的URL创建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创建
CGPDFDocument
对象。 - 2) 检查以确保创建了
CGPDFDocument
对象。 如果没有,代码退出是因为没有文档就没有意义。 - 3) 检查文档是否已加密。 如果文档已加密,则尝试打开的代码使用空白密码。 如果失败,代码会询问用户密码并尝试使用密码解锁文档。
- 4) 检查文档是否已解锁。 如果不是,代码退出。
- 5) 检查以确保文档至少有一页。 否则,代码退出。
4. Scan the Content Stream for Each Page - 扫描每个页面的内容流
Listing 14-5
中的代码片段扫描文档中的每个页面。 当scanner
遇到您为其注册回调的PDF运算符之一时,Quartz会调用您的回调。 列表后面的每个编号行代码的详细说明。
// 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) 获取先前打开的文档中的页数。 请参阅Open the PDF Document。
- 2) 检索要扫描的页面。 页码从1开始。
- 3) 为页面创建内容流。
- 4) 为内容流创建扫描程序
scanner
。 您必须传递先前创建并使用回调设置的内容流和操作符号表。 请参阅Create and Set Up the Operator Table。 您还可以传递回调所需的任何数据。 - 5) 解析与
scanner
关联的内容流。 每次遇到您提供回调的运算符之一时,Quartz都会调用您的回调。 - 6) 释放页面。
- 7) 释放
scanner
。 - 8) 释放内容流。
- 9) 扫描PDF中的所有页面后释放操作符号表。
后记
本篇主要讲述了PDF文件解析,感兴趣的给个赞或者关注~~~