IOS使用libharu创建PDF文档打开并分享

之前用过IOS导出图片到相册,今天又搞了下IOS导出pdf文档的方法,百度了一堆,好多网站都转载的是这个文章《如何使用HTML模版和iOS中的UIPrintPageRenderer来生成PDF文档》,但是这个文章的方案是通过在app里面通过html来是实现的,这个是一种方案,但是一个app仅仅为了这个功能就去嵌套一个网页未免有点蛋疼了,搜索了下,感觉更好的方案是通过libharu来创建pdf文档。

一、libharu介绍

libharu是一个c语言的跨平台,开源免费的pdf生成库,可以把文字,图片等生成到pdf文件中,同时也依赖了Libpng这个库,通过这两个库就可以生成pdf文档了。

还有一个c++的是PDFLib,因为PDFLib库对于个人是免费的,对于商业产品需要购买许可,所以就首选这个libharu了。

libharu官网地址:http://libharu.org/

libharu的Github下载地址:https://github.com/libharu/libharu

libpng的Github下载地址:https://github.com/glennrp/libpng

二、libharu的安装引入

下面三种方案,任选其一,我demo使用的是第三种

2.1、官方推荐的方法

官方推荐的方法在INSTALL里面有说明,下载之后,通过./configure && make && make install这几个脚本命令安装,如果是通过git下载的,是没有configure这个文件的,需要运行./buildconf.sh这个命令生成之后安装,可以参考官方教程:https://github.com/libharu/libharu/wiki/Installation,但是我在安装的时候,mac会提示这个报错

make:?aclocal:?No?such?file?or?directory
make:?***?[aclocal.m4]?Error?1

懒得去查了,所以就没有用官方推荐的方法了,但是官方推荐的应该是可以用的

2.2、使用ruby安装方案

这个是通过Homebrew安装,如果你电脑里面安装的有Homebrew,可以直接使用

brew?install?libharu

这个命令,如果没有安装,可以查看这个文章《mac用终端对ipa包重新签名》里面的Homebrew安装方法。

屏幕快照 2017-02-06 17.49.29.png
这个安装的是直接生成的库,并不推荐使用这个安装,通过这个安装的的库并没有开源的文件用起来舒服。

这个下载完成之后,把下载路径/usr/local/Cellar的libharu和libpng两个库的所有文件全部引入工程,如果运行报错,注意报错,删除那两个专门针对mac,不是针对手机的那两个库

2.3、引入源文件安装方案

2.3.1、libharu处理

1、把从github下载的libharu文件夹下的include文件里面的Makefile.am删除,然后把win32/include/hpdf_config.h文件复制到include文件夹下,把include文件夹整个引入工程

2、把src文件夹下的Makefile.am删除,然后把整个src文件夹引入工程

2.3.2、libpng处理

1、把从github下载的libpng最外面文件夹中的.c、.h文件,除了pngtest.c外,全部引入到工程中

2、把scripts文件夹下的pnglibconf.h.prebuilt文件名修改为pnglibconf.h也引入到工程中

2.3.3、引入系统库

1、引入libz.tbd

2、引入CoreGraphics.framework

2.3.4、其他报错处理:

1、libpng中报错

"_png_init_filter_functions_neon",?referenced?from:

这个错的原因是对NEON做了限制,解决方案 修改pngpriv.h文件的128行到136行左右:

#??ifdef?__ARM_NEON__
#?????define?PNG_ARM_NEON_OPT?2
#??else
#?????define?PNG_ARM_NEON_OPT?0
#??endif

都修改为

#define?PNG_ARM_NEON_OPT?0

2、libpng中报错

Undefined?symbols?for?architecture?x86_64:
??"_adler32",?referenced?from:
??????_png_icc_set_sRGB?in?libpng.a(png.o)
??"_crc32",?referenced?from:
??????_png_reset_crc?in?libpng.a(png.o)
??????_png_calculate_crc?in?libpng.a(png.o)
??????_png_icc_set_sRGB?in?libpng.a(png.o)

这类报错,需要注意是否引入了libz.tbd这个系统库

3、如果在自己写的PDFService.h文件中报错

arc?forbids?objective-c?objects?in?struct

需要把加上__unsafe_unretained关键字

typedef?struct?_PDFService_userData?{
????HPDF_Doc?pdf;
????__unsafe_unretained?PDFService?*service;
????__unsafe_unretained?NSString?*filePath;
}?PDFService_userData;

4、libpng报错

CgBI:?unhandled?critical?chunk

这个是因为生成pdf中图片问题,可以尝试需要在build setting里面,把Compress PNG Images和Remove Text Metadata From PNG Files这两个选项都设置为NO。

5、libharu报错<png.h> not found

这个主要就是libpng这个库没有引入正确

可以查考http://stackoverflow.com/questions/20954719/png-h-not-found-in-mac-os-x-mavericks

三、libharu的使用

3.1、生成pdf文档

引入hpdf.h头文件,然后创建设置参数

-?(void)createPDFFile:(NSString?*)filePath
{
????//?Creates?a?test?PDF?file?to?the?specified?path.
????//?TODO:?use?UIImage?to?create?non-optimized?PNG?rather?than?build?target?setting
????NSString?*path?=?nil;
????const?char?*pathCString?=?NULL;

????LOG(@"[libharu]?PDF?Creation?START");
????PDFService_userData?userData;
????HPDF_Doc?pdf?=?HPDF_New(PDFService_defaultErrorHandler,?&userData);
????userData.pdf?=?pdf;
????userData.service?=?self;
????userData.filePath?=?filePath;
????LOG(@"[libharu]?Adding?page?1");
????HPDF_Page?page1?=?HPDF_AddPage(pdf);
????LOG(@"[libharu]?SetSize?page?1");
????HPDF_Page_SetSize(page1,?HPDF_PAGE_SIZE_A4,?HPDF_PAGE_LANDSCAPE);
????LOG(@"[libharu]?TextOut?page?1");
????HPDF_Page_BeginText(page1);
????//英文
????HPDF_Font?fontEn?=?HPDF_GetFont(pdf,?"Helvetica",?"StandardEncoding");
????//中文
????HPDF_UseCNSFonts(pdf);
????HPDF_UseCNSEncodings(pdf);
????HPDF_Font?fontCH?=?HPDF_GetFont(pdf,?"SimSun",?"GBK-EUC-H");
????//日文
????HPDF_UseJPFonts?(pdf);
????HPDF_UseJPEncodings?(pdf);
????HPDF_Font?fontJP?=?HPDF_GetFont(pdf,?"MS-PGothic",?"90ms-RKSJ-H");
????HPDF_Page_SetFontAndSize(page1,?fontEn,?16.0);
????HPDF_Page_TextOut(page1,?50.00,?500.00,?"Hello?libHaru!");
????HPDF_Page_SetFontAndSize(page1,?fontCH,?16.0);
????HPDF_Page_TextOut(page1,?50.00,?460.00,?[@"胡东东博客"?cStringUsingEncoding:CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000)]);
????HPDF_Page_SetFontAndSize(page1,?fontJP,?16.0);
????HPDF_Page_TextOut(page1,?50.00,?420.00,?[@"はろー日本語"?cStringUsingEncoding:NSShiftJISStringEncoding]);
????HPDF_Page_EndText(page1);
????LOG(@"[libharu]?Path?drawing?page?1");
????HPDF_Page_SetLineWidth(page1,?4.0);
????HPDF_Page_SetRGBStroke(page1,?1.0,?0,?0);
????HPDF_Page_Rectangle(page1,?200,?200,?40,?150);
????HPDF_Page_Stroke(page1);
????LOG(@"[libharu]?PNG?image?drawing?page?1");
????//?comment?out?this?line?intentionally?causes?an?error?here?to?test?error?handling
//????path?=?[[NSBundle?mainBundle]?pathForResource:@"no_such_file_hogehoge"
//???????????????????????????????????????????ofType:@"png"];
????path?=?[[NSBundle?mainBundle]?pathForResource:@"test"
???????????????????????????????????????????ofType:@"png"];
????pathCString?=?[path?cStringUsingEncoding:NSASCIIStringEncoding];
????LOG(@"[libharu]?LoadPngImageFromFile?path:%@\n?pathCString:%s",?path,?pathCString);
????HPDF_Image?image?=?HPDF_LoadPngImageFromFile(pdf,?pathCString);
????HPDF_Page_DrawImage(page1,?image,?260,?240,?245,?319);

????pathCString?=?[filePath?cStringUsingEncoding:1];
????LOG(@"[libharu]?SaveToFile?filePath:%@\n?pathCString:%s",?filePath,?pathCString);
????HPDF_SaveToFile(pdf,?pathCString);
????LOG(@"[libharu]?Freeing?PDF?object?");
????if?(HPDF_HasDoc(pdf))?{
????????HPDF_Free(pdf);
????}
????LOG(@"[libharu]?PDF?Creation?END");
}

Simulator Screen Shot 2017年2月6日 17.18.03.png

这里需要注意的是libHaru如果需要生成多种语言混合的PDF,比如中英日文,就需要先声明要使用哪种语言了,比如日文,使用的时候,需要先声明用日文

//日文
HPDF_UseJPFonts?(pdf);
HPDF_UseJPEncodings?(pdf);
HPDF_Font?fontJP?=?HPDF_GetFont(pdf,?"MS-PGothic",?"90ms-RKSJ-H");
HPDF_Page_SetFontAndSize(page1,?fontJP,?16.0);
HPDF_Page_TextOut(page1,?50.00,?420.00,?[@"はろー日本語"?cStringUsingEncoding:NSShiftJISStringEncoding]);

HPDF_GetFont是获取字体,第二个参数和第三个参数是看库中是实现的,比如日文,就参考hpdf_fontdef_jp.c这个文件中的字体。 在HPDF_Page_TextOut输出中,最后一个参数传值是const char*类型的,所以如果是中文,因为是GBK的编码,就需要使用GBK转码

[@"汉语"?cStringUsingEncoding:CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000)]

否则会乱码。

3.2、读取pdf文档

读取是通过webview读取的

-(void)getPDF{
????NSLog(@"getPDF");
????NSArray?*arrayPaths?=
????NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
????NSString?*path?=?[arrayPaths?objectAtIndex:0];
????path?=?[path?stringByAppendingPathComponent:@"test.pdf"];
????NSURL?*url?=?[NSURL?fileURLWithPath:path];
????NSURLRequest?*request?=?[NSURLRequest?requestWithURL:url];
????UIWebView?*web?=?[[UIWebView?alloc]?initWithFrame:CGRectMake(10,?300,?300,?300)];
????[self.view?addSubview:web];
????[web?loadRequest:request];
}

3.3、分享pdf文档

生成pdf文档之后,可以分享出去文件,分享可以使用UIDocumentInteractionController这个类来分享

-(void)sharePDF{
    NSLog(@"需要注意pdf文件是否存在,这里是需要先creatPDF生成文件,否则会抛出NSInternalInconsistencyException异常");
    UIDocumentInteractionController *documentController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:self.m_filePath]];
//    documentController.delegate = self;
    
    documentController.UTI = @"com.adobe.pdf";
    [documentController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES];
}


documentController.UTI 的参数可以根据要打开的文档格式参考苹果的官方文档:Uniform Type Identifiers Reference

如果抛出NSInternalInconsistencyException异常,那么需要确认下生成的pdf文档是否生成了,如果没有生成,生成的documentController是空的。

如果想让自己的应用在打开pdf文档中的列表的话,可以参考下面的参考文档。

四、demo下载:

Github下载:https://github.com/DamonHu/PDFDemo

Gitosc下载:http://git.oschina.net/DamonHoo/PDFDemo

五、参考文章:

Last modification:February 13th, 2017 at 06:51 pm
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

Leave a Comment