iOS繪圖和打印編程指導(dǎo)(五)-生成PDF內(nèi)容

UIKit提供大量函數(shù)支持使用原生代碼生成PDF內(nèi)容. 這些函數(shù)允許你創(chuàng)建一個(gè)以PDF文件或PDF數(shù)據(jù)為目標(biāo)的繪圖上下文. 然后你可以創(chuàng)建一個(gè)或多個(gè)PDF頁(yè)面, 使用的UIKit和Core Graphic技術(shù), 和繪制到屏幕一樣的步驟將PDF繪制到這些頁(yè)面. 當(dāng)你完成后, 剩下的就是PDF文件.

整個(gè)繪制PDF過(guò)程看起來(lái)很像繪制image(本系列文章四已講), 包括一些步驟:

  1. 創(chuàng)建PDF上下文, 然后將它push到Graphic堆棧
  2. 創(chuàng)建頁(yè)面(page)
  3. 使用UIKit或者Core Graphic常規(guī)方法繪制內(nèi)容到頁(yè)面
  4. 如果需要, 可以添加鏈接
  5. 重復(fù)2,3,4這三個(gè)步驟, 如果需要的話
  6. 最后將PDF context從堆棧中pop出來(lái), 然后將結(jié)果寫(xiě)入到具體的PDF文件或者保存到制定的NSMutableData對(duì)象, 這取決于context是如何創(chuàng)建的.

下面的章節(jié)就是圍繞上面的步驟具體展開(kāi)的, 包括使用代碼展示如何實(shí)現(xiàn)上的步驟.

創(chuàng)建和配置PDF上下文


創(chuàng)建PDF上下文的函數(shù)有兩個(gè), 分別是UIGraphicsBeginPDFContextToDataUIGraphicsBeginPDFContextToFile. 這兩個(gè)函數(shù)將不同PDF數(shù)據(jù)輸出地和圖形上下文綁定, UIGraphicsBeginPDFContextToData函數(shù)式將數(shù)據(jù)輸出到NSMutableData對(duì)象, 而UIGraphicsBeginPDFContextToFile函數(shù)是將內(nèi)容輸出到PDF文件(APP的home路徑下)

PDF文件是以頁(yè)結(jié)構(gòu)來(lái)管理內(nèi)容的. 所以在繪制時(shí)有兩個(gè)限制如下:

  • 在將內(nèi)容繪制到page之前, 你需要open該page
  • 你必須設(shè)置page的大小.

在創(chuàng)建PDF繪圖上下文時(shí), 那兩個(gè)函數(shù)會(huì)默認(rèn)指定page的大小, 但不會(huì)自動(dòng)打開(kāi)page. 創(chuàng)建上下文后, 必須使用UIGraphicsBeginPDFPageUIGraphicsBeginPDFPageWithInfo函數(shù)顯式打開(kāi)新page. 每次創(chuàng)建新page時(shí), 必須再次調(diào)用這些函數(shù)中的一個(gè)來(lái)標(biāo)記新page的開(kāi)始. UIGraphicsBeginPDFPage函數(shù)使用默認(rèn)大小創(chuàng)建一個(gè)page, UIGraphicsBeginPDFPageWithInfo函數(shù)允許你自定義page大小和其他屬性.

完成繪制后, 通過(guò)調(diào)用UIGraphicsBeginPDFPage函數(shù)來(lái)關(guān)閉PDF上下文. 同時(shí)還會(huì)關(guān)閉最后一頁(yè), 并將PDF內(nèi)容寫(xiě)入到創(chuàng)建上下文時(shí)指定的文件或者數(shù)據(jù)對(duì)象中. 此函數(shù)還從上下文堆棧中移除PDF上下文.

代碼5-1展示了應(yīng)用程序在textView中獲取文本然后循環(huán)寫(xiě)入PDF文件中. 除了配置和管理PDF上下文的函數(shù)調(diào)用之外, 大多數(shù)代碼都與繪制所需的內(nèi)容有關(guān). textView是一個(gè)成員變量保存包含所文本的text view對(duì)象. 該應(yīng)用程序使用Core Text框架(更具體的說(shuō)是CTFramesetterRef數(shù)據(jù)類型)來(lái)處理連續(xù)頁(yè)面上的文本布局和頁(yè)面管理. 自定義方法renderPageWithTextRange:andFramesetter:drawPageNumber:方的實(shí)現(xiàn)如代碼5-2所示

代碼清單4-1 創(chuàng)建一個(gè)新的PDF文件

- (IBAction)savePDFFile:(id)sender {
    // Prepare the text using a Core Text Framesetter.
    CFAttributedStringRef currentText = CFAttributedStringCreate(NULL, (CFStringRef)textView.text, NULL);
    if (currentText) {
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);
        if (framesetter) {
 
            NSString *pdfFileName = [self getPDFFileName];
            // Create the PDF context using the default page size of 612 x 792.
            UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);
 
            CFRange currentRange = CFRangeMake(0, 0);
            NSInteger currentPage = 0;
            BOOL done = NO;
 
            do {
                // Mark the beginning of a new page.
                UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);
 
                // Draw a page number at the bottom of each page.
                currentPage++;
                [self drawPageNumber:currentPage];
 
                // Render the current page and update the current range to
                // point to the beginning of the next page.
                currentRange = [self renderPageWithTextRange:currentRange andFramesetter:framesetter];
 
                // If we're at the end of the text, exit the loop.
                if (currentRange.location == CFAttributedStringGetLength((CFAttributedStringRef)currentText))
                    done = YES;
            } while (!done);
 
            // Close the PDF context and write the contents out.
            UIGraphicsEndPDFContext();
 
            // Release the framewetter.
            CFRelease(framesetter);
 
        } else {
            NSLog(@"Could not create the framesetter needed to lay out the atrributed string.");
        }
        // Release the attributed string.
        CFRelease(currentText);
    } else {
            NSLog(@"Could not create the attributed string for the framesetter");
    }
}

繪制PDF頁(yè)面


所有的PDF內(nèi)容繪制需要在page中完成. 每個(gè)PDF文檔至少有一個(gè)page, 但是多數(shù)是多個(gè)page. 你通過(guò)調(diào)用UIGraphicsBeginPDFPageUIGraphicsBeginPDFPageWithInfo函數(shù)來(lái)指定新的page. 這些函數(shù)關(guān)閉前一個(gè)page(如果它打開(kāi)的話), 然后創(chuàng)建一個(gè)新的page, 并為繪圖做好準(zhǔn)備.

創(chuàng)建page之后, PDF繪圖上下文將捕獲所有繪圖命令, 并將其轉(zhuǎn)換為PDF命令. 你可以在頁(yè)面中繪制任何你想要的東西, 包括文本, 矢量形狀和圖像, 就像在APP中的自定義view中一樣. 你發(fā)出的繪圖命令會(huì)被PDF上下文捕獲并翻譯成PDF數(shù)據(jù). 在頁(yè)面上放置內(nèi)容完全取決于你, 但必須在page中size范圍內(nèi)進(jìn)行.

代碼5-2展示了在PDF頁(yè)中繪制內(nèi)容的兩種自定義方法. renderPageWithTextRange:andFramesetter:方法使用Core Text創(chuàng)建一個(gè)適合頁(yè)面的文本框架, 然后在該框架中布局一些文本. 在布局文本之后, 它返回一個(gè)更新的range, 該range反映了當(dāng)前頁(yè)面的結(jié)束和下一頁(yè)的開(kāi)始. drawPageNumber:方法使用NSString的繪制功能在PDF的每頁(yè)底部繪制一個(gè)頁(yè)碼字符串.

注意:此代碼片段使用了Core Text框架, 你需要手動(dòng)添加該框架到你的項(xiàng)目中

代碼清單5-2 繪制PDF頁(yè)面內(nèi)容

// Use Core Text to draw the text in a frame on the page.
- (CFRange)renderPage:(NSInteger)pageNum withTextRange:(CFRange)currentRange
                 andFramesetter:(CTFramesetterRef)framesetter {
   // Get the graphics context.
   CGContextRef    currentContext = UIGraphicsGetCurrentContext();
 
   // Put the text matrix into a known state. This ensures
   // that no old scaling factors are left in place.
   CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
 
   // Create a path object to enclose the text. Use 72 point
   // margins all around the text.
   CGRect    frameRect = CGRectMake(72, 72, 468, 648);
   CGMutablePathRef framePath = CGPathCreateMutable();
   CGPathAddRect(framePath, NULL, frameRect);
 
   // Get the frame that will do the rendering.
   // The currentRange variable specifies only the starting point. The framesetter
   // lays out as much text as will fit into the frame.
   CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
   CGPathRelease(framePath);
 
   // Core Text draws from the bottom-left corner up, so flip
   // the current transform prior to drawing.
   CGContextTranslateCTM(currentContext, 0, 792);
   CGContextScaleCTM(currentContext, 1.0, -1.0);
 
   // Draw the frame.
   CTFrameDraw(frameRef, currentContext);
 
   // Update the current range based on what was drawn.
   currentRange = CTFrameGetVisibleStringRange(frameRef);
   currentRange.location += currentRange.length;
   currentRange.length = 0;
   CFRelease(frameRef);
 
   return currentRange;
}
 
- (void)drawPageNumber:(NSInteger)pageNum {
   NSString *pageString = [NSString stringWithFormat:@"Page %d", pageNum];
   UIFont *theFont = [UIFont systemFontOfSize:12];
   CGSize maxSize = CGSizeMake(612, 72);
 
   CGSize pageStringSize = [pageString sizeWithFont:theFont
                                       constrainedToSize:maxSize
                                       lineBreakMode:UILineBreakModeClip];
   CGRect stringRect = CGRectMake(((612.0 - pageStringSize.width) / 2.0),
                                  720.0 + ((72.0 - pageStringSize.height) / 2.0),
                                  pageStringSize.width,
                                  pageStringSize.height);
 
   [pageString drawInRect:stringRect withFont:theFont];
}

創(chuàng)建PDF內(nèi)容鏈接


除了繪制PDF內(nèi)容外, 可以在內(nèi)容中加入鏈接(將用戶帶到同一PDF文檔中其他位置或者外部URL鏈接). 創(chuàng)建單個(gè)鏈接, 必須向PDF文檔頁(yè)面添加鏈接的源矩形區(qū)域和鏈接的目的地. 鏈接目的地的屬性之一是作為該鏈接的唯一表示符(字符串). 要?jiǎng)?chuàng)建到特定目的地的鏈接, 需要在創(chuàng)建源矩形區(qū)域時(shí), 指定鏈接目的地的標(biāo)識(shí)符.

若要向PDF內(nèi)容添加新的鏈接目的地, 請(qǐng)使用UIGraphicsAddPDFContextDestinationAtPoint函數(shù). 這個(gè)函數(shù)將一個(gè)命名(唯一標(biāo)識(shí)符)好的目的地和當(dāng)前page中的具體點(diǎn)關(guān)聯(lián)起來(lái). 當(dāng)希望鏈接到該目的地時(shí), 可以使用UIGraphicsSetPDFContextDestinationForRect函數(shù)將鏈接目的地和鏈接原矩形區(qū)域關(guān)聯(lián)起來(lái). 圖5-1顯示了上面兩個(gè)函數(shù)的關(guān)系. 當(dāng)用戶點(diǎn)擊"see Chapter 1"文本時(shí), PDF會(huì)跳到目的地-"第一章", 該目的點(diǎn)位于第一章的頂部

圖5-1 創(chuàng)建一個(gè)鏈接目的地并跳轉(zhuǎn)到點(diǎn)

另外, 如果要在PDF中創(chuàng)建指向外部URL的鏈接時(shí), 可以使用UIGraphicsSetPDFContextURLForRect函數(shù)來(lái)設(shè)置.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子罐氨,更是在濱河造成了極大的恐慌,老刑警劉巖绪爸,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)译仗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)官觅,“玉大人,你說(shuō)我怎么就攤上這事阐污⌒莸樱” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵笛辟,是天一觀的道長(zhǎng)功氨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)手幢,這世上最難降的妖魔是什么捷凄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮围来,結(jié)果婚禮上跺涤,老公的妹妹穿的比我還像新娘。我一直安慰自己监透,他們只是感情好桶错,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著胀蛮,像睡著了一般院刁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上粪狼,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天退腥,我揣著相機(jī)與錄音,去河邊找鬼再榄。 笑死狡刘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的不跟。 我是一名探鬼主播颓帝,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了购城?” 一聲冷哼從身側(cè)響起吕座,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瘪板,沒(méi)想到半個(gè)月后吴趴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侮攀,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年锣枝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兰英。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撇叁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畦贸,到底是詐尸還是另有隱情陨闹,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布薄坏,位于F島的核電站趋厉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏胶坠。R本人自食惡果不足惜君账,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沈善。 院中可真熱鬧乡数,春花似錦、人聲如沸矮瘟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)澈侠。三九已至劫侧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哨啃,已是汗流浹背烧栋。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拳球,地道東北人审姓。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像祝峻,于是被迫代替她去往敵國(guó)和親魔吐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扎筒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容