CodeV

3.10-渲染PDF

图3-11显示了通过将PDF页面渲染到图像上下文而构建的图像。这个任务通过Core Graphics API完成有点复杂,它不提供“在矩形框中绘制页面”的选项。

图3-11

图3-11渲染PDF页面到上下文中需要进行一些调整。

该过程涉及几个阶段,如下面列出的详细说明。首先打开一个PDF文档,如清单3-11所示。CGPDFDocumentCreateWithURL()函数返回一个新的文档引用,您可以使用它来提取和绘制页面。

获取该文档后,请按照下列步骤操作:

  1. 通过调用CGPDFDocumentGetNumberOfPages()检查文档中的页数。
  2. 使用CGPDFDocumentGetPage()检索每个页面。页数以第1页开头,而不是你可能会期望的第0页。
  3. 确保在完成工作后使用CGPDFDocumentRelease()释放文档。

清单3-11打开PDF文档

1
2
3
4
5
6
7
8
9
10
11
12
// Open PDF document
NSString *pdfPath = [[NSBundle mainBundle] pathForResource:@"drawingwithquartz2d" ofType:@"pdf"];
CGPDFDocumentRef pdfRef = CGPDFDocumentCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:pdfPath]);
if (pdfRef == NULL)
{
NSLog(@"Error loading PDF");
return nil;
}

// ... use PDF document here

CGPDFDocumentRelease(pdfRef);

在抓取CGPDFPageRef页面时,清单3-12使您能够使用指定的矩形将每个页面绘制到图像上下文中。具有挑战性的是,PDF函数使用Quartz坐标系统绘制(原点在左下角),您要绘制的目的地是在UIKit坐标系统(原点在左上角)。

要处理这个,你必须在使用您的目的地和上下文时变一个小戏法。首先,垂直翻转整个上下文,以确保PDF页面从顶部向下绘制。接下来,转换目标矩形,以便页面绘制在正确的位置。

你可能想知道为什么你需要执行这个双重转换,答案是:在你翻转你的坐标系统以启用从上到下的Quartz绘图时,你的之前的目标矩形,例如,在右上角,现在将在右下方绘制。这是因为它仍然存在于UIKit世界。调整绘图上下文的变换后,矩形必须适应该变换,如下面清单3-12所示。如果您跳过此步骤,PDF输出将显示在右下角,而不是您希望的右上角。

我鼓励你通过注释掉矩形转换步骤和测试各种目的地位置来自己尝试一下。你会发现什么是坐标系统一致性的重要教训。翻转上下文不只是“修复”Quartz图像,它会影响所有位置定义。在第7章中,当您将文本绘制到UIKit路径时,会出现同样的问题。在这种情况下,你不会只使用矩形。您必须垂直镜像绘图目标中的整个路径。

执行坐标系调整时,将为页面计算适当的矩形。如第2章所讨论的,拟合矩形保留宽高比,同时将上下文定位在目的地中。这需要最后一次上下文调整,因此绘图从该拟合矩形的左上角开始。 最后,CGContextDrawPDFPage()函数将页面内容转换为活动上下文。

DrawPDFPageInRect()函数仅用于绘制到UIKit图像目标。将PDF页面绘制到PDF上下文时无法使用它。它取决于从上下文中检索UIImage以便执行其几何调整。要使此清单适合更一般的使用,您需要为垂直变换提供上下文参数(而不是从UIKit中检索一个)和上下文大小。

清单3-12将PDF页面绘制到目标矩形中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void DrawPDFPageInRect(CGPDFPageRef pageRef,CGRect destinationRect)
{
CGContextRef context = UIGraphicsGetCurrentContext();
if (context == NULL)
{
NSLog(@"Error: No context to draw to");
return;
}

CGContextSaveGState(context);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

// Flip the context to Quartz space
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformScale(transform, 1.0f, -1.0f);
transform = CGAffineTransformTranslate(transform, 0.0f, -image.size.height);
CGContextConcatCTM(context, transform);

// Flip the rect, which remains in UIKit space
CGRect d = CGRectApplyAffineTransform(destinationRect, transform);

// Calculate a rectangle to draw to
// CGPDFPageGetBoxRect() returns a rectangle
// representing the page’s dimension

CGRect pageRect = CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox);
CGFloat drawingAspect = AspectScaleFit(pageRect.size, d);
CGRect drawingRect = RectByFittingInRect(pageRect, d);

// Draw the page outline (optional)
UIRectFrame(drawingRect);

// Adjust the context to the page draws within
// the fitting rectangle (drawingRect)
CGContextTranslateCTM(context,drawingRect.origin.x, drawingRect.origin.y);
CGContextScaleCTM(context, drawingAspect, drawingAspect);

// Draw the page
CGContextDrawPDFPage(context, pageRef);
CGContextRestoreGState(context);
}

位图上下文几何

当使用位图上下文时,您可以直接从上下文引用中检索某些细节,如上下文的高度和宽度。 CGBitmapContextGetHeight()CGBitmapContextGetWidth()函数得到整数尺寸。但是,您不能检索上下文的缩放比例 - 上下文中的像素数量关联到输出图像的点数的方式。 (That’s because scale lives a bit too high up in abstraction)。正如你在本书中看到的,scale是由UIKit的UIGraphicsBeginImageContextWithOptions()设置的,通常不是一个直接与Quartz绘图相关联的特征。

为此,清单3-12不使用位图上下文功能检索大小,这很重要,因为变换是以点而不是像素操作的。如果你对你的上下文应用视网膜级翻转,你将在数学上失去两个因素。您的200点转换将使用CGBitmapContextGetHeight()返回的400像素值。

除了点对像素计算之外,上下文函数不是没有用处。它们可以检索当前上下文变换矩阵(CGContextGetCTM()),每行字节数(CGBitmapContextGetBytesPerRow())和alpha透明程度(CGBitmapContextGetAlphaInfo())以及其他选项。在Xcode文档管理器中搜索“ContextGet”以获取更多特定于上下文的函数。


本文翻译自《iOS Drawing Practical UIKit Solutions》作者:Erica Sadun,翻译:Cheng Dong。如果觉得本书不错请购买支持正版:亚马逊购买传送门,本书所有源代码可在GitHub上下载。译者虽然力求做到信,达,雅,但是由于时间仓促加之译者水平十分有限,文中难免会出现不正确,不准确,词不达意,难于理解的地方,还望各位批评指正,共同进步,谢谢。转载请注明出处。