Quartz 2D编程指南 (十六) —— PDF文档创建,查看和转换(一)

版本记录

版本号 时间
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图层绘制(一)

PDF Document Creation, Viewing, and Transforming - PDF文档创建,查看和转换

PDF文档将与分辨率无关的矢量图形,文本和图像存储为以紧凑编程语言编写的一系列命令。 PDF文档可以包含多页图像和文本。 PDF对于创建跨平台,只读文档和绘制与分辨率无关的图形非常有用。

对于所有应用程序,Quartz为保留应用程序绘图操作创建高保真PDF文档,如图13-1所示。 生成的PDF可以针对特定用途(例如特定打印机或Web)针对系统的其他部分或第三方产品进行优化。 Quartz产生的PDF文档可以在PreviewAcrobat中正确显示。

Figure 13-1 Quartz creates high-quality PDF documents

Quartz不仅使用PDF作为其“数字纸”,还包括作为其API的一部分,可用于显示和生成PDF文件以及完成许多其他PDF相关任务的许多功能。

有关PDF的详细信息,包括PDF语言和语法,请参阅PDF Reference, Fourth Edition, Version 1.5


Opening and Viewing a PDF - 打开和显示一个PDF

Quartz提供数据类型CGPDFDocumentRef来表示PDF文档。 使用函数CGPDFDocumentCreateWithProvider或函数CGPDFDocumentCreateWithURL创建CGPDFDocument对象。 创建CGPDFDocument对象后,可以将其绘制到图形上下文。 图13-2显示了窗口内显示的PDF文档。

Figure 13-2 A PDF document

Listing 13-1显示了如何创建CGPDFDocument对象并获取文档中的页数。 列表后面会显示每个编号行代码的详细说明。

// Listing 13-1  Creating a CGPDFDocument object from a PDF file

CGPDFDocumentRef MyGetPDFDocumentRef (const char *filename)
{
    CFStringRef path;
    CFURLRef url;
    CGPDFDocumentRef document;
    size_t count;
 
    path = CFStringCreateWithCString (NULL, filename,
                         kCFStringEncodingUTF8);
    url = CFURLCreateWithFileSystemPath (NULL, path, // 1
                        kCFURLPOSIXPathStyle, 0);
    CFRelease (path);
    document = CGPDFDocumentCreateWithURL (url);// 2
    CFRelease(url);
    count = CGPDFDocumentGetNumberOfPages (document);// 3
    if (count == 0) {
        printf("`%s' needs at least one page!", filename);
        return NULL;
    }
    return document;
}

这是代码的作用:

  • 1) 调用Core Foundation函数从CFString对象创建CFURL对象,该对象表示要显示的PDF文件的文件名。
  • 2) 从CFURL对象创建CGPDFDocument对象。
  • 3) 获取PDF中的页数,以便代码中的下一个语句可以确保文档至少有一个页面。

通过查看Listing 13-2中的代码,您可以了解如何将PDF页面绘制到图形上下文。 列表后面会显示每个编号行代码的详细说明。

// Listing 13-2  Drawing a PDF page

void MyDisplayPDFPage (CGContextRef myContext,
                    size_t pageNumber,
                    const char *filename)
{
    CGPDFDocumentRef document;
    CGPDFPageRef page;
 
    document = MyGetPDFDocumentRef (filename);// 1
    page = CGPDFDocumentGetPage (document, pageNumber);// 2
    CGContextDrawPDFPage (myContext, page);// 3
    CGPDFDocumentRelease (document);// 4
}

这是代码的作用:

  • 1) 调用您的函数(参见Listing 13-1)从您提供的文件名创建CGPDFDocument对象。
  • 2) 从PDF文档中获取指定页码的页面。
  • 3) 通过调用函数CGContextDrawPDFPage从PDF文件中绘制指定的页面。 您需要提供图形上下文和要绘制的页面。
  • 4) 释放CGPDFDocument对象。

Creating a Transform for a PDF Page - 为PDF页面创建转换

Quartz提供了一个函数-CGPDFPageGetDrawingTransform,它通过将PDF页面中的框映射到您指定的矩形来创建仿射变换。 该函数的原型是:

CGAffineTransform CGPDFPageGetDrawingTransform (
        CGPPageRef page,
        CGPDFBox box,
        CGRect rect,
        int rotate,
        bool preserveAspectRatio
);

该函数使用以下算法返回仿射变换:

  • 将与您在box参数(media,crop,bleed,trim或art)中指定的PDF框类型相关联的矩形与指定PDF页面的/ MediaBox条目相交。交叉点产生有效的矩形。
  • 按照PDF页面的/ Rotate条目指定的量旋转有效矩形。
  • 将生成的矩形居中放在您在rect参数中提供的矩形上。
  • 如果您提供的rotate参数的值为非零且为90的倍数,则该函数会将有效矩形旋转您提供的度数。正值将矩形向右旋转,负值将矩形向左旋转。请注意,您传递度数,而不是弧度。请记住,PDF页面的/ Rotate条目也包含旋转,您提供的rotate参数与/ Rotate条目组合在一起。
  • 如有必要,缩放有效矩形,使其与您提供的矩形的边缘重合。
  • 如果通过在preserveAspectRatio参数中传递true来指定保留纵横比,则最终矩形与您在rect参数中提供的矩形的限制性更大的维度的边缘重合。

例如,如果您正在编写类似于图13-3中所示的PDF查看应用程序,则可以使用此函数。如果要提供“向左旋转/向右旋转”功能,则可以调用CGPDFPageGetDrawingTransform来计算当前窗口大小和旋转设置的相应变换。

Figure 13-3 A PDF page rotated 90 degrees to the right

Listing 13-3显示了一个函数,该函数使用传递给函数的参数为PDF页面创建仿射变换,应用变换,然后绘制PDF页面。 列表后面会显示每个编号行代码的详细说明。

// Listing 13-3  Creating an affine transform for a PDF page

void MyDrawPDFPageInRect (CGContextRef context,
                    CGPDFPageRef page,
                    CGPDFBox box,
                    CGRect rect,
                    int rotation,
                    bool preserveAspectRatio)
{
    CGAffineTransform m;
 
    m = CGPDFPageGetDrawingTransform (page, box, rect, rotation,// 1
                                    preserveAspectRato);
    CGContextSaveGState (context);// 2
    CGContextConcatCTM (context, m);// 3
    CGContextClipToRect (context,CGPDFPageGetBoxRect (page, box));// 4
    CGContextDrawPDFPage (context, page);// 5
    CGContextRestoreGState (context);// 6
}

这是代码的作用:

  • 1)根据提供给函数的参数创建仿射变换。
  • 2)保存图形状态。
  • 3)将CTM与仿射变换连接起来。
  • 4)将图形上下文剪切为box参数指定的矩形。 函数CGPDFPageGetBoxRect获取与您提供的常量相关联的页面边界框(media, crop, bleed, trim, and art boxes) - kCGPDFMediaBoxkCGPDFCropBoxkCGPDFBleedBoxkCGPDFTrimBoxkCGPDFArtBox
  • 5)将PDF页面绘制到已变换和剪切的上下文中。
  • 6)恢复图形状态。

Creating a PDF File - 创建PDF文件

使用Quartz 2D创建PDF文件非常容易,因为它可以绘制到任何图形上下文。 您可以指定PDF文件的位置,设置PDF图形上下文,并使用与任何图形上下文相同的绘图例程。 Listing 13-4中显示的函数MyCreatePDFFile显示了代码为创建PDF文件而执行的所有任务。 列表后面会显示每个编号行代码的详细说明。

请注意,代码通过调用函数CGPDFContextBeginPageCGPDFContextEndPage来描述PDF页面。 您可以传递CFDictionary对象以指定页面属性,包括media, crop, bleed, trim, and art boxes。 有关字典键常量的列表以及每个常量的更详细说明,请参阅CGPDFContext Reference

// Listing 13-4  Creating a PDF file

void MyCreatePDFFile (CGRect pageRect, const char *filename)// 1
{
    CGContextRef pdfContext;
    CFStringRef path;
    CFURLRef url;
    CFDataRef boxData = NULL;
    CFMutableDictionaryRef myDictionary = NULL;
    CFMutableDictionaryRef pageDictionary = NULL;
 
    path = CFStringCreateWithCString (NULL, filename, // 2
                                kCFStringEncodingUTF8);
    url = CFURLCreateWithFileSystemPath (NULL, path, // 3
                     kCFURLPOSIXPathStyle, 0);
    CFRelease (path);
    myDictionary = CFDictionaryCreateMutable(NULL, 0,
                        &kCFTypeDictionaryKeyCallBacks,
                        &kCFTypeDictionaryValueCallBacks); // 4
    CFDictionarySetValue(myDictionary, kCGPDFContextTitle, CFSTR("My PDF File"));
    CFDictionarySetValue(myDictionary, kCGPDFContextCreator, CFSTR("My Name"));
    pdfContext = CGPDFContextCreateWithURL (url, &pageRect, myDictionary); // 5
    CFRelease(myDictionary);
    CFRelease(url);
    pageDictionary = CFDictionaryCreateMutable(NULL, 0,
                        &kCFTypeDictionaryKeyCallBacks,
                        &kCFTypeDictionaryValueCallBacks); // 6
    boxData = CFDataCreate(NULL,(const UInt8 *)&pageRect, sizeof (CGRect));
    CFDictionarySetValue(pageDictionary, kCGPDFContextMediaBox, boxData);
    CGPDFContextBeginPage (pdfContext, pageDictionary); // 7
    myDrawContent (pdfContext);// 8
    CGPDFContextEndPage (pdfContext);// 9
    CGContextRelease (pdfContext);// 10
    CFRelease(pageDictionary); // 11
    CFRelease(boxData);
}

这是代码的作用:

  • 1) 将参数设置为指定PDF页面大小的矩形和指定文件名的字符串。
  • 2) 从传递给函数MyCreatePDFFile的文件名创建CFString对象。
  • 3) 从CFString对象创建CFURL对象。
  • 4) 创建一个空的CFDictionary对象来保存元数据。接下来的两行添加标题和创建者。您可以使用函数CFDictionarySetValue添加任意数量的键值对。有关创建字典的更多信息,请参阅CFDictionary Reference
  • 5) 创建PDF图形上下文,传递三个参数:
    • CFURL对象,用于指定PDF数据的位置。
    • 指向矩形的指针,用于定义PDF页面的默认大小和位置。矩形的原点通常为(0,0)。 Quartz使用此矩形作为页面媒体框的默认边界。如果传递NULL,Quartz使用的默认页面大小为8.5 x 11英寸(612 x 792点)
    • 包含PDF元数据的CFDictionary对象。如果您没有要添加的元数据,请传递NULL。您可以使用CFDictionary对象指定输出方法选项 - 意图子类型,条件,条件标识符,注册表名称,目标输出配置文件以及包含有关预期目标设备或生产条件的其他信息或注释的人类可读文本字符串。有关输出方法选项的更多信息,请参阅CGPDFContext Reference
  • 6) 创建CFDictionary对象以保存PDF页面的页面框。此示例设置media box
  • 7) 表示页面的开头。当您使用支持多个页面(例如PDF)的图形上下文时,可以使用CGPDFContextBeginPageCGPDFContextEndPage调用函数来描述输出中的页面边界。每个页面必须通过调用CGPDFContextBeginPageCGPDFContextEndPage进行括号。 Quartz忽略在基于页面的上下文中在页边界外执行的所有绘制操作。
  • 8) 调用应用程序定义的函数以将内容绘制到PDF上下文。您可以在此处提供绘图程序。
  • 9) 在基于页面的图形上下文中指示页面的结尾。
  • 10) 释放PDF上下文。
  • 11) 释放页面词典。

Adding Links - 添加链接

您可以为您创建的PDF上下文添加链接和锚点。 Quartz提供了三个函数,每个函数都将PDF图形上下文以及有关链接的信息作为参数:

  • CGPDFContextSetURLForRect允许您指定在用户单击当前PDF页面中的矩形时打开的URL。
  • CGPDFContextSetDestinationForRect允许您设置当用户单击当前PDF页面中的矩形时跳转到的目标。 您必须提供目的地名称。
  • CGPDFContextAddDestinationAtPoint允许您设置当用户单击当前PDF页面中的点时跳转到的目标。 您必须提供目的地名称。

Protecting PDF Content - 保护PDF内容

为了保护PDF内容,您可以在辅助字典中指定许多安全选项,并将其传递给函数CGPDFContextCreate。您可以通过在辅助字典中包含以下键来设置所有者密码,用户密码以及是否可以打印或复制PDF:

  • kCGPDFContextOwnerPassword,用于定义PDF文档的所有者密码。如果指定了此key,则使用值作为所有者密码对文档进行加密;否则,文档未加密。此键的值必须是可以ASCII编码表示的CFString对象。只有前32个字节用于密码。此key没有默认值。如果此键的值无法用ASCII表示,则不会创建文档,并且创建函数将返回NULLQuartz使用40位加密。
  • kCGPDFContextUserPassword,用于定义PDF文档的用户密码。如果文档已加密,则此键的值是文档的用户密码。如果未指定,则用户密码为空字符串。此键的值必须是可以ASCII编码表示的CFString对象;只有前32个字节用于密码。如果此键的值无法用ASCII表示,则不会创建文档,并且创建函数将返回NULL。
  • kCGPDFContextAllowsPrinting指定在使用用户密码解锁文档时是否可以打印文档。此键的值必须是CFBoolean对象。此键的默认值为kCFBooleanTrue
  • kCGPDFContextAllowsCopying指定在使用用户密码解锁文档时是否可以复制文档。此键的值必须是CFBoolean对象。此键的默认值为kCFBooleanTrue

Listing 14-4(在下一章中)显示了检查PDF文档是否被锁定的代码,如果是,则尝试使用密码打开文档。

后记

本篇主要讲述了PDF文档创建,查看和转换,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容