适用于 iPhone / iPad / iOs 的快速精益 PDF 查看器 - 提示和提示?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3889634/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 17:51:57  来源:igfitidea点击:

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

iospdfcalayer

提问by Luke Mcneice

There has been many Questions recently about drawing PDF's.

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

Yes, you can render PDF's very easily with a UIWebViewbut this cant give the performance and functionality that you would expect from a good PDF viewer.

是的,您可以使用 a 轻松渲染 PDF,UIWebView但这无法提供您期望从一个好的 PDF 查看器获得的性能和功能。

You can draw a PDF page to a CALayeror to a UIImage. Apple even have sample code to show how draw a large PDF in a Zoomable UIScrollview

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

But the same issues keep cropping up.

但同样的问题不断出现。

UIImage Method:

UIImage 方法:

  1. PDF's in a UIImagedon't optically scale as well as a Layer approach.
  2. The CPU and memory hit on generating the UIImagesfrom a PDFcontextlimits/prevents using it to create a real-time render of new zoom-levels.
  1. PDF 的UIImage光学缩放和图层方法不同。
  2. CPU和内存击中产生UIImagesPDFcontext使用它来创建一个实时渲染的新的放大级别限制/防止。

CATiledLayer Method:

CATiledLayer 方法:

  1. Theres a significant Overhead (time) drawing a full PDF page to a CALayer: individual tiles can be seen rendering (even with a tileSize tweak)
  2. CALayerscant be prepared ahead of time (rendered off-screen).
  1. 将完整的 PDF 页面绘制到一个显着的开销(时间)CALayer:可以看到单个图块的渲染(即使使用 tileSize 调整)
  2. CALayers不能提前准备(在屏幕外渲染)。

Generally PDF viewers are pretty heavy on memory too. Even monitor the memory usage of apple's zoomable PDF example.

通常,PDF 查看器的内存也很重。甚至可以监控苹果的可缩放 PDF 示例的内存使用情况。

In my current project, I'm developing a PDF viewer and am rendering a UIImageof a page in a separate thread (issues here too!) and presenting it while the scale is x1. CATiledLayerrendering kicks in once the scale is >1. iBooks takes a similar double take approach as if you scroll the pages you can see a lower res version of the page for just less than a second before a crisp version appears.

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

Im rendering 2 pages each side of the page in focus so that the PDF image is ready to mask the layer before it starts drawing.Pages are destroyed again when they are +2 pages away from the focused page.

我在焦点页面的每一侧渲染 2 个页面,以便 PDF 图像准备好在开始绘制之前屏蔽图层。当页面离焦点页面 +2 页时,页面会再次被销毁。

Does anyone have any insights, no matter how small or obvious to improve the performance/ memory handling of Drawing PDF's? or any other issues discussed here?

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

EDIT:Some Tips (Credit- Luke Mcneice,VdesmedT,Matt Gallagher,Johann):

编辑:一些提示(图片来源- Luke Mcneice、VdesmedT、Matt Gallagher、Johann):

  • Save any media to disk when you can.

  • Use larger tileSizes if rendering on TiledLayers

  • init frequently used arrays with placeholder objects, alternitively another design approach is this one

  • Note that images will render faster than a CGPDFPageRef

  • Use NSOperationsor GCD & Blocksto prepare pages ahead of time.

  • call CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);before CGContextDrawPDFPageto reduce memory usage while drawing

  • init'ing your NSOperationswith a docRef is a bad idea (memory), wrap the docRef into a singleton.

  • Cancel needless NSOperationsWhen you can, especially if they will be using memory, beware of leaving contexts open though!

  • Recycle page objects and destroy unused views

  • Close any open Contexts as soon as you don't need them

  • on receiving memory warnings release and reload the DocRef and any page Caches

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

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

  • 用占位符对象初始化经常使用的数组,或者另一种设计方法是这个

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

  • 使用NSOperations或 GCD & Blocks提前准备页面。

  • 在绘制时调用CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);beforeCGContextDrawPDFPage以减少内存使用

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

  • 取消不必要的NSOperations当你可以的时候,特别是如果他们将使用内存,但要注意让上下文保持打开状态!

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

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

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

Other PDF Features:

其他PDF功能:

Documentation

文档

Example projects

示例项目

采纳答案by VdesmedT

I have build such kind of application using approximatively the same approach except :

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

  • I cache the generated image on the disk and always generate two to three images in advance in a separate thread.
  • I don't overlay with a UIImagebut instead draw the image in the layer when zooming is 1. Those tiles will be released automatically when memory warnings are issued.
  • 我将生成的图像缓存在磁盘上,并且总是在单独的线程中预先生成两到三个图像。
  • UIImage当缩放为 1 时,我不使用 a 覆盖而是在图层中绘制图像。当发出内存警告时,这些图块将自动释放。

Whenever the user start zooming, I acquire the CGPDFPageand render it using the appropriate CTM. The code in - (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) contextis like :

每当用户开始缩放时,我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);
    }
}

issue is the object containg the CGPDFDocumentRef. I synchronize the part where I access the pdfDocproperty because I release it and recreate it when receiving memoryWarnings. It seems that the CGPDFDocumentRefobject do some internal caching that I did not find how to get rid of.

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

回答by Joshua J. McKinnon

For a simple and effective PDF viewer, when you require only limited functionality, you can now (iOS 4.0+) use the QuickLook framework:

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

First, you need to link against QuickLook.frameworkand #import <QuickLook/QuickLook.h>;

首先,您需要链接到QuickLook.framework#import <QuickLook/QuickLook.h>;

Afterwards, in either viewDidLoador any of the lazy initialization methods:

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

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

回答by Tamás Sengel

Since iOS 11, you can use the native framework called PDFKitfor displaying and manipulating PDFs.

iOS 11 开始,您可以使用名为PDFKit的本机框架来显示和操作 PDF。

After importing PDFKit, you should initialize a PDFViewwith a local or a remote URL and display it in your view.

导入 PDFKit 后,您应该PDFView使用本地或远程 URL初始化 a并将其显示在您的视图中。

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

Read more about PDFKit in the Apple Developer documentation.

在 Apple 开发人员文档中阅读有关 PDFKit 的更多信息。

回答by Kuldeep singh

 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);
     } }