适用于iPhone / iPad / iOs的快速和精简PDF查看器 – 提示和提示?
最近有很多关于绘制PDF的问题。
是的,您可以使用UIWebView
非常轻松地呈现PDF,但是不能提供您期望从一个好的PDF查看器获得的性能和function。
您可以将PDF页面绘制到CALayer或UIImage 。 苹果甚至有示例代码来展示如何在Zoomable UIScrollview中绘制大型PDF
但同样的问题还在不断出现。
UIImage方法:
-
UIImage
PDF在光学上并不像Layer方法那么好。 - 从
PDFcontext
生成UIImages
的CPU和内存限制/阻止使用它来创build新的缩放级别的实时渲染。
CATiledLayer方法:
- Theres一个重要的开销(时间)绘制一个完整的PDF页面
CALayer
:个别瓷砖可以看到渲染(即使有tileSize调整) -
CALayers
不能提前准备(脱离屏幕)。
一般来说PDF阅读器在内存上也相当重要。 甚至监控苹果的可缩放PDF实例的内存使用情况。
在我当前的项目中,我正在开发一个PDF查看器,并在一个单独的线程中渲染一个页面的UIImage
(在这里也有问题!),并在scale为x1时显示它。 一旦比例大于1, CATiledLayer
渲染就会启动。 iBooks采取了类似的双重处理方法,就像在页面上滚动页面一样,在清晰的版本出现之前,您可以看到页面的较低分辨率版本不到一秒。
Im在页面的每一面都呈现2页的焦点,这样PDF图像就可以在开始绘制之前对图层进行遮罩了。页面在离焦点页面+2页时被再次销毁。
有没有人有任何见解,无论多么微小或明显的改善绘图PDF的性能/内存处理? 或在这里讨论的任何其他问题?
编辑:一些技巧(信用,卢克Mcneice,VdesmedT,马特加拉格尔,约翰):
-
尽可能将所有媒体保存到磁盘。
-
如果在TiledLayers上渲染,则使用较大的tileSizes
-
初始化常用的占位符对象的数组,另一种devise方法是另一种
-
请注意,图像渲染速度比
CGPDFPageRef
快 -
使用
NSOperations
或GCD& Blocks提前准备页面。 -
调用
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
在CGContextDrawPDFPage
之前减less绘图时的内存使用量 -
用docRef启动你的NSOperations是一个坏主意(内存),把docRef包装成一个单例。
-
取消不必要的
NSOperations
当你可以,特别是如果他们将使用内存,要小心留下的上下文打开虽然! -
回收页面对象并销毁未使用的视图
-
一旦你不需要它们,closures所有打开的上下文
-
在收到内存警告释放并重新加载DocRef和任何页面caching
其他PDFfunction:
-
在PDF中获取链接 (以及这里和这里 )
-
了解PDF Rect链接定位
-
转换PDF注释datestring
-
获取链接的目标 (从
/Dest
数组获取页码)
-
-
获取目录
-
文档标题和关键字
-
获取原始文本 (以及这里和这里和这里 (定位重点))
-
search (和这里 )(不适用于所有的PDF(有些只是显示怪异的字符,我猜这是一个编码问题,但我不知道) – 信用BrainFeeder)
-
CALayer和离屏渲染 – 渲染下一页,以便快速/平滑显示
文档
- Quartz PDFObjects (用于元信息,注释,大拇指)
- Abobe PDF Spec
示例项目
- Apple / ZoomingPDF – 缩放,
UIScrollView
,CATiledLayer
- vfr / reader – 缩放,分页,
UIScrollView
,CATiledView
- 眉毛/叶 – 分页与漂亮的过渡
- / skim – 一切似乎(OSX的PDF阅读器/编辑器)
我已经使用近似相同的方法构build了这样的应用程序,除了:
- 我将生成的图像caching在磁盘上,并在一个单独的线程中提前生成两到三个图像。
- 我不覆盖
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时重新创build它。 看来, CGPDFDocumentRef
对象做了一些内部caching,我没有find如何摆脱。
对于简单而有效的PDF查看器,当您只需要有限的function时,您现在可以(iOS 4.0+)使用QuickLook框架:
QLPreviewController *previewController = [[QLPreviewController alloc] init]; previewController.dataSource = self; previewController.delegate = self; previewController.currentPreviewItemIndex = indexPath.row; [self presentModalViewController:previewController animated:YES]; [previewController release];
您需要链接到QuickLook.framework
和#import <QuickLook/QuickLook.h>
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); } }