PDF文档的几种打开方式

作者: 普通的工具人 | 来源:发表于2016-02-29 22:52 被阅读2950次

    之前的老项目遇到了一个问题:iOS9后PDF中文会显示乱码,而且试了各种方法都不行,还好最终找到了解决方法---MuPDF
    在此总结一下解决该问题过程中使用过的方法:

    1.使用UIWebView加载

    //代码很简单
    UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:webView];
        
    NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"pdf"];
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:path]];
    [webView loadRequest:request];
    

    2.使用QLPreviewController打开

    //初始化QLPreviewController对象
    QLPreviewController *previewController = [[QLPreviewController alloc] init];
    previewController.dataSource = self;
    [self.navigationController pushViewController:pageController animated:YES];
    
    //再实现QLPreviewControllerDataSource的两个方法即可显示
    //显示文件数量
    - (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller;
    //文件路径URL
    - (id <QLPreviewItem>)previewController: (QLPreviewController *)controller previewItemAtIndex:(NSInteger)index;
    

    3.使用CGContextDrawPDFPage绘制

    UIView- (void)drawRect:(CGRect)rect方法中绘制PDF内容,代码如下:

    /*
    Quartz2D              UIKit
     
            y               (0, 0)|----------x
            |                     |
            |                     |
            |                     |
            |                     |
     (0, 0) |---------x           y
    
    */
    - (void)drawRect:(CGRect)rect {
        // Drawing code
        
        CGContextRef context = UIGraphicsGetCurrentContext();
        
        //调整坐标系
        CGContextTranslateCTM(context, 0.0, self.bounds.size.height);//先垂直下移height高度
        CGContextScaleCTM(context, 1.0, -1.0);//再垂直向上翻转
        
        //绘制pdf内容
        CGPDFPageRef pageRef = CGPDFDocumentGetPage(pdfDocument, page);
        CGContextSaveGState(context);
        CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(pageRef, kCGPDFCropBox, self.bounds, 0, true);
        CGContextConcatCTM(context, pdfTransform);
        CGContextDrawPDFPage(context, pageRef);
        CGContextRestoreGState(context);
    }
    

    需要说明的是:

    • Quartz 2D坐标系的原点在界面左下角,而UIKit坐标系的原点在左上角,所以需要对画布进行翻转,才能得到正确的视图;
    • 方法中需要传入page和pdfDocument,CGContextDrawPDFPage是按照页码来一张张绘制的;

    再放入UIPageViewController中来展示,翻页效果杠杠的(具体效果见最后Demo)。

    顺便说说UIPageViewController的使用

    • UIPageViewControllerde感觉就像是一个书夹,其中每一页是一个UIViewController,页面内容在UIViewController的视图中绘制,翻页效果是自带的,你只要绘制好要展示的画面就好了;

    • 在初始化UIPageViewControllerde对象的时候,就要加载第一页,避免开始时出现空白页;

    • 在下面方法中写上一页,下一页的调用逻辑;

    //加载某一页调用方法
    - (void)setViewControllers:(nullable NSArray<UIViewController *> *)viewControllers direction:(UIPageViewControllerNavigationDirection)direction animated:(BOOL)animated completion:(void (^ __nullable)(BOOL finished))completion;
    
    //上一页视图控制器
    - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
    //下一页视图控制器
    - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
    
    • UIPageViewControllerde显示的内容没有缩放的功能,所以需要自己加。我是在UIViewController视图里面嵌了一层UIScrollView,设置其缩放系数minimumZoomScale, maximumZoomScale,然后实现下面的代理方法来实现缩放的。
    - (nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
    

    当然,这也可以用其他方法来完成,比如:UIPinchGestureRecognizer

    4.MuPDF类库的使用

    MuPDF官网:http://www.mupdf.com/

    4.1 生成依赖包

    1. 源码下载:git clone --recursive git://git.ghostscript.com/mupdf.git
    2. 下载完成后,进入mupdf/platform/ios目录下;
    3. 打开MuPDF工程;
    4. 修改工程的Build Configuration为Release;
    5. 选择iPhone模拟器,运行这个工程,这将生成i386或x86_64结构的.a包(根据你选择的模拟器类型);
    6. 连接你的iPhone,修改你自己的bundle identifier, certificate和 provisioning profile,在真机环境下运行该程序,这将会生成armv7,arm64架构的.a包;
    7. 同理,修改工程的Build Configuration为Debug,可以生成Debug对应的.a包;
    8. 进入mupdf/build/,你会找到各个架构下的文件夹,里面包含了所有编译之后生成的包:
    9. 现在,你可以使用lipo命令去合并些架构下的依赖包,这将会生成比较大的包;

    使用lipo命令合并.a包:

    Last login: Mon Feb 29 20:43:50 on console
    promote:~ mac$ cd /Users/mac/Desktop/lib 
    promote:lib mac$ lipo -create libmupdf_arm64.a libmupdf_x86_64.a -output libmupdf.a
    promote:lib mac$ lipo -info libmupdf.a
    Architectures in the fat file: libmupdf.a are: x86_64 arm64 
    promote:lib mac$ lipo -create libmupdfthird_arm64.a libmupdfthird_x86_64.a -output libmupdfthird.a
    promote:lib mac$ lipo -info libmupdfthird.a
    Architectures in the fat file: libmupdfthird.a are: x86_64 arm64 
    promote:lib mac$ 
    
    
    lipo之后的包
    • 我只合并了debug中的arm64和x86_64的包,要是再合并i386就太大了。

    4.2 开始集成

    1. 添加所需要的源码文件:
      mupdf/include/mupdf路径下的所有头文件;
      mupdf/platform/ios/classes路径下的所有obj-c类;
      mupdf/platform/ios路径下的common.h,common .m
    2. 添加之前生成好的依赖包到你的工程中;
    3. 配置你工程的Library Search Path,添加依赖包的路径,
      比如:$(inherited) $(PROJECT_DIR)/External/MuPDF/lib/
    4. 由于库中引用文件#include "mupdf/fitz.h",使用include编译指令,所以头文件绝对路径=搜索路径+相对路径,所以需要配置搜索路径Header Search Paths"$(SRCROOT)/OpenPFDemo/ThirdLib/include"

    现在,你应该就可以运行你的程序了(具体的文件目录见Demo)。

    4.3 提示:

    1. 由于合并之后的依赖包比较大,所以为了减少你App的大小,你可以配置两个路径:
      在release文件夹下添加mupdf/build/release-ios-armv7-arm64路径下的依赖包;
      在debug文件夹下添加mupdf/build/debug-ios-arm64和debug-ios-x86_64路径下的合并后的包;
    • 在你的工程配置Library Search Path中,分别配置Debug和Release的路径;

    这样你在模拟器和真机Debug环境下使用的就是Debug对应的依赖包,而在打包的时候使用的就是Release对应的依赖包,而不会包含其他依赖包。

    4.4 注意

    • 按照 stackoverflow上面的这个问答的集成步骤在当前最新的MuPDF版本上集成时会有些出入,所以我修改了一下;
    • 由于这个库包含了很多平台(见platform文件夹下),所以比较大,要等完全下载完成之后再使用;
    • 下载完成之后,运行其中的iOS代码,发现居然报错了(我用的xcode7.0)
      原因是找不到pdf_write_document(ctx, idoc, tmp, &opts)方法。

    解决方法:将MuDocumentController.m中的pdf_write_document(ctx, idoc, tmp, &opts);改为pdf_create_document(ctx);即可;

    4.6 使用打开PDF

    • 首先,在程序启动后,初始化相关参数:
    enum
    {
        ResourceCacheMaxSize = 128<<20  /**< use at most 128M for resource cache */
    };
    
    queue = dispatch_queue_create("com.artifex.mupdf.queue", NULL);
    ctx = fz_new_context(NULL, NULL, ResourceCacheMaxSize);
    fz_register_document_handlers(ctx);
    
    • 然后调用下面方法打开:
    - (void)openMuPDF:(NSString *)path name:(NSString *)name
    {
        _filePath = malloc(strlen([path UTF8String])+1);
        
        strcpy(_filePath, [path UTF8String]);
        
        dispatch_sync(queue, ^{});
        
        printf("open document '%s'\n", _filePath);
        
        _filename = [name retain];
        
        doc = [[MuDocRef alloc] initWithFilename:_filePath];
        if (!doc) {
            
            return;
        }
        
        if (fz_needs_password(ctx, doc->doc))
        {
            [self askForPassword: @"'%@' needs a password:"];
        }
        else
        {
            [self onPasswordOkay];
        }
    }
    
    - (void)askForPassword: (NSString*)prompt
    {
        UIAlertView *passwordAlertView = [[UIAlertView alloc]
                                          initWithTitle: @"Password Protected"
                                          message: [NSString stringWithFormat: prompt, [_filename lastPathComponent]]
                                          delegate: self
                                          cancelButtonTitle: @"Cancel"
                                          otherButtonTitles: @"Done", nil];
        [passwordAlertView setAlertViewStyle: UIAlertViewStyleSecureTextInput];
        [passwordAlertView show];
        [passwordAlertView release];
    }
    
    - (void)onPasswordOkay
    {
        MuDocumentController *document = [[MuDocumentController alloc] initWithFilename:_filename path:_filePath document:doc];
        if (document) {
            [self setTitle: @"Library"];
            [[self navigationController] pushViewController:document animated:YES];
            [document release];
        }
        [_filename release];
        free(_filePath);
    }
    

    Demo下载链接:OpenPDFDemo

    @Kangqj

    相关文章

      网友评论

      • SoulmateA:QLPreviewController无法显示PDF内容请问是什么问题呢?
      • 小蜗牛成长记:你好,请问现在MuPDF还能用吗?为什么您把demo删除了?
        下载MuPDF文件包里面找不到ios的demo了?能分享一下吗?
      • de4dce0c9bbb:你好,使用mupdf , 做PDF 的相关批注,能分享下怎么实现吗? 感谢
      • 嫁给猿吧:既然放demo,就放一个可以运行的,随便抄一个往这里一放有啥意思
      • Micro同学:大神 求问 mupdf怎么加水印呀。。。 想要的效果是 有现成的pdf文件 打开之后是看到有水印的样子。。。
      • ea105108a443:你好,mupdf怎么获取到标记的文字呢
      • ea105108a443:为啥我加入的include的文件路劲不对呢
      • 881ac1c7bd02:楼主在吗?最近在弄PDF,使用git clone --recursive git://git.ghostscript.com/mupdf.git下载下来没有ios版本的,不知道什么原因,求搭救,QQ:2322601449
        雨影:你确定?我中午刚下载下来一点问题都没有
        881ac1c7bd02:@雨影 官网下载的mupdf-iso-viewer中的generate不能编译,怎么破?
        雨影:直接去官网就可以下载了,不过有个问题,这个库是非 ARC 的转换成 ARC 的时候,内存会一直增加不减少.
      • 溜溜leesin:大神 能回复一下吗?我现在也在做PDF方面的app,主要涉及PDF浏览 涂鸦 保存,更新。我改用哪一个呢?能回复我?
      • 73926c633cf5:但我发现一个问题,使用mupdf显示PDF时,没有返回按钮。不过点击屏幕左上角是可以返回的
      • 73926c633cf5:请教楼主,我使用MUPDF,我想把PDF拉伸,使得PDF的宽和高都铺满屏幕,应该如何修改?
        望不吝赐教!
        73926c633cf5:CGSize fitPageToScreen(CGSize page, CGSize screen)
        {
        /* luotao
        float hscale = screen.width / page.width;
        float vscale = screen.height / page.height;
        float scale = fz_min(hscale, vscale);
        hscale = floorf(page.width * scale) / page.width;
        vscale = floorf(page.height * scale) / page.height;
        return CGSizeMake(hscale, vscale);*/

        float hscale = screen.width / page.width;
        float vscale = screen.height / page.height;
        return CGSizeMake(hscale, vscale);
        }
        这样就可以了。
      • 8d8728c22a40:还有,用CGContextDrawPDFPage方式打开pdf,有没有办法实现文本选择?
      • 8d8728c22a40:UIWebview打开pdf能水平显示吗?
      • MarkMiao:有个发票的pdf文件,上面印章显示不出来。UIWebView、Quartz 2D、QLPreviewController都试验过,都不行。查了一下因为pdf有多个图层,部分图层读不出来。除了MuPDF还有其他方式读出pdf的所有图层内容吗?
      • 孤独感爆棚:QLPreviewController预览pdf时怎么隐藏掉导航栏啊
        孤独感爆棚:最后没有用这种方法,换成网页打开
      • b401a2d97ff2:mupdf这个类库不错,解决了我项目一个问题,感谢。
        普通的工具人:@TroyNieh 不客气,分享出来就是为了能够帮其他人解决一些问题。

      本文标题:PDF文档的几种打开方式

      本文链接:https://www.haomeiwen.com/subject/gwokkttx.html