适用于 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
Fast and Lean PDF Viewer for iPhone / iPad / iOs - tips and hints?
提问by Luke Mcneice
There has been many Questions recently about drawing PDF's.
最近有很多关于绘制 PDF 的问题。
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.
是的,您可以使用 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 页面绘制到 CALayer或UIImage。Apple 甚至有示例代码来展示如何在 Zoomable UIScrollview 中绘制大型 PDF
But the same issues keep cropping up.
但同样的问题不断出现。
UIImage Method:
UIImage 方法:
- PDF's in a
UIImage
don't optically scale as well as a Layer approach. - The CPU and memory hit on generating
the
UIImages
from aPDFcontext
limits/prevents using it to create a real-time render of new zoom-levels.
- PDF 的
UIImage
光学缩放和图层方法不同。 - CPU和内存击中产生
UIImages
从PDFcontext
使用它来创建一个实时渲染的新的放大级别限制/防止。
CATiledLayer Method:
CATiledLayer 方法:
- Theres a significant Overhead (time)
drawing a full PDF page to a
CALayer
: individual tiles can be seen rendering (even with a tileSize tweak) CALayers
cant be prepared ahead of time (rendered off-screen).
- 将完整的 PDF 页面绘制到一个显着的开销(时间)
CALayer
:可以看到单个图块的渲染(即使使用 tileSize 调整) 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 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.
在我当前的项目中,我正在开发一个 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
NSOperations
or GCD & Blocksto prepare pages ahead of time.call
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
beforeCGContextDrawPDFPage
to reduce memory usage while drawinginit'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
尽可能将任何媒体保存到磁盘。
如果在 TiledLayers 上渲染,请使用更大的 tileSizes
用占位符对象初始化经常使用的数组,或者另一种设计方法是这个
请注意,图像的渲染速度比
CGPDFPageRef
使用
NSOperations
或 GCD & Blocks提前准备页面。在绘制时调用
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
beforeCGContextDrawPDFPage
以减少内存使用用 docRef 初始化你
NSOperations
是一个坏主意(内存),将 docRef 包装成一个单例。取消不必要的
NSOperations
当你可以的时候,特别是如果他们将使用内存,但要注意让上下文保持打开状态!回收页面对象并销毁未使用的视图
一旦你不需要它们就关闭所有打开的上下文
在收到内存警告时释放并重新加载 DocRef 和任何页面缓存
Other PDF Features:
其他PDF功能:
Getting Links inside a PDF(and hereand here)
Getting the target of the link(Getting the page number from the
/Dest
array)
Getting Raw Text(and hereand Hereand here(positioning focused))
Searching(and here) (doesn't work with all PDFs (some just show weird characters, I guess it's an encoding issue but I'm not sure) -Credit BrainFeeder)
CALayer and Off-Screen Rendering- render the next page for fast/smooth display
获取 PDF 中的链接(以及此处和此处)
获取链接的目标(从
/Dest
数组中获取页码)
搜索(和这里)(不适用于所有 PDF(有些只是显示奇怪的字符,我想这是一个编码问题,但我不确定)-Credit BrainFeeder)
CALayer 和 Off-Screen Rendering- 渲染下一页以实现快速/平滑显示
Documentation
文档
- Quartz PDFObjects(Used for meta info, annotations, thumbs)
- Abobe PDF Spec
- Quartz PDFObjects(用于元信息、注释、拇指)
- Abobe PDF 规范
Example projects
示例项目
- Apple/ ZoomingPDF- zooming,
UIScrollView
,CATiledLayer
- vfr/ reader- zooming, paging,
UIScrollView
,CATiledView
- brow/ leaves- paging with nice transitions
- / skim- everything it seems (PDF reader/editor for OSX)
- Apple/ ZoomingPDF- 缩放
UIScrollView
、、CATiledLayer
- vfr/阅读器- 缩放、分页
UIScrollView
、、CATiledView
- 眉毛/叶子- 分页有很好的过渡
- / 脱脂- 看起来的一切(OSX 的 PDF 阅读器/编辑器)
采纳答案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
UIImage
but 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 CGPDFPage
and render it using the appropriate CTM. The code in - (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) context
is 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 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.
问题是包含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.framework
and #import
<QuickLook/QuickLook.h>;
首先,您需要链接到QuickLook.framework
和#import
<QuickLook/QuickLook.h>;
Afterwards, in either viewDidLoad
or 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 PDFView
with 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.
回答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);
} }