开发者

How can I programmatically generate a thumbnail of a PDF with iOS?

We're displaying PDF content using UIWebViews at the moment. Ideally I would like to be able to display thumbnails in the UITableView without loading many different UIWebViews concurrently... they're slow enough loading one document - neve开发者_开发知识库r mind 10+!

How do I go about doing this?

I've thought about screen capturing a document loaded using UIDocumentInteractionController or UIWebView but this means they'd all have to be thumbnailed before displaying the table.


Apple supply a whole bunch of methods down at the CoreGraphics level for drawing PDF content directly. As far as I'm aware, none of it is neatly packaged up at the UIKit level, so it may not be a good fit for your project at this point, especially if you're not as comfortable down at the C level. However, the relevant function is CGContextDrawPDFPage; per the normal CoreGraphics way of things there are other methods to create a PDF reference from a data source and then to get a page reference from a PDF. You'll then need to deal with scaling and translating to the view you want, and be warned that you'll need to perform a horizontal flip because PDFs (and OS X) use the lower left as the origin whereas iOS uses the top left. Example code, off the top of my head:

UIGraphicsBeginImageContext(thumbnailSize);
CGPDFDocumentRef pdfRef = CGPDFDocumentCreateWithProvider( (CGDataProviderRef)instanceOfNSDataWithPDFInside );
CGPDFPageRef pageRef = CGPDFDocumentGetPage(pdfRef, 1); // get the first page

CGContextRef contextRef = UIGraphicsGetCurrentContext();

// ignore issues of transforming here; depends on exactly what you want and
// involves a whole bunch of normal CoreGraphics stuff that is nothing to do
// with PDFs
CGContextDrawPDFPage(contextRef, pageRef);

UIImage *imageToReturn = UIGraphicsGetImageFromCurrentImageContext();

// clean up
UIGraphicsEndImageContext();
CGPDFDocumentRelease(pdfRef);

return imageToReturn;

At a guess, you'll probably want to use CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox) to get the page's crop box and then work out how to scale/move that to fit your image size.

Probably easier for your purposes is the -renderInContext: method on CALayer (see QA 1703) — the old means of getting a screenshot was UIGetScreenImage, but that was never really official API and was seemingly temporarily allowed only because of the accidental approval of RedLaser. With the code in the QA you can rig yourself up to get a UIImage from any other view without that view having to be on screen. Which possibly resolves some of your issue with screen capturing? Though it means you can support OS 4.x only.

In either case, PDFs just don't draw that quickly. You probably need to populate the table then draw the thumbnails on a background thread, pushing them upwards when available. You can't actually use UIKit objects safely on background threads but all the CoreGraphics stuff is safe.


Here is sample code considering transformation. :)

NSURL* pdfFileUrl = [NSURL fileURLWithPath:finalPath];
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfFileUrl);
CGPDFPageRef page;

CGRect aRect = CGRectMake(0, 0, 70, 100); // thumbnail size
UIGraphicsBeginImageContext(aRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
UIImage* thumbnailImage;


NSUInteger totalNum = CGPDFDocumentGetNumberOfPages(pdf);

for(int i = 0; i < totalNum; i++ ) {


    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0.0, aRect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    CGContextSetGrayFillColor(context, 1.0, 1.0);
    CGContextFillRect(context, aRect);


    // Grab the first PDF page
    page = CGPDFDocumentGetPage(pdf, i + 1);
    CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFMediaBox, aRect, 0, true);
    // And apply the transform.
    CGContextConcatCTM(context, pdfTransform);

    CGContextDrawPDFPage(context, page);

    // Create the new UIImage from the context
    thumbnailImage = UIGraphicsGetImageFromCurrentImageContext();

    //Use thumbnailImage (e.g. drawing, saving it to a file, etc)

    CGContextRestoreGState(context);

}


UIGraphicsEndImageContext();    
CGPDFDocumentRelease(pdf);


Here is a swift 3 method for generating a UIImage thumbnail for a pdf page

static func getThumbnailForPDF(_ urlString:String, pageNumber:Int) -> UIImage? {
    let bundle = Bundle.main
    let path = bundle.path(forResource: urlString, ofType: "pdf")
    let pdfURL = URL(fileURLWithPath: path!)

    let pdf = CGPDFDocument(pdfURL as CFURL )!
    let page = pdf.page(at: pageNumber)!
    let rect = CGRect(x: 0, y: 0, width: 100.0, height: 70.0) //Image size here

    UIGraphicsBeginImageContext(rect.size)
    let context = UIGraphicsGetCurrentContext()!

    context.saveGState()
    context.translateBy(x: 0, y: rect.height)
    context.scaleBy(x: 1.0, y: -1.0)
    context.setFillColor(gray: 1.0, alpha: 1.0)
    context.fill(rect)


    let pdfTransform = page.getDrawingTransform(CGPDFBox.mediaBox, rect: rect, rotate: 0, preserveAspectRatio: true)
    context.concatenate(pdfTransform)
    context.drawPDFPage(page)

    let thumbImage = UIGraphicsGetImageFromCurrentImageContext()
    context.restoreGState()

    UIGraphicsEndImageContext()

    return thumbImage
}


If you use PDFKit, there is a method for PDFPage called thumbnail. It is available in iOS11 and above and does the job in just a few lines of code. Here is a simple method for generating a thumbnail of a given page in a PDFDocument.

func makeThumbnail(pdfDocument: PDFDocument?, page: Int) -> UIImage? {
        return pdfDocument?.page(at: page)?.thumbnail(of: CGSize(width: 40, height: 40), for: .artBox)
    }

Unfortunately it misses official documentation (see, https://developer.apple.com/documentation/pdfkit/pdfpage/2869834-thumbnail). However, there are some comments accessible in the code for PDFPage.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜