22

I have read the Apple PDF documentation with Quartz.

But I do not know how to generate the thumbnails from a PDF document...

Any idea/sample code ?

Max Desiatov
  • 5,087
  • 3
  • 48
  • 56
fvisticot
  • 7,936
  • 14
  • 49
  • 79

6 Answers6

29

Here is my sample code.

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);
alones
  • 2,848
  • 2
  • 27
  • 30
  • 2
    Welcome : Plz select my answer :). For your informatino, you can encounter crash if you try to make all thunmbnails of a PDF over 300dpi which has tens of pages on iPad. – alones Jan 24 '11 at 04:12
  • this gives last page image of thumbnail , i removed the for loop , and i got first page of pdf , anyways ur answer helped me a lot. – Logic Nov 24 '16 at 19:27
25

From iOS 11 you can use PDFKit.

import PDFKit

func generatePdfThumbnail(of thumbnailSize: CGSize , for documentUrl: URL, atPage pageIndex: Int) -> UIImage? {
    let pdfDocument = PDFDocument(url: documentUrl)
    let pdfDocumentPage = pdfDocument?.page(at: pageIndex)
    return pdfDocumentPage?.thumbnail(of: thumbnailSize, for: PDFDisplayBox.trimBox)
}

Call it:

let thumbnailSize = CGSize(width: 100, height: 100)

let thumbnail = generatePdfThumbnail(of: thumbnailSize, for: url, atPage: 0)
Sayooj
  • 375
  • 3
  • 13
SuperGlenn
  • 572
  • 6
  • 9
20

Starting with iOS 11 where PDFKit framework became available, you can get a thumbnail as simply as:

import PDFKit

func pdfThumbnail(url: URL, width: CGFloat = 240) -> UIImage? {
  guard let data = try? Data(contentsOf: url),
  let page = PDFDocument(data: data)?.page(at: 0) else {
    return nil
  }

  let pageSize = page.bounds(for: .mediaBox)
  let pdfScale = width / pageSize.width

  // Apply if you're displaying the thumbnail on screen
  let scale = UIScreen.main.scale * pdfScale
  let screenSize = CGSize(width: pageSize.width * scale, 
                          height: pageSize.height * scale)

  return page.thumbnail(of: screenSize, for: .mediaBox)
} 
Max Desiatov
  • 5,087
  • 3
  • 48
  • 56
  • I use this code to load both local and remote location pdfs thumbnails working fine but it takes minimum 5sec to navigate the screen – Sai kumar Reddy Aug 09 '19 at 06:58
  • any help @Max Desiatov – Sai kumar Reddy Aug 12 '19 at 05:53
  • it would be very hard to say why that's the case without knowing more details. What is the size of the PDF? Is this reproducible on all iOS versions and devices or only simulators? Which version of Xcode do you use? Even if a PDF is small, it still could be a complex one that takes long to render, so maybe attaching that PDF would work best for diagnosing the issue – Max Desiatov Oct 03 '19 at 16:37
  • I'm using Xcode 10.3 swift4.2 and pdf file size is less than 3mb and it is reproducible on all ios devices and simulators also @Max Desiatov – Sai kumar Reddy Oct 10 '19 at 07:01
  • Would you be able to attach the PDF so that anyone else could reproduce it? – Max Desiatov Oct 10 '19 at 11:20
  • You can use any random PDFs not only for a particular size or type of pdfs – Sai kumar Reddy Oct 11 '19 at 04:56
3

In the meantime I have created a library which does support PDF, Images and Videos for creating thumbnails. Maybe someone else can use it:

https://github.com/prine/ROThumbnailGenerator

Solution in Swift. Just pass the NSURL to the PDF and the page number you want to retrieve as UIImage:

func getThumbnail(url:NSURL, pageNumber:Int) -> UIImage {

    var pdf:CGPDFDocumentRef = CGPDFDocumentCreateWithURL(url as CFURLRef);

    var firstPage = CGPDFDocumentGetPage(pdf, pageNumber)

    // Change the width of the thumbnail here
    var width:CGFloat = 240.0;

    var pageRect:CGRect = CGPDFPageGetBoxRect(firstPage, kCGPDFMediaBox);
    var pdfScale:CGFloat = width/pageRect.size.width;
    pageRect.size = CGSizeMake(pageRect.size.width*pdfScale, pageRect.size.height*pdfScale);
    pageRect.origin = CGPointZero;

    UIGraphicsBeginImageContext(pageRect.size);

    var context:CGContextRef = UIGraphicsGetCurrentContext();

    // White BG
    CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
    CGContextFillRect(context,pageRect);

    CGContextSaveGState(context);

    // ***********
    // Next 3 lines makes the rotations so that the page look in the right direction
    // ***********
    CGContextTranslateCTM(context, 0.0, pageRect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(firstPage, kCGPDFMediaBox, pageRect, 0, true));

    CGContextDrawPDFPage(context, firstPage);
    CGContextRestoreGState(context);

    var thm:UIImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();
    return thm;
}
Prine
  • 12,192
  • 8
  • 40
  • 59
1

For objective-c

CGRect pageSize = [[self.pdfView.document pageAtIndex:0] boundsForBox:kPDFDisplayBoxMediaBox];
CGFloat pdfScale = [UIScreen mainScreen].bounds.size.width /pageSize.size.width;
CGFloat scale = [UIScreen mainScreen].scale * pdfScale;
UIImage thumbnailImage = [[self.pdfView.document pageAtIndex:0] thumbnailOfSize:CGSizeMake(pageSize.size.width *scale, pageSize.size.height *scale) forBox:kPDFDisplayBoxMediaBox];
Giang
  • 2,384
  • 2
  • 25
  • 26
0

Swift 4 Version of Prine's Answer

func thumbnailFromPdf(withUrl url:URL, pageNumber:Int, width: CGFloat = 240) -> UIImage? {
    guard let pdf = CGPDFDocument(url as CFURL),
        let page = pdf.page(at: pageNumber)
        else {
            return nil
    }

    var pageRect = page.getBoxRect(.mediaBox)
    let pdfScale = width / pageRect.size.width
    pageRect.size = CGSize(width: pageRect.size.width*pdfScale, height: pageRect.size.height*pdfScale)
    pageRect.origin = .zero

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

    // White BG
    context.setFillColor(UIColor.white.cgColor)
    context.fill(pageRect)
    context.saveGState()

    // Next 3 lines makes the rotations so that the page look in the right direction
    context.translateBy(x: 0.0, y: pageRect.size.height)
    context.scaleBy(x: 1.0, y: -1.0)
    context.concatenate(page.getDrawingTransform(.mediaBox, rect: pageRect, rotate: 0, preserveAspectRatio: true))

    context.drawPDFPage(page)
    context.restoreGState()

    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}
Max Desiatov
  • 5,087
  • 3
  • 48
  • 56
heyfrank
  • 5,291
  • 3
  • 32
  • 46