之前用过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安装方法。
这个安装的是直接生成的库,并不推荐使用这个安装,通过这个安装的的库并没有开源的文件用起来舒服。
这个下载完成之后,把下载路径/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");
}
这里需要注意的是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
五、参考文章:
- C++ 用PDFLib生成PDF文档
- 十个最好的PDF生成库
- iPhonePDF
- 【从简】秒懂iOS文件分享
- 【iOS功能实现】之利用UIDocumentInteractionController打开和预览文档
版权属于:东哥笔记 - DongGe.org
本文链接:https://dongge.org/blog/399.html
自2017年12月26日起,『转载以及大段采集进行后续编辑』须注明本文标题和链接!否则禁止所有转载和采集行为!