iOS將HTML頁面轉換成PDF文件保存到本地并分享傳輸文件

第一次修改:2017.05.26

修改內容:之前的方法在轉化帶有圖片的html頁面時會出現圖片缺失問題,由于之前我需要轉換的html不包含圖片,所以沒有發現這一問題,現在增加一個直接將webView轉化為PDF的方法,在下面進行詳述。



前言:最近項目有個新的需求:用戶可以將當前HTML頁面內容保存到本地,并可將此內容進行分享。為此我想了兩種辦法:

1、將HTML頁面截屏拼接為長圖進行保存;

2、將HTML頁面轉換為PDF文件進行保存;

相比之下,顯然第一種在操作性和實用性上都不太合適,因此我選擇第二種方法進行實現。


Github:看這里


簡單整理下思路,我們可以分以下幾步進行處理:

1、獲取HTML文件

2、對HTML富文本進行處理

3、將處理后的富文本進行PDF文件保存

4、對PDF文件進行分享


1、獲取HTML文件

//獲取本地HTML內容并進行加載展示

NSString *readmePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"];

_htmlStr = [NSString stringWithContentsOfFile:readmePath encoding:NSUTF8StringEncoding error:nil];

[webView loadHTMLString:_htmlStr baseURL:nil];

2、對HTML富文本進行處理

這里我們通過集成DTCoreText對富文本進行處理:

先簡單介紹一下CoreText,CoreText是iOS/OSX里的文字渲染引擎,在iOS/OSX中看到的所有文字在底層都是由CoreText去渲染。

DTCoreText是個開源的iOS富文本組件,它可以解析HTML與CSS最終用CoreText繪制出來,通常用于在一些需要顯示富文本的場景下代替低性能的UIWebView。具體可參見這篇文章:iOS富文本組件的實現—DTCoreText源碼解析

然而不得不說一下DTCoreText這個開源框架的集成還是很坑爹的,一般的第三方框架直接down下來拖到項目中就能用,但是這個框架down下來之后缺少文件,從cocoapods集成運行時又會出現crash。我試著集成了幾次都以失敗告終,最后上網查找了一下集成方法,使用Framwork的方式導入;有興趣的也可以看看:官方提供的導入方法

集成DTCoreText:

1、將兩個依賴庫導入項目中

2、按圖示步驟進行操作:選中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];

}

3、將處理后的富文本進行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;

}

4、對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的代碼也做了同步更新。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,807評論 1 92
  • 1、設置UILabel行間距 NSMutableAttributedString* attrString = [[...
    FF_911閱讀 1,424評論 0 3
  • 老人是智慧的,無論他們上過多少學,他們就是一本活教科書,總會顯現出來各種各樣的生活技巧,老人不是累贅,...
    安qian閱讀 370評論 0 0
  • 今天霜降 這場秋雨淅淅瀝瀝滴滴答答纏綿了三四天了,今天迎來了秋天最后一個節氣——霜降。寒意侵衣薄,白露凝為霜,七里...
    陽光坐懷閱讀 506評論 0 2
  • 從出發前的擔心,緊張又有點小激動\(≧≦)/,經過了一夜的顛簸,拖著我隨身將近30多公斤的行李到達了提前預定的青旅...
    鉄柵欄閱讀 173評論 0 0