天天看點

iOS開發之Quartz2D生成PDF-Part2

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
iOS開發之Quartz2D生成PDF-Part2

這些labels的位置将會在PDF上進行布局。給每個label從0-7設定tag。例如:

Recipient

的tag是0,

Recipient’s Address

的tag事1,以此類推。

iOS開發之Quartz2D生成PDF-Part2

打開

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();
}
           

運作下模拟器:

iOS開發之Quartz2D生成PDF-Part2

啊哈,能運作出來,但結果不是令人滿意,文字啥的都是反的。接下來就坐下處理,修改

drawText

的代碼:

CGContextTranslateCTM(currentContext, , frameRect.origin.y*);
    CGContextScaleCTM(currentContext, , -);

    CTFrameDraw(frameRef, currentContext);

    CGContextScaleCTM(currentContext, , -);
    CGContextTranslateCTM(currentContext, , (-) * frameRect.origin.y * 2);
           

再次運作下模拟器,看下結果:

iOS開發之Quartz2D生成PDF-Part2

結果比較令人滿意。

添加logo

打開

InvoiceView.xib

添加一個

UIImageView

iOS開發之Quartz2D生成PDF-Part2

然後在

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]

。來看下運作效果:

iOS開發之Quartz2D生成PDF-Part2

繪制一個表格

繪制表格不能像使用

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();
}
           

運作模拟器,看下效果:

iOS開發之Quartz2D生成PDF-Part2

接下來是繪制垂直線條,在

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];
    }
}
           

再次運作下模拟器,看下效果:

iOS開發之Quartz2D生成PDF-Part2

看着似乎已完成,但還缺少一些資料填充到表格當中,那麼接下來完成此過程,讓此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];
           

運作模拟器,将會看到表中填充的資料:

iOS開發之Quartz2D生成PDF-Part2

感覺還差點什麼,再做最後一次調整:添加間距

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];
        }
    }
}
           

最終結果:

iOS開發之Quartz2D生成PDF-Part2

大功告成,此PDF主要展示了圖檔、表格和資料。