之前用过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安装方法。

 title=
这个安装的是直接生成的库,并不推荐使用这个安装,通过这个安装的的库并没有开源的文件用起来舒服。

这个下载完成之后,把下载路径/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");
}

 title=

这里需要注意的是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

五、参考文章:


☟☟可点击下方广告支持一下☟☟

最后修改:2021 年 03 月 07 日
请我喝杯可乐,请随意打赏: ☞已打赏列表