适用于iPhone / iPad / iOS的Fast and Lean PDF Viewer-提示和提示?


368

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

是的,您可以使用轻松渲染PDF,UIWebView但这不能提供您期望的优质PDF查看器所具有的性能和功能。

您可以将PDF页面绘制到CALayerUIImage。苹果甚至提供了示例代码来展示如何在Zoomable UIScrollview中绘制大型PDF

但是同样的问题不断出现。

UIImage方法:

  1. PDF UIImage不像图层方法那样光学缩放。
  2. CPU和内存击中产生UIImagesPDFcontext 使用它来创建一个实时渲染的新的放大级别限制/防止。

CATiledLayer方法:

  1. 将整个PDF页面绘制到上有大量开销(时间)CALayer:可以看到各个图块的呈现(即使使用tileSize进行了调整)
  2. CALayers 无法提前准备(在屏幕外渲染)。

通常,PDF查看器也占用大量内存。甚至监视苹果可缩放PDF示例的内存使用情况。

在我当前的项目中,我正在开发PDF查看器,并UIImage在单独的线程中渲染页面的A(此处也有问题!),并在比例为x1时显示它。CATiledLayer比例大于1时,渲染就会开始。iBooks采用类似的重复处理方法,就好像您滚动页面一样,在显示清晰版本之前,可以看到页面的较低分辨率版本不到一秒钟。

我在页面的每一侧都渲染了2页,以便PDF图像准备好在开始绘制之前掩盖该层。当页面距聚焦页面+2页时,页面将再次被销毁。

是否有任何见解,无论大小或明显程度如何提高Drawing PDF的性能/内存处理能力?还是这里讨论的任何其他问题?

编辑:一些技巧(信贷-卢克·麦克尼斯,VdesmedT,马特·加拉格尔,约翰):

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

  • 如果在TiledLayers上渲染,请使用更大的tileSize

  • INIT频繁使用的阵列,占位符对象,alternitively另一设计方法是这一个

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

  • 使用NSOperations或GCD和来提前准备页面。

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

  • 用docRef初始化您的NSOperations主意(内存)是一个坏主意,将docRef包装为单例。

  • 不需要NSOperations时取消可以的话,尤其是当他们将使用内存时,请当心不要打开上下文!

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

  • 不需要任何打开的上下文后,立即关闭它们

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

其他PDF功能:

文献资料

示例项目


评论以确保窥视得到编辑通知
Luke Mcneice 2010年

+1,感谢您添加所有这些信息,希望在开发阅读器时得到它;)也感谢您添加有关PDF批注的问题(它也包含示例代码的答案)。几天前,我打开了这个书:stackoverflow.com/questions/4097044/pdf-search-on-the-iphone您有什么提示吗?
pt2ph8 2010年

我还没有自己介绍这个问题,所以除了指向您指向随机想法博客之外,我无话可说random-ideas.net/posts/42不过,感谢您的发帖,我试图将所有PDF问题收集到一个地点。
卢克·麦克尼斯

8
在我的公司,我们使用PDF呈现,符号等的第三方解决方案称为PSPDFKit,这是不便宜,但值得:pspdfkit.com
亚诺什

1
+1我遵循了这些有用的技巧,适用于我的开源pdf查看器Swifty PDF github.com/prcela/SwiftyPDF
Prcela '16

Answers:


103

我已经使用大约相同的方法构建了这种应用程序,除了:

  • 我将生成的映像缓存在磁盘上,并且始终总是在单独的线程中预先生成两到三个映像。
  • 我没有覆盖a UIImage,而是在缩放为1时在图层中绘制图像。当发出内存警告时,这些图块将自动释放。

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

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属性的部分,因为我释放它并在收到memoryWarnings时重新创建它。似乎该CGPDFDocumentRef对象做了一些内部缓存,而我没有找到如何摆脱它。


1
用户开始缩放时,您的处理方法是什么?
卢克·麦克尼斯

2
@Luke:我修改了帖子以回答
VdesmedT 2010年

1
非常感谢您,以及有关CGPDFDocumentRef缓存的提示。
卢克·麦克尼斯

只是一个简单的问题:当比例为1.0时,为什么要获取pdfPageRef并进行转换?因为我看到您在创建转换之前绘制了baseImage(来自bg worker的图像?)。
卢克·麦克尼斯

@Luke:请在此处发布您可以进行的任何性能改进。谢谢 !
VdesmedT

67

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

首先,您需要针对QuickLook.framework#import <QuickLook/QuickLook.h>;

然后,使用viewDidLoad任何一种或多种惰性初始化方法:

QLPreviewController *previewController = [[QLPreviewController alloc] init];
previewController.dataSource = self;
previewController.delegate = self;
previewController.currentPreviewItemIndex = indexPath.row;
[self presentModalViewController:previewController animated:YES];
[previewController release];

是的,这在概念上与使用UIWebView相同。如果那样简单,我们将不会花费很多时间来弄清楚如何使用CGPDF *。
pt2ph8 2011年

5
当然可以。我当然不会将其作为完整的解决方案。但是,这个问题的某些读者可能并不知道,出于某些目的,这是一个合理的解决方案。更新了答案以使其更清楚。
约书亚·麦金农

6
为此+1。我的主要兴趣是允许用户查看PDF格式的一些应用程序内文档。我不在乎搜索,突出显示或其他任何花哨功能,而只是高效的PDF渲染。
记忆

2
我的UIImage + PDF类别是带有内置缓存层的快速的免费PDF渲染器。 github.com/mindbrix/UIImage-PDF
Mindbrix 2014年

6

iOS 11开始,您可以使用称为PDFKit的本机框架来显示和处理PDF。

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

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 Developer文档中阅读有关PDFKit的更多信息。


-2
 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);
     } }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.