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

发布于 2024-09-26 07:30:06 字数 5716 浏览 4 评论 0原文

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

是的,您可以使用 UIWebView 非常轻松地呈现 PDF,但这无法提供您所期望的优秀 PDF 查看器的性能和功能。

您可以将 PDF 页面绘制到 CALayer到UIImage。 Apple 甚至有示例代码来展示如何绘制大型 PDF 在可缩放的 UIScrollview

但同样的问题不断出现。

UIImage 方法:

  1. UIImage 中的 PDF 不具有光学效果 规模以及层方法。
  2. CPU 和内存在生成时命中 来自 PDFcontextUIImages 限制/阻止使用它来创建 新缩放级别的实时渲染。

CATiledLayer 方法:

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

一般来说,PDF 查看器对内存的占用也很大。甚至可以监控苹果可缩放 PDF 示例的内存使用情况。

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

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

是否有人对改善绘图 PDF 的性能/内存处理有任何见解,无论多么小或多么明显?或此处讨论的任何其他问题?

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

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

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

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

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

  • 使用 NSOperations 或 GCD & 用于准备前面的页面

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

  • docRef 绘制

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

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

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

  • 在不需要时立即关闭所有打开的上下文

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

其他 PDF 功能:

文档

示例项目

  • Apple/ ZoomingPDF - 缩放,< code>UIScrollView, CATiledLayer
  • vfr/ reader - 缩放、分页, UIScrollView, CATiledView
  • brow/leaves - 分页具有良好的过渡
  • /skim - 看起来的一切(OSX 的 PDF 阅读器/编辑器)

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

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

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

But the same issues keep cropping up.

UIImage Method:

  1. PDF's in a UIImage don't optically
    scale as well as a Layer approach.
  2. The CPU and memory hit on generating
    the UIImages from a PDFcontext
    limits/prevents using it to create a
    real-time render of new zoom-levels.

CATiledLayer Method:

  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. CALayers cant be prepared ahead of
    time (rendered off-screen).

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

In my current project, I'm developing a PDF viewer and am rendering a UIImage of a page in a separate thread (issues here too!) and presenting it while the scale is x1. CATiledLayer rendering 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.

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.

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?

EDIT: Some Tips (Credit- 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 NSOperations or GCD & Blocks to prepare pages ahead
    of time.

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

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

  • Cancel needless NSOperations When 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

Other PDF Features:

Documentation

Example projects

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

乖乖兔^ω^ 2024-10-03 07:30:06

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

  • 我将生成的图像缓存在磁盘上,并且总是在单独的线程中提前生成两到三个图像。
  • 我不使用 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 属性的部分,因为我在收到内存警告时释放了它并重新创建了它。 CGPDFDocumentRef 对象似乎做了一些内部缓存,我没有找到如何摆脱它。

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 UIImage but instead draw the image in the layer when zooming is 1. Those tiles will be released automatically when memory warnings are issued.

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

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 pdfDoc property because I release it and recreate it when receiving memoryWarnings. It seems that the CGPDFDocumentRef object do some internal caching that I did not find how to get rid of.

喜爱皱眉﹌ 2024-10-03 07:30:06

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

首先,您需要链接 QuickLook.framework#进口
;

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

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

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

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

Afterwards, in either viewDidLoad or any of the lazy initialization methods:

QLPreviewController *previewController = [[QLPreviewController alloc] init];
previewController.dataSource = self;
previewController.delegate = self;
previewController.currentPreviewItemIndex = indexPath.row;
[self presentModalViewController:previewController animated:YES];
[previewController release];
贵在坚持 2024-10-03 07:30:06

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

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

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 的更多信息。

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文