iEamin
iEamin

Reputation: 279

iOS - Generating PDF from UIView by rendering loses quality

I've used the following methods to generate a PDF from a UIView. All of them create the PDF but loses quality:

Method 1:

@implementation UIView(PDFWritingAdditions)

- (void)renderInPDFFile:(NSString*)path
{
    CGRect mediaBox = self.bounds;
    CGContextRef ctx = CGPDFContextCreateWithURL((__bridge_retained CFURLRef)[NSURL fileURLWithPath:path], &mediaBox, NULL);
    CGPDFPageRef page;
    CGContextDrawPDFPage(ctx, page);

    CGPDFContextBeginPage(ctx, NULL);

    // Also tried following commented lines but no luck
    //    CGContextSetFlatness(ctx, 0.1);
    //    CGContextSetAllowsAntialiasing(ctx, YES);
    //    CGContextSetAllowsFontSmoothing(ctx, YES);
    //    CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
    CGContextScaleCTM(ctx, 1, -1);
    CGContextTranslateCTM(ctx, 0, -mediaBox.size.height);
    [self.layer renderInContext:ctx];
    CGPDFContextEndPage(ctx);
    CFRelease(ctx);
}

@end

Method 2:

- (void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
    // Creates a mutable data object for updating with binary data, like a byte array
    NSMutableData *pdfData = [NSMutableData data];

    // Points the pdf converter to the mutable data object and to the UIView to be converted
    UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
    UIGraphicsBeginPDFPage();
    CGContextRef pdfContext = UIGraphicsGetCurrentContext();


    // draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData

    [aView.layer renderInContext:pdfContext];

    // remove PDF rendering context
    UIGraphicsEndPDFContext();

    // Retrieves the document directories from the iOS device
    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];

    // instructs the mutable data object to write its context to a file on disk
    [pdfData writeToFile:documentDirectoryFilename atomically:YES];
    NSLog(@"documentDirectoryFileName: %@",documentDirectoryFilename);
}

I think I may be missing some settings for the pdf context, not sure. Also I've create png image from UIView with following method which is exact same quality:

- (UIImage *)imageFromView:(UIView *)view
{
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];

    UIImage * img = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();
    // Retrieves the document directories from the iOS device
    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:@"pdf.png"];

    // instructs the mutable data object to write its context to a file on disk
    [UIImagePNGRepresentation(img) writeToFile:documentDirectoryFilename atomically:YES];
    NSLog(@"documentDirectoryFileName: %@",documentDirectoryFilename);
    return img;
}

Can someone see what I'm doing wrong? Thanks.

Upvotes: 8

Views: 7600

Answers (3)

nielsbot
nielsbot

Reputation: 16022

edit ok, it's not exactly the same issue, but I think the solution will be similar

I think this is a dupe of High quality UIImage from PDF

See my answer there:

https://stackoverflow.com/a/14152516/210171

When you ask the PDF for it's page size, you're getting a width/height for 72 PPI. You might try creating a context that's scaled up using a scale transform. For example, if you wanted to render at 300 dpi, add a scale transform to scale by 300.0/72.0.

If you export as TIFF, you will be able to encapsulate the final PPI (300) of the generated image.

Upvotes: 0

ikuramedia
ikuramedia

Reputation: 6058

Looking at your example images, I'm not sure your expectations are right... Your Preview window is partially zoomed in, whereas you're drawing a Bitmap image into your PDF - so you'll see 'jagged edges' when you zoom in. If you view actual size (from the view menu in Preview, or Cmd-0) then you should see a more or less identical image to your screenshot.

However, what you really want to do is not just render your bitmap UIView into the PDF Context, but rather do the CoreGraphics drawing and layout directly into the PDF Context instead. Check out this tutorial http://www.raywenderlich.com/6818/how-to-create-a-pdf-with-quartz-2d-in-ios-5-tutorial-part-2 for an example. This will ensure that your text and lines remain crisp when zoomed in, and ensure a quality product if printed on paper. Quartz2D vector graphics is your friend here, as you'll never get a high quality with a bitmap, at least not without creating a huge PDF file...

Upvotes: 0

Gordon Childs
Gordon Childs

Reputation: 36072

Is this using a retina display? It could be a contentsScale thing. Have a look at this SO post

EDIT Ok, now that you've posted before & after pics, I see that what you want the invoice UIView to be rendered as nice, high quality, printable vector data in your PDF.

I'm pretty sure that [CALayer renderInContext] always produces raster data, so you're probably better off constructing the PDF manually. And if you're talking about tax invoices, do you really want the layout to change with the next iOS update?

However, you could look at this answer, which reluctantly suggests using the UIView as a template system for your PDF layout.

Upvotes: 2

Related Questions