简体   繁体   中英

iOS improve PDF render speed

I'm writing an app that manages documents for a user and (eventually) posts PDF files served out through a web service on the iPhone/iPad. These are almost exclusively scanned PDF files, and they appear to REALLLLLY bog down the iOS device.

The first solution I came up with was to simply host the PDF in a UIWebView. This works really, really well for "generated" PDF files, but NOT for scanned PDF files (I'm guessing the difference here is raster vs. vector?).

My next solution was to implement a UIDocumentInteractionController, which was said to increase snappiness. I can report that it does, indeed, seem to be faster than UIWebView, but it's still unacceptably slow, even on smallish, 2 page PDF files. (On a side note, the "auto-open-in-another-app" feature, along with built-in printing is super slick!)

I've read a post or 2 about the QuickLook framework, and I plan to look into that, but I've also stumbled across a few posts talking about CGPDFDocument classes and such. Those seem to have a finer control over document navigation (a la xPdf), but I have no idea where to start with that. Plus, I'm not even sure if it offers a performance benefit for what I'm doing.

So, first question: what's the fastest way to render scanned PDF files on the iPhone/iPad?

Second question: The scanned PDF files are generated by my company, so I have some control over the PDF generation settings. Does anybody know what settings might improve load speed for image-based PDF files?

Thanks!

(By the way: I've been coding for 19 hours straight today, so if I rambled or didn't make sense, please forgive me: :) )

The fastest solution will be to write your own custom pdf parser and rendering framework using the CGPDFDocument classes. The secret to superfast pdf rendering is using techniques like:

  • Off-screen rendering of full-size pages to images.
  • Only activate CATiledLayer drawing after a certain zoom level. When the user views your pdf page at the default zoom level there's no need to activate the CATiledLayer drawing because it's very expensive. Just display the already off-screen rendered image. When the user starts zooming in you can activate the CATiledLayer.
  • Using a smart algorithm to cache off-screen rendered pdf pages. You can cache the previous and next pages as off-screen rendered images.
  • On fast devices (iPhone 4 or greater and iPad 2 or greater) you can start a background job that will render all pages off-screen and save them to disk.
  • Cache regularly used information about pdf pages like: original rect, rotation, rotated rect etc.
  • You'll use a lot of CGPDFPageRef objects. It's important to know that this will increase memory usage a lot. A little trick is to close and re-open the CGPDFDocumentRef object when you receive a memory warning.

I used all of the above mentioned techniques in PDFTouch SDK for iOS which is a fast pdf rendering framework developed by me!

You may wanna try to reduce image size. Large PDFs are really pushing the iPad/iPhone to its limits. Of course this means that you need to draw/manage the pdf yourself, with Quartz calls.

More speed is possible with proper caching. You can render pages off-screen, and display them without opening the actual pdf - which is much, much faster.

if you get desperate you could re-process the pdf files on your server to turn them into simple image files (associated with the original pdf files) then load those. that way no "pdf" parsing needs to take place. and then you can host on the server or include as part of the app if the volume is low.

basically you'd need a server script that does all the re-processing and sticks the new files into a special folder. maybe you make a database to reference the new files. or maybe you create a directory name for each new file that mirrors the original pdf file name.

By using UIWebView to render the pdf, we will not get proper control over the pdf. like we cannot go to the desired page directly. and we cannot search a particular word and highlight.

its better to use CGPDF classes like CGPDFDocumentRef and CGPDFPageRef to handle the pdf documents properly.

Using these classes we have two options to handle the pdf's. 1. extracting the pdf pages as images individually and display using UIImageView. 2. extract the content of each page using CGPDFPageRef and create pdf for each page at runtime and display on to the webview.(better control control over pdf and neat display)

below is the sample code to extract individual page as an image.

-(UIImage *)getPage : (NSString*) filePath{

    const char *myBuffer           = (const char*)filePath;                        // PDF Path
    CFStringRef urlString          = (CFStringRef)myBuffer;
    CFURLRef url                   = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, urlString, 2, NO);  
    CGPDFDocumentRef myDocumentRef = CGPDFDocumentCreateWithURL(url);

    CGPDFPageRef myPageRef         = CGPDFDocumentGetPage(myDocumentRef, 1);
    CGRect pdfcropBox              = CGPDFPageGetBoxRect(myPageRef,kCGPDFCropBox); //kCGPDFCropBox

    UIGraphicsBeginImageContext      (pdfcropBox.size);
    CGContextRef context           = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM            (context,0,pdfcropBox.size.height);// [self pageRect].origin.x,[self pageRect].origin.y+[self pageRect].size.height); //320);
    // scale 1:1 100%
    CGContextScaleCTM                (context, 1.0, -1.0);
    CGContextSaveGState              (context);
    CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(myPageRef, kCGPDFCropBox, CGRectMake(0, 0, pdfcropBox.size.width,pdfcropBox.size.height), 0, true);  //
    CGContextConcatCTM               (context, pdfTransform);
    CGContextDrawPDFPage             (context, myPageRef);
    CGContextRestoreGState           (context);

    UIImage *resultingImage        = UIGraphicsGetImageFromCurrentImageContext();  
    UIGraphicsEndImageContext        ();
    CGPDFDocumentRelease             (myDocumentRef);

    //CGPDFPageRelease(myPageRef);
        //myPageRef   = nil;
    myDocumentRef = nil;
    urlString     = nil;
    url           = nil;
    return resultingImage;
}

Here are my 2 cents about easy and fast pdf rendering in swift.

SwiftyPDF

  • Paging with system UIPageViewController
  • Zooming with UIScrollView zooming features
  • Renders the PDF fast by converting the page to placeholder image
  • PDF page is scaled and divided into small tiles. Tiles are cached to image files and rendered over the placeholder image (using the CATiledLayer)

Still WIP

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM