第一次修改:2017.05.26html
修改內容:以前的方法在轉化帶有圖片的html頁面時會出現圖片缺失問題,因爲以前我須要轉換的html不包含圖片,因此沒有發現這一問題,如今增長一個直接將webView轉化爲PDF的方法,在下面進行詳述。git
前言:最近項目有個新的需求:用戶能夠將當前HTML頁面內容保存到本地,並可將此內容進行分享。爲此我想了兩種辦法:
github
一、將HTML頁面截屏拼接爲長圖進行保存;web
二、將HTML頁面轉換爲PDF文件進行保存;框架
相比之下,顯然第一種在操做性和實用性上都不太合適,所以我選擇第二種方法進行實現。ide
Github:看這裏oop
簡單整理下思路,咱們能夠分如下幾步進行處理:性能
一、獲取HTML文件ui
二、對HTML富文本進行處理atom
三、將處理後的富文本進行PDF文件保存
四、對PDF文件進行分享
一、獲取HTML文件
//獲取本地HTML內容並進行加載展現
NSString *readmePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"];
_htmlStr = [NSString stringWithContentsOfFile:readmePath encoding:NSUTF8StringEncoding error:nil];
[webView loadHTMLString:_htmlStr baseURL:nil];
二、對HTML富文本進行處理
這裏咱們經過集成DTCoreText對富文本進行處理:
先簡單介紹一下CoreText,CoreText是iOS/OSX裏的文字渲染引擎,在iOS/OSX中看到的全部文字在底層都是由CoreText去渲染。
DTCoreText是個開源的iOS富文本組件,它能夠解析HTML與CSS最終用CoreText繪製出來,一般用於在一些須要顯示富文本的場景下代替低性能的UIWebView。具體可參見這篇文章:iOS富文本組件的實現—DTCoreText源碼解析。
然而不得不說一下DTCoreText這個開源框架的集成仍是很坑爹的,通常的第三方框架直接down下來拖到項目中就能用,可是這個框架down下來以後缺乏文件,從cocoapods集成運行時又會出現crash。我試着集成了幾回都以失敗了結,最後上網查找了一下集成方法,使用Framwork的方式導入;有興趣的也能夠看看:官方提供的導入方法。
集成DTCoreText:
一、將兩個依賴庫導入項目中
二、按圖示步驟進行操做:選中target的BuildPhases-->選中左側加號NewCopy Files Phases-->將兩個依賴庫添加進去,選擇Destination爲Frameworks
新建OCPDFGenerator類對HTML文本進行處理:
#import@interface OCPDFGenerator : NSObject
+(NSString *)generatePDFFromAttributedString:(NSAttributedString *)str;
+(NSString *)generatePDFFromHTMLString:(NSString *)str;
@end
將HTML文本內容轉換爲富文本:
+(NSString *)generatePDFFromHTMLString:(NSString *)html {
NSData *data = [html dataUsingEncoding:NSUTF8StringEncoding];
// Create attributed string from HTML
CGSize maxImageSize = CGSizeMake(500, 500);
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithFloat:1.0], NSTextSizeMultiplierDocumentOption, [NSValue valueWithCGSize:maxImageSize], DTMaxImageSize,
@"Arial", DTDefaultFontFamily, @"blue", DTDefaultLinkColor, nil];
NSAttributedString *str = [[NSAttributedString alloc]initWithHTMLData:data options:options documentAttributes:NULL];
return [self generatePDFFromAttributedString:str];
}
三、將處理後的富文本進行PDF文件保存
建立目標爲指定可變數據對象的基於PDF的圖形上下文,並進行存儲:
+(NSString *)generatePDFFromAttributedString:(NSAttributedString *)str {
NSString *fileName = @"testFile.pdf";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *newFilePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:fileName];
int fontSize = 12;
NSString *font = @"Verdana";
UIColor *color = [UIColor blackColor];
int DOC_WIDTH = 612;
int DOC_HEIGHT = 792;
int LEFT_MARGIN = 50;
int RIGHT_MARGIN = 50;
int TOP_MARGIN = 50;
int BOTTOM_MARGIN = 50;
int CURRENT_TOP_MARGIN = TOP_MARGIN;
//You can make the first page have a different top margin to place headers, etc.
int FIRST_PAGE_TOP_MARGIN = TOP_MARGIN;
CGRect a4Page = CGRectMake(0, 0, DOC_WIDTH, DOC_HEIGHT);
NSDictionary *fileMetaData = [[NSDictionary alloc] init];
if (!UIGraphicsBeginPDFContextToFile(newFilePath, a4Page, fileMetaData )) {
NSLog(@"error creating PDF context");
return nil;
}
BOOL done = NO;
CGContextRef context = UIGraphicsGetCurrentContext();
CFRange currentRange = CFRangeMake(0, 0);
CGContextSetTextDrawingMode (context, kCGTextFill);
CGContextSelectFont (context, [font cStringUsingEncoding:NSUTF8StringEncoding], fontSize, kCGEncodingMacRoman);
CGContextSetFillColorWithColor(context, [color CGColor]);
// Initialize an attributed string.
CFAttributedStringRef attrString = (__bridge CFAttributedStringRef)str;
// Create the framesetter with the attributed string.
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
int pageCount = 1;
do {
UIGraphicsBeginPDFPage();
CGMutablePathRef path = CGPathCreateMutable();
if(pageCount == 1) {
CURRENT_TOP_MARGIN = FIRST_PAGE_TOP_MARGIN;
} else {
CURRENT_TOP_MARGIN = TOP_MARGIN;
}
CGRect bounds = CGRectMake(LEFT_MARGIN,
CURRENT_TOP_MARGIN,
DOC_WIDTH - RIGHT_MARGIN - LEFT_MARGIN,
DOC_HEIGHT - CURRENT_TOP_MARGIN - BOTTOM_MARGIN);
CGPathAddRect(path, NULL, bounds);
// Create the frame and draw it into the graphics context
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, currentRange, path, NULL);
if(frame) {
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0, bounds.origin.y);
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -(bounds.origin.y + bounds.size.height));
CTFrameDraw(frame, context);
CGContextRestoreGState(context);
// Update the current range based on what was drawn.
currentRange = CTFrameGetVisibleStringRange(frame);
currentRange.location += currentRange.length;
currentRange.length = 0;
}
// If we're at the end of the text, exit the loop.
if (currentRange.location == CFAttributedStringGetLength((CFAttributedStringRef)attrString))
done = YES;
pageCount++;
} while(!done);
UIGraphicsEndPDFContext();
return newFilePath;
}
四、對PDF文件進行分享
UIDocumentInteractionController是從iOS 3.2的SDK開始支持的,它是直接繼承的NSObject,UIDocumentInteractionController主要給咱們提供了三種用途:
展現一個能夠操做咱們分享的文檔類型的第三方App列表,包括官方處理應用,第三方應用,AirDrop等。
在第一條展現列表的基礎上添加額外的操做,好比複製,打印,預覽,保存等。
結合Quick Look框架直接展現文檔內容。
代碼實現:
- (void)sharePdf{
NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/testFile.pdf"];
UIDocumentInteractionController *ctrl = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
ctrl.delegate = self;
ctrl.UTI = @"com.adobe.pdf";
[ctrl presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES];
}
注:這裏運行以後點擊iBooks進行分享,會發現程序crash,查看crash日誌發現:猜想是UIDocumentInteractionController提早釋放內存致使,因而咱們將UIDocumentInteractionController對象聲明爲strong類型的實例對象,防止其內存提早釋放便可。
@property (nonatomic, strong) UIDocumentInteractionController *documentInterCtrl;
2017.05.26修改內容:
上面的方法因爲是直接對富文本進行操做,因此忽略了處理圖片這一問題。上網研究了一下,發現能夠用UIPrintPageRenderer進行頁面繪製並保存,這一方法更簡單,代碼也更簡潔。
具體實現:
1. 建立一個webView的類別用於轉換文件,並添加內容轉換的接口@interface UIWebView (ConverToData)
@interface UIWebView (ConverToData)
- (NSData *)converToPDF;
2. 使用UIPrintPageRenderer對當前webView進行繪製
- (NSData *)converToPDF{
UIViewPrintFormatter *fmt = [self viewPrintFormatter];
UIPrintPageRenderer *render = [[UIPrintPageRenderer alloc] init];
[render addPrintFormatter:fmt startingAtPageAtIndex:0];
CGRect page;
page.origin.x=0;
page.origin.y=0;
page.size.width=600;
page.size.height=768;
CGRect printable=CGRectInset( page, 50, 50 );
[render setValue:[NSValue valueWithCGRect:page] forKey:@"paperRect"];
[render setValue:[NSValue valueWithCGRect:printable] forKey:@"printableRect"];
NSMutableData * pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData( pdfData, CGRectZero, nil );
for (NSInteger i=0; i < [render numberOfPages]; i++)
{
UIGraphicsBeginPDFPage();
CGRect bounds = UIGraphicsGetPDFContextBounds();
[render drawPageAtIndex:i inRect:bounds];
}
UIGraphicsEndPDFContext();
return pdfData;
}
Github的代碼也作了同步更新。