Yogesh
Yogesh

Reputation: 127

Creating pdf Thumbnail in iphone

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];
}

Upvotes: 7

Views: 12177

Answers (5)

Rafał Augustyniak
Rafał Augustyniak

Reputation: 2031

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) }
}

Upvotes: 2

Alexandre G
Alexandre G

Reputation: 1693

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
}

Upvotes: 4

Prine
Prine

Reputation: 12528

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;
}

Upvotes: 1

Luke Mcneice
Luke Mcneice

Reputation: 2720

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);

}

Upvotes: 2

reflog
reflog

Reputation: 7645

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;
} 

Upvotes: 24

Related Questions