关于适用于iPhone / iPad / iOs的快速和精益PDF阅读器:适用于iPhone / iPad / iOs的快速和精益PDF阅读器 – 提示和提示?

Fast and Lean PDF Viewer for iPhone / iPad / iOs - tips and hints?

最近有很多关于绘制PDF的问题。

是的,使用UIWebView可以很容易地呈现PDF,但这不能提供良好的PDF查看器所期望的性能和功能。

您可以将PDF页面绘制到Calayer或UIImage。苹果甚至有示例代码来显示如何在可缩放的uiscrollview中绘制大型PDF。

但同样的问题不断出现。

ui图像方法:

  • PDF在UIImage中,不在光学上缩放以及图层方法。
  • CPU和内存命中生成来自PDFcontextUIImages。限制/阻止使用它创建实时渲染新的缩放级别。
  • 阳离子层法:

  • 有很大的开销(时间)在CALayer上绘制一个完整的pdf页面:可以看到单独的图块渲染(即使使用tileSize调整)
  • CALayers不能提前准备时间(在屏幕外渲染)。
  • 一般来说,PDF浏览器的内存也相当大。甚至监控苹果的可缩放PDF示例的内存使用情况。

    在我当前的项目中,我正在开发一个PDF查看器,并在一个单独的线程中呈现一个页面的UIImage(这里也有问题!)在比例尺为x1时显示。当比例大于1时,CATiledLayer渲染开始。iBooks采用了类似的双取方法,就像滚动页面一样,在出现清晰版本之前,您可以看到页面的低分辨率版本不到一秒钟。

    我在焦点处呈现页面的每一面2页,这样PDF图像就可以在开始绘制之前屏蔽层了。当页面距离焦点页面+2页时,页面会再次被破坏。

    是否有人有任何见解,无论多么微小或明显,以提高绘图PDF的性能/内存处理?或者这里讨论的其他问题?

    编辑:一些提示(信用-卢克·麦克内西,维德斯梅特,马特·加拉赫,约翰):

    • 尽可能将任何媒体保存到磁盘。

    • 如果在平铺层上渲染,则使用较大的平铺

    • 初始化带有占位符对象的常用数组,交替地,另一种设计方法是

    • 请注意,图像的渲染速度将比CGPDFPageRef快。

    • 使用NSOperations或gcd&block提前准备页面时间的问题。

    • CGContextDrawPDFPage之前调用CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);以减少绘制时的内存使用

    • 用docref初始化你的NSOperations是一个坏主意(内存),把docref包装成一个单例。

    • 如果可以的话,取消不必要的NSOperations,特别是如果他们将使用内存,那么请注意不要让上下文打开!

    • 回收页面对象并销毁未使用的视图

    • 一旦你不需要,就关闭所有打开的上下文

    • 收到内存警告时,释放并重新加载docref和任何页面缓存

    其他PDF功能:

    • 获取PDF中的链接(以及此处和此处)

      • 了解链接定位的PDF rect

      • 转换PDF Annot日期字符串

      • 获取链接的目标(从/Dest数组中获取页码)

    • 获取目录

    • 文档标题和关键字

    • 获取原始文本(此处、此处和此处(重点定位))

    • 搜索(这里)(不适用于所有的PDF(有些只是显示奇怪的字符,我想这是一个编码问题,但我不确定)-CreditBrainFeeder)

    • Calayer和屏幕外渲染-渲染下一页以快速/平滑显示

    文档

    • Quartz PDFObjects(用于元信息、注释、拇指)
    • Abobe PDF规范

    示例项目

    • apple/zoomingpdf-缩放,UIScrollViewCATiledLayer
    • vfr/读卡器-缩放、分页、UIScrollViewCATiledView
    • 眉头/树叶-寻呼和良好的过渡
    • /skim-所有看起来的东西(osx的pdf阅读器/编辑器)


    我已经使用近似相同的方法构建了此类应用程序,除了:

    • 我将生成的映像缓存在磁盘上,并且总是在单独的线程中提前生成两到三个映像。
    • 我不使用UIImage覆盖,而是在缩放为1时在图层中绘制图像。当发出内存警告时,这些磁贴将自动释放。

    每当用户开始缩放时,我就获取CGPDFPage,并使用适当的CTM进行渲染。- (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) context中的代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    CGAffineTransform currentCTM = CGContextGetCTM(context);    
    if (currentCTM.a == 1.0 && baseImage) {
        //Calculate ideal scale
        CGFloat scaleForWidth = baseImage.size.width/self.bounds.size.width;
        CGFloat scaleForHeight = baseImage.size.height/self.bounds.size.height;
        CGFloat imageScaleFactor = MAX(scaleForWidth, scaleForHeight);

        CGSize imageSize = CGSizeMake(baseImage.size.width/imageScaleFactor, baseImage.size.height/imageScaleFactor);
        CGRect imageRect = CGRectMake((self.bounds.size.width-imageSize.width)/2, (self.bounds.size.height-imageSize.height)/2, imageSize.width, imageSize.height);
        CGContextDrawImage(context, imageRect, [baseImage CGImage]);
    } else {
        @synchronized(issue) {
            CGPDFPageRef pdfPage = CGPDFDocumentGetPage(issue.pdfDoc, pageIndex+1);
            pdfToPageTransform = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFMediaBox, layer.bounds, 0, true);
            CGContextConcatCTM(context, pdfToPageTransform);    
            CGContextDrawPDFPage(context, pdfPage);
        }
    }

    问题是包含CGPDFDocumentRef的对象。我同步访问pdfDoc属性的部分,因为我释放它并在接收memorywarning时重新创建它。似乎CGPDFDocumentRef对象做了一些内部缓存,但我没有找到如何摆脱这些缓存。


    对于简单有效的PDF查看器,当您只需要有限的功能时,现在可以(iOS 4.0+)使用QuickLook框架:

    首先,您需要链接到QuickLook.framework#import
    ;

    之后,在viewDidLoad或任何延迟初始化方法中:

    1
    2
    3
    4
    5
    6
    QLPreviewController *previewController = [[QLPreviewController alloc] init];
    previewController.dataSource = self;
    previewController.delegate = self;
    previewController.currentPreviewItemIndex = indexPath.row;
    [self presentModalViewController:previewController animated:YES];
    [previewController release];


    自iOS11以来,您可以使用名为pdfkit的本机框架来显示和操作pdf。

    导入pdfkit后,应使用本地或远程URL初始化PDFView,并在视图中显示它。

    1
    2
    3
    4
    5
    if let url = Bundle.main.url(forResource:"example", withExtension:"pdf") {
        let pdfView = PDFView(frame: view.frame)
        pdfView.document = PDFDocument(url: url)
        view.addSubview(pdfView)
    }

    请阅读Apple开发者文档中有关PDFKIT的更多信息。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     CGAffineTransform currentCTM = CGContextGetCTM(context);     if
     (currentCTM.a == 1.0 && baseImage)  {
         //Calculate ideal scale
         CGFloat scaleForWidth = baseImage.size.width/self.bounds.size.width;
         CGFloat scaleForHeight = baseImage.size.height/self.bounds.size.height;
         CGFloat imageScaleFactor = MAX(scaleForWidth, scaleForHeight);
         CGSize imageSize = CGSizeMake(baseImage.size.width/imageScaleFactor,
     baseImage.size.height/imageScaleFactor);
         CGRect imageRect = CGRectMake((self.bounds.size.width-imageSize.width)/2,
     (self.bounds.size.height-imageSize.height)/2, imageSize.width,
     imageSize.height);
         CGContextDrawImage(context, imageRect, [baseImage CGImage]); }  else  {
         @synchronized(issue)  {
             CGPDFPageRef pdfPage = CGPDFDocumentGetPage(issue.pdfDoc, pageIndex+1);
             pdfToPageTransform = CGPDFPageGetDrawingTransform(pdfPage, kCGPDFMediaBox, layer.bounds, 0, true);
             CGContextConcatCTM(context, pdfToPageTransform);    
             CGContextDrawPDFPage(context, pdfPage);
         } }