7

I am new to Objective-c iPhone programming. I have an application in which I display a PDF in my UIWebView successfully, but now I want to create a thumbnail of my PDF. My PDF is stored in my resource folder.

So please give me code for how I can show a thumbnail of my PDF. My code is for displaying PDF is which is taken in button function:

-(void)show:(id)sender {

    pdfView.autoresizesSubviews = NO;
    pdfView.scalesPageToFit=YES;
    pdfView.autoresizingMask=(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
    [pdfView setDelegate:self];

    NSString *path = [[NSBundle mainBundle] pathForResource:@"com" ofType:@"pdf"];
    NSLog(@"Path of res is%@",path);
    NSURL *url = [NSURL fileURLWithPath:path];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [pdfView loadRequest:request];
}
Ingve
  • 1,086
  • 2
  • 20
  • 39
Yogesh
  • 127
  • 1
  • 3
  • 10

5 Answers5

24

try the following method:

- (UIImage *)imageFromPDFWithDocumentRef:(CGPDFDocumentRef)documentRef {
    CGPDFPageRef pageRef = CGPDFDocumentGetPage(documentRef, 1);
    CGRect pageRect = CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox);

    UIGraphicsBeginImageContext(pageRect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, CGRectGetMinX(pageRect),CGRectGetMaxY(pageRect));
    CGContextScaleCTM(context, 1, -1);  
    CGContextTranslateCTM(context, -(pageRect.origin.x), -(pageRect.origin.y));
    CGContextDrawPDFPage(context, pageRef);

    UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return finalImage;
} 
reflog
  • 7,587
  • 1
  • 42
  • 47
  • thanks it run and give me single image of pdf but i want to display thumbnail of my pdf if my pdf have 7 page then it give me 7 thumbnail of my pdf thats why i write following code – Yogesh Apr 14 '11 at 08:38
  • Change the above function to this: - (UIImage *)imageFromPDFWithDocumentRef:(CGPDFDocumentRef)documentRef and PageNumber:(int)pageNumber { CGPDFPageRef pageRef = CGPDFDocumentGetPage(documentRef, pageNumber); then call it in a loop. – Luke Mcneice Apr 15 '11 at 08:48
  • 1
    Do you have any idea how much time this saved me? I'm going to print your picture and make out with it. – d512 Feb 06 '14 at 22:22
  • This code is making problem in iOS8..can any one help? – Tunvir Rahman Tusher Sep 16 '14 at 12:54
4

Swift 3

(Thanks to Prine for Swift 2!)

func getPdfThumb(url:NSURL, pageBase1:Int) -> UIImage? {
    guard let document = CGPDFDocument(url as CFURL) else { return nil }
    guard let firstPage = document.page(at: pageBase1) else { return nil }

    let width:CGFloat = 240.0;

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

    UIGraphicsBeginImageContext(pageRect.size)

    let context:CGContext = UIGraphicsGetCurrentContext()!

    // White background
    context.setFillColor(red: 1.0,green: 1.0,blue: 1.0,alpha: 1.0)
    context.fill(pageRect)

    context.saveGState()

    // Handle rotation
    context.translateBy(x: 0.0, y: pageRect.size.height)
    context.scaleBy(x: 1.0, y: -1.0)
    context.concatenate(firstPage.getDrawingTransform(.mediaBox, rect: pageRect, rotate: 0, preserveAspectRatio: true))

    context.drawPDFPage(firstPage)
    context.restoreGState()

    let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()!

    UIGraphicsEndImageContext()
    return image
}
Alexandre G
  • 1,655
  • 20
  • 30
2

I come up with a solution that uses CoreGraphics and Swift 3.0. It's highly inspired by the one presented Alexandre. In my opinion my approach results in more 'Swifty' code. Also, my solution fixes a couple of problems with scaling and orientation of resulting image.

Note that my code uses AVMakeRect(aspectRatio:, insideRect:) and requires import of AVFoundation.

//pages numbering starts from 1.
func generate(size: CGSize, page: Int) -> UIImage? {
    guard let document = CGPDFDocument(url as CFURL), let page = document.page(at: page) else { return nil }

    let originalPageRect: CGRect = page.getBoxRect(.mediaBox)
    var targetPageRect = AVMakeRect(aspectRatio: originalPageRect.size, insideRect: CGRect(origin: CGPoint.zero, size: size))
    targetPageRect.origin = CGPoint.zero

    UIGraphicsBeginImageContextWithOptions(targetPageRect.size, true, 0)
    defer { UIGraphicsEndImageContext() }
    guard let context = UIGraphicsGetCurrentContext() else { return nil }

    context.setFillColor(gray: 1.0, alpha: 1.0)
    context.fill(targetPageRect)

    context.saveGState()
    context.translateBy(x: 0.0, y: targetPageRect.height)
    context.scaleBy(x: 1.0, y: -1.0)
    context.concatenate(page.getDrawingTransform(.mediaBox, rect: targetPageRect, rotate: 0, preserveAspectRatio: true))
    context.drawPDFPage(page)
    context.restoreGState()

    return context.makeImage().flatMap() { UIImage(cgImage: $0, scale: UIScreen.main.scale, orientation: .up) }
}
Rafał Augustyniak
  • 2,011
  • 19
  • 14
2

There are as you may already know 2 ways of rendering PDF's:

  • UIWebView
  • Quartz Rendering

The Other answers at the time of writing this have focused on Quartz. There are a number of good reasons for this mostly performance related but in my opinion using Quartz is worth it. I would recommend reading this thread to get a better idea of the pro's and cons.

There is apparently an excellent newish api for Quartz based pdf rendering here

ofc you could present the pdf via UIWebView and render the thumbs using quartz.

There is also a bit of confussion around thumbs, for people new quartz pdf magic, it might seem that after some searching there are apis that support thumbs abut you should check if the support is for embedded thumbs only, many PDF's don't have these.

Another option is to create the thumbs yourself (using quartz) and there are plenty of examples of this around the net including the two answers above. However If you are targeting iOS 4 or above I would strongly recommend using blocks. (Also graphics contexts are thread safe since 4).

I found a significant performance increase when I generated thumbs with blocks.

What I have done in the past is:

  • Have a ViewController for your thumbs, it has a scrollview that has a content size appropriate for all your pages. Insert Placeholder ImageViews into is if you like.

  • On document load, kick off a thumb generator in the background (see code below)

  • The code below calls a method drawImageView that takes the index of the page, grabs the image from the disk and puts it into the scroll view

  • If your feeling really motivated you can implement a render scope on the thumb scrollView (only rendering the thumbs you need to - something you should be doing for the pdf's anyway)

  • Dont forget to delete the thumbs when your done, unless you want to cache..

#define THUMB_SIZE 100,144

-(void)generateThumbsWithGCD
{

    thumbQueue = dispatch_queue_create("thumbQueue", 0);//thumbQueue = dispatch_queue_t
    NSFileManager *fm = [NSFileManager defaultManager];
    //good idea to check for previous thumb cache with NSFileManager here   

    CGSize thumbSize = CGSizeMake(THUMB_SIZE);
    __block CGPDFPageRef myPageRef;     

    NSString *reqSysVer = @"4.0";   
    NSString *currSysVer = [[UIDevice currentDevice] systemVersion];    

    //need to handle ios versions < 4
    if ([currSysVer compare:reqSysVer options:NSNumericSearch] == NSOrderedAscending) {NSLog(@"UIKIT MULTITHREADING NOT SUPPORTED!");return;}//thread/api saftey

dispatch_async(thumbQueue, ^{

    for (i=1; i<=_maxPages; i++) {      

        //check if worker is valid (class member bool) for cancelations

        myPageRef=[[PDFDocument sharedPDFDocument]getPageData:i];//pdfdocument is a singleton class     

        if(!myPageRef)return;




            NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

            NSString* imageName = [NSString stringWithFormat:@"%@thumb%i.png",documentName,i];  
            NSString* fullPathToFile =  [thumbDocPath stringByAppendingPathComponent:imageName];

            if(![fm fileExistsAtPath:fullPathToFile]){
                //NSLog(@"Not there");
                UIGraphicsBeginImageContext(thumbSize);//thread Safe in iOs4
                CGContextRef context = UIGraphicsGetCurrentContext();//thread Safe in iOs4          
                CGContextTranslateCTM(context, 0, 144);
                CGContextScaleCTM(context, 0.15, -0.15);
                CGContextDrawPDFPage (context, myPageRef);
                UIImage * render = UIGraphicsGetImageFromCurrentImageContext();                     
                UIGraphicsEndImageContext();        
                NSData* imageData= UIImagePNGRepresentation(render);

                if(imageData){
                    NSLog(@"WROTE TO:%@",fullPathToFile);
                    if(![imageData writeToFile:fullPathToFile atomically:NO])NSLog(@"ERROR: Thumb Didnt Save"); //COMMENT OUT TO DISABLE WRITE                  
                }               
            }
            else NSLog(@"Allready There! %@",fullPathToFile);
                    //update progress on thumb viewController if you wish here
            [pool release];
            dispatch_sync(dispatch_get_main_queue(), ^{
            [self drawImageView:i];
        });

    }   
    });
    dispatch_release(thumbQueue);

}

Community
  • 1
  • 1
Luke Mcneice
  • 3,012
  • 4
  • 38
  • 50
  • Nice answer, I see your PDFDocument singleton, how do you handle memory warnings where you need to release the CGPDFDocument in order to flush the cache it creates when accessing pages? – Chris Wagner Jun 24 '11 at 22:24
  • 2
    If I needed to blow away the doc then on a level 2 warning I do simply that but also set a flag that my background tasks check periodically. Your thumbnail generator needs to have logic in it (ie at the start of the loop or at least before it accesses the doc) that can end it generation prematurely if it sees this flag. If before you start a thumb you also check for if it already exists (or do a full check first) then it will not have to redo the thumbs already cached (this is also valid for reopening documents and changing the doc ref half way through thumb operations too) – Luke Mcneice Jun 25 '11 at 09:25
  • apparently UIGraphicsGetCurrentContext isn't thread safe in 5.0 – Luke Mcneice May 15 '12 at 18:18
1

I just rewrote the Objective-C code to Swift. Maybe anyone else can use it:

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

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

    var firstPage = CGPDFDocumentGetPage(pdf, pageNumber)

    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