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(20, 100, 300, 60);
[PDFRenderer drawImage:logo inRect:frame];
在上面的代碼中,創建一個UIImage對象,並定義圖像的位置和大小,調用drawImage
方法將兩個參數傳過去進行繪製。
完整代碼如下:
+ (void)drawPDF:(NSString*)fileName {
UIGraphicsBeginPDFContextToFile(fileName, CGRectZero, nil);
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);
CGPoint from = CGPointMake(0, 0);
CGPoint to = CGPointMake(200, 300);
[PDFRenderer drawLineFromPoint:from toPoint:to];
UIImage *logo = [UIImage imageNamed:@"ray-logo"];
CGRect frame = CGRectMake(20, 100, 300, 60);
[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
這些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(0, 0);
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
CGPathRelease(framePath);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
CGContextTranslateCTM(currentContext, 0,100);
CGContextScaleCTM(currentContext, 1.0, -1.0);
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(0, 0, 612, 792), nil);
[self drawText:@"Hello world" inFrame:CGRectMake(0, 0, 300, 50)];
[self drawLabels];
// [self drawText];
UIGraphicsEndPDFContext();
}
運行下模擬器:
啊哈,能運行出來,但結果不是令人滿意,文字啥的都是反的。接下來就坐下處理,修改drawText
的代碼:
CGContextTranslateCTM(currentContext, 0, frameRect.origin.y*2);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CTFrameDraw(frameRef, currentContext);
CGContextScaleCTM(currentContext, 1.0, -1.0);
CGContextTranslateCTM(currentContext, 0, (-1.0) * 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 = 0; 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(0, 0, 612, 792), nil);
[self drawText:@"Hello world" inFrame:CGRectMake(0, 0, 300, 50)];
[self drawLabels];
[self drawLogo];
int xOrigin = 50;
int yOrigin = 300;
int rowHeight = 50;
int columnWidth = 120;
int numberOfRows = 7;
int numberOfColumns = 4;
[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 = 0; 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 = 0; 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 = 0; i < allInfo.count; i++) {
NSArray *infoToDraw = allInfo[i];
for (int j = 0; j< numberOfColumns; j++) {
int newOriginX = origin.x + (columnWidth * j);
int newOriginY = origin.y + (rowHeight * (i+1));
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 = 10;
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 = 0; i < allInfo.count; i++) {
NSArray *infoToDraw = allInfo[i];
for (int j = 0; j< numberOfColumns; j++) {
int newOriginX = origin.x + (columnWidth * j);
int newOriginY = origin.y + (rowHeight * (i+1));
CGRect frame = CGRectMake(newOriginX+padding, newOriginY+padding, columnWidth, rowHeight);
[self drawText:infoToDraw[j] inFrame:frame];
}
}
}
最終結果:
大功告成,此PDF主要展示了圖片、表格和數據。