8
\$\begingroup\$

This is the first PDF class I have made for the iOS using code from Apple's samples. There could easily be stuff here wrong that I am missing.

I intend this to get images out of for a layer, for example:

image.contents = (id)[self.test imageForPage: 3 size: CGSizeMake(4*dpc, 2*dpc)].CGImage;

Here is this hopefully decent code. Any suggestions are welcome.

@interface PDFDocument : NSObject {
CGPDFDocumentRef pdfFile;
}

- (id) initWithURL: (NSURL*) url;
- (UIImage*) imageForPage: (size_t) pageno size: (CGSize) size;
- (CGSize) sizeOfPage: (size_t) pageno;

@end

@implementation PDFDocument

- (id) initWithURL: (NSURL*) url {
    if ([super init])
    {
        // Open PDF
        CGPDFDocumentRef doc = CGPDFDocumentCreateWithURL((CFURLRef)url);

        if (doc == NULL)
            @throw @"PDF File does not exist.";

        pdfFile = doc;

    }
    return self;
}

- (UIImage*) imageForPage: (size_t) pageno size: (CGSize) size {
    if (pdfFile) {
        // Get First Page
        CGPDFPageRef page = CGPDFDocumentGetPage(pdfFile, pageno);

        if (page == NULL)
            @throw @"Page does not exist.";

        // Get Page Size
        CGRect cropBox = CGPDFPageGetBoxRect(page, kCGPDFCropBox);

        // Start Drawing Context to Render PDF
        UIGraphicsBeginImageContext(size);

        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);

        CGRect u = { {0, 0}, size};

        CGContextFillRect(context, u);
        CGContextSetAllowsAntialiasing(context, NO);

        CGContextSaveGState(context);

        // Scale PDF
        CGContextScaleCTM(context, size.width / cropBox.size.width, size.height / cropBox.size.height);

        // Flip Context to render PDF correctly
        CGContextTranslateCTM(context, 0.0, cropBox.size.height);
        CGContextScaleCTM(context, 1.0, -1.0);

        CGContextDrawPDFPage(context, page);
        CGContextRestoreGState(context);

        UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); // must retain?

        UIGraphicsEndImageContext();

        return img;

    }
    return nil;
}

- (CGSize) sizeOfPage: (size_t) pageno {
    if (pdfFile) {
        CGPDFPageRef page = CGPDFDocumentGetPage(pdfFile, pageno);

        if (page == NULL)
            @throw @"Page does not exist";

        CGRect cropBox = CGPDFPageGetBoxRect(page, kCGPDFCropBox);

        return cropBox.size;
    }

    @throw @"No document loaded.";
}

@end
\$\endgroup\$
3
  • 1
    \$\begingroup\$ I have encountered PDF files where the text is written in landscape but the page width and height will tell you that the page is in portrait. When opened in a PDF viewer, the file will be in landscape. I had to add special handling for files like those. Unfortunately, I don't have my code with me right now so I can't share, but I believe you're missing that part. \$\endgroup\$
    – Altealice
    May 31, 2011 at 5:35
  • 1
    \$\begingroup\$ Something like this? ipdfdev.com/2011/03/23/… \$\endgroup\$ May 31, 2011 at 13:35
  • \$\begingroup\$ Yeah, something like that. But I remember mine to be much simpler, like ~8 additional lines only. \$\endgroup\$
    – Altealice
    Jun 1, 2011 at 13:29

1 Answer 1

7
\$\begingroup\$

One immediate change I'd make is how you're handling errors. While try-catch blocks are extremely common in other programming languages, I actually don't see them all that very often in Objective-C.

That doesn't mean we won't have errors. It just means error handling is typically handled differently in my experience with Objective-C. Just take a look at some of the Foundation methods as example:

stringWithContentsOfFile:encoding:error:

You can send nil as the argument for the error, but otherwise, you send the method an NSError object, and when the method returns, if there was an error, you can check the object to find out what it was. In the case of an init method, if an error occurred, you'd also want to return nil as well as loading the error description in the passed error object.


For completeness, it seems like it might be a good idea to create a corresponding PDFPage class. And instead of imageForPage:size: being called over and over to create pages, the end user would create instances of PDFPage. The PDFDocument in turn isn't much more than an array of PDFPages with some logic for adding a single page, array of pages, remove pages, from front, back, specific index, etc.

\$\endgroup\$
1
  • \$\begingroup\$ I never liked that kind of error control, although now I would never use exceptions like that either. \$\endgroup\$ Feb 21, 2014 at 6:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.