iOS开发之Quartz2D生成PDF-Part2
在上一节当中,我们创建了一个基于Quartz2D的PDF,并在PDF中添加一线条。
在这一节,主要是添加一个logo,和绘制一个table。
添加logo
下载图片资源,然后添加到工程当中。
在`PDFRenderer.m`文件中添加下面方法:
//绘制图像
+ (void)drawImage:(UIImage*)image inRect:(CGRect)rect {
[image drawInRect:rect];
}
在
PDFRenderer.h
中添加下面方法:
+ (void)drawImage:(UIImage*)image inRect:(CGRect)rect;
为了能在PDF上显示此logo,在
PDFRenderer.m
的
drawPDF
方法中添加下面代码,此代码写在
UIGraphicsEndPDFContext();
之前:
UIImage *logo = [UIImage imageNamed:@"ray-logo"];
CGRect frame = CGRectMake(, , , );
[PDFRenderer drawImage:logo inRect:frame];
在上面的代码中,创建一个UIImage对象,并定义图像的位置和大小,调用
drawImage
方法将两个参数传过去进行绘制。
完整代码如下:
+ (void)drawPDF:(NSString*)fileName {
UIGraphicsBeginPDFContextToFile(fileName, CGRectZero, nil);
UIGraphicsBeginPDFPageWithInfo(CGRectMake(, , , ), nil);
CGPoint from = CGPointMake(, );
CGPoint to = CGPointMake(, );
[PDFRenderer drawLineFromPoint:from toPoint:to];
UIImage *logo = [UIImage imageNamed:@"ray-logo"];
CGRect frame = CGRectMake(, , , );
[PDFRenderer drawImage:logo inRect:frame];
[self drawText];
UIGraphicsEndPDFContext();
}
至此了解到如何绘制清单的基本元素:文本、线条、图片。接下来将运用这些所有元素构建更为完美的布局。
绘制Labels
创建一个xib,并命名为
InvoiceView
,选中
InvoiceView
IB,设置View的width: 612 和height: 792,这些都是A4PDF的默认尺寸。下面拖拽8个UILabel,并按如下命名:
- Recipient [Name]
- Recipient’s Address
- Recipient’s City
- Recipient’s Postal Code
- Invoicer [Name]
- Invoicer’s Address
- Invoicer’s City
- Invoicer’s Postal Code
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiNxYDMykzMzEDOxgDM1EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
这些labels的位置将会在PDF上进行布局。给每个label从0-7设置tag。例如:
Recipient
的tag是0,
Recipient’s Address
的tag事1,以此类推。
打开
PDFRenderer.m
文件,并重构
drawText
方法。代码清单如下:
+(void)drawText:(NSString*)textToDraw inFrame:(CGRect)frameRect {
CFStringRef stringRef = (__bridge CFStringRef)textToDraw;
CFAttributedStringRef currentText = CFAttributedStringCreate(NULL, stringRef, NULL);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);
CGMutablePathRef framePath = CGPathCreateMutable();
CGPathAddRect(framePath, NULL, frameRect);
CFRange currentRange = CFRangeMake(, );
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
CGPathRelease(framePath);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
CGContextTranslateCTM(currentContext, ,);
CGContextScaleCTM(currentContext, , -);
CTFrameDraw(frameRef, currentContext);
CFRelease(frameRef);
CFRelease(stringRef);
CFRelease(framesetter);
}
在
PDFRenderer.h
文件中添加下面代码:
+(void)drawText:(NSString*)textToDraw inFrame:(CGRect)frameRect
下面是从
InvoiceView
中加载label,使用文本和位置大小来绘制到PDF上。在
PDFRenderer.m
中的
drawPDF
的商法添加一个新方法
drawLabels
:
+ (void)drawLabels {
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:@"InvoiceView" owner:nil options:nil];
UIView *mainView = [objects lastObject];
for (UIView *view in [mainView subviews]) {
if ([view isKindOfClass:[UILabel class]]) {
UILabel *label = (UILabel*)view;
[self drawText:label.text inFrame:label.frame];
}
}
}
这个方法是加载
InvoiceView
,遍历
InvoiceView
中的labels,调用
drawText
,将text和frame变量传递过去。
需改
drawPDF
方法:
+ (void)drawPDF:(NSString*)fileName {
UIGraphicsBeginPDFContextToFile(fileName, CGRectZero, nil);
UIGraphicsBeginPDFPageWithInfo(CGRectMake(, , , ), nil);
[self drawText:@"Hello world" inFrame:CGRectMake(, , , )];
[self drawLabels];
// [self drawText];
UIGraphicsEndPDFContext();
}
运行下模拟器:
啊哈,能运行出来,但结果不是令人满意,文字啥的都是反的。接下来就坐下处理,修改
drawText
的代码:
CGContextTranslateCTM(currentContext, , frameRect.origin.y*);
CGContextScaleCTM(currentContext, , -);
CTFrameDraw(frameRef, currentContext);
CGContextScaleCTM(currentContext, , -);
CGContextTranslateCTM(currentContext, , (-) * frameRect.origin.y * 2);
再次运行下模拟器,看下结果:
结果比较令人满意。
添加logo
打开
InvoiceView.xib
添加一个
UIImageView
然后在
PDFRenderer.m
中添加
drawLogo
方法:
+ (void)drawLogo {
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:@"InvoiceView" owner:nil options:nil];
UIView *mainView = [objects lastObject];
for (UIView *view in [mainView subviews]) {
if ([view isKindOfClass:[UIImageView class]]) {
UIImage *logo = [UIImage imageNamed:@"ray-logo"];
[self drawImage:logo inRect:view.frame];
}
}
}
处理逻辑和
drawLabels
方法类似。
最后在
drawPDF
方法中的
[self drawLabels]
语句后调用
[self drawLogo]
。来看下运行效果:
绘制一个表格
绘制表格不能像使用
InvoiceView
那样,需要一系列的变量来替代,例如:table的width和height,row的height,column的width。
下面在
PDFRenderer.m
的
drawPDF
上方添加如下代码:
+ (void)drawTableAt:(CGPoint)origin
withRowHeight:(int)rowHeight
andColumnWidth:(int)columnWidth
andRowCount:(int)numberOfRows
andColumnCount:(int)numberOfColumns
{
for (int i = ; i <= numberOfRows; i++) {
int newOrigin = origin.y + (rowHeight * i);
CGPoint from = CGPointMake(origin.x, newOrigin);
CGPoint to = CGPointMake(origin.x + (numberOfColumns*columnWidth), newOrigin);
[self drawLineFromPoint:from toPoint:to];
}
}
上面方法是绘制水平线,循环遍历每一行,计算每行的起始和结束位置。最后调用
drawLine:from:to
方法绘制水平线:
+ (void)drawPDF:(NSString*)fileName {
UIGraphicsBeginPDFContextToFile(fileName, CGRectZero, nil);
UIGraphicsBeginPDFPageWithInfo(CGRectMake(, , , ), nil);
[self drawText:@"Hello world" inFrame:CGRectMake(, , , )];
[self drawLabels];
[self drawLogo];
int xOrigin = ;
int yOrigin = ;
int rowHeight = ;
int columnWidth = ;
int numberOfRows = ;
int numberOfColumns = ;
[self drawTableAt:CGPointMake(xOrigin, yOrigin)
withRowHeight:rowHeight
andColumnWidth:columnWidth
andRowCount:numberOfRows
andColumnCount:numberOfColumns];
UIGraphicsEndPDFContext();
}
运行模拟器,看下效果:
接下来是绘制垂直线条,在
drawTable
方法中的第一个循环的下方再添加一个循环:
+ (void)drawTableAt:(CGPoint)origin
withRowHeight:(int)rowHeight
andColumnWidth:(int)columnWidth
andRowCount:(int)numberOfRows
andColumnCount:(int)numberOfColumns
{
//绘制水平线
for (int i = ; i <= numberOfRows; i++) {
int newOrigin = origin.y + (rowHeight * i);
CGPoint from = CGPointMake(origin.x, newOrigin);
CGPoint to = CGPointMake(origin.x + (numberOfColumns*columnWidth), newOrigin);
[self drawLineFromPoint:from toPoint:to];
}
//绘制垂直线
for (int i = ; i <= numberOfColumns; i++) {
int newOrigin = origin.x + (columnWidth * i);
CGPoint from = CGPointMake(newOrigin, origin.y);
CGPoint to = CGPointMake(newOrigin, origin.y + (numberOfRows * rowHeight));
[self drawLineFromPoint:from toPoint:to];
}
}
再次运行下模拟器,看下效果:
看着似乎已完成,但还缺少一些数据填充到表格当中,那么接下来完成此过程,让此PDF近乎完美。
填充表格
模拟数据填充表格,在
PDFRenderer.m
中的
drawPDF
方法的上方添加
drawTableDataAt
方法:
+ (void)drawTableDataAt:(CGPoint)origin
withRowHeight:(int)rowHeight
andColumnWidth:(int)columnWidth
andRowCount:(int)numberOfRows
andColumnCount:(int)numberOfColumns
{
NSArray *header = @[@"Quantity", @"Description", @"Unit price", @"Total"];
NSArray *invoiceInfo1 = @[@"1", @"Development", @"$1000", @"1000"];
NSArray *invoiceInfo2 = @[@"1", @"Development", @"$1000", @"1000"];
NSArray *invoiceInfo3 = @[@"1", @"Development", @"$1000", @"1000"];
NSArray *invoiceInfo4 = @[@"1", @"Development", @"$1000", @"1000"];
NSArray *allInfo = @[header, invoiceInfo1, invoiceInfo2, invoiceInfo3, invoiceInfo4];
for (int i = ; i < allInfo.count; i++) {
NSArray *infoToDraw = allInfo[i];
for (int j = ; j< numberOfColumns; j++) {
int newOriginX = origin.x + (columnWidth * j);
int newOriginY = origin.y + (rowHeight * (i+));
CGRect frame = CGRectMake(newOriginX, newOriginY, columnWidth, rowHeight);
[self drawText:infoToDraw[j] inFrame:frame];
}
}
}
第一个数组是表头数据,其他数组是表中行和列的值。
在
drawPDF
中调用
drawTableDataAt
(在
UIGraphicsEndPDFContext
之前调用):
[self drawTableDataAt:CGPointMake(xOrigin, yOrigin)
withRowHeight:rowHeight
andColumnWidth:columnWidth
andRowCount:numberOfRows
andColumnCount:numberOfColumns];
运行模拟器,将会看到表中填充的数据:
感觉还差点什么,再做最后一次调整:添加间距
padding
+ (void)drawTableDataAt:(CGPoint)origin
withRowHeight:(int)rowHeight
andColumnWidth:(int)columnWidth
andRowCount:(int)numberOfRows
andColumnCount:(int)numberOfColumns
{
int padding = ;
NSArray *header = @[@"Quantity", @"Description", @"Unit price", @"Total"];
NSArray *invoiceInfo1 = @[@"1", @"Development", @"$1000", @"1000"];
NSArray *invoiceInfo2 = @[@"1", @"Development", @"$1000", @"1000"];
NSArray *invoiceInfo3 = @[@"1", @"Development", @"$1000", @"1000"];
NSArray *invoiceInfo4 = @[@"1", @"Development", @"$1000", @"1000"];
NSArray *allInfo = @[header, invoiceInfo1, invoiceInfo2, invoiceInfo3, invoiceInfo4];
for (int i = ; i < allInfo.count; i++) {
NSArray *infoToDraw = allInfo[i];
for (int j = ; j< numberOfColumns; j++) {
int newOriginX = origin.x + (columnWidth * j);
int newOriginY = origin.y + (rowHeight * (i+));
CGRect frame = CGRectMake(newOriginX+padding, newOriginY+padding, columnWidth, rowHeight);
[self drawText:infoToDraw[j] inFrame:frame];
}
}
}
最终结果:
大功告成,此PDF主要展示了图片、表格和数据。