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:
- PDF’s in a
UIImagedon’t optically
scale as well as a Layer approach. - The CPU and memory hit on generating
theUIImagesfrom aPDFcontext
limits/prevents using it to create a
real-time render of new zoom-levels.
CATiledLayer Method:
- Theres a significant Overhead (time)
drawing a full PDF page to aCALayer: individual tiles can be seen rendering (even with a tileSize tweak) CALayerscant 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
NSOperationsor GCD & Blocks to prepare pages ahead
of time. -
call
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh); CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);beforeCGContextDrawPDFPageto 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
Other PDF Features:
-
Getting Links inside a PDF (and here and here)
-
Getting the target of the link (Getting the page number from the
/Destarray)
-
Getting Raw Text (and here and Here and 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
Documentation
- Quartz PDFObjects (Used for meta info, annotations, thumbs)
- Abobe PDF Spec
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)
I have build such kind of application using approximatively the same approach except :
UIImagebut 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
CGPDFPageand render it using the appropriate CTM. The code in- (void)drawLayer: (CALayer*)layer inContext: (CGContextRef) contextis like :issue is the object containg the
CGPDFDocumentRef. I synchronize the part where I access thepdfDocproperty because I release it and recreate it when receiving memoryWarnings. It seems that theCGPDFDocumentRefobject do some internal caching that I did not find how to get rid of.