8

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.

Jeeter
  • 5,887
  • 6
  • 44
  • 67
iEamin
  • 283
  • 4
  • 10

3 Answers3

2

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.

Community
  • 1
  • 1
Rhythmic Fistman
  • 34,352
  • 5
  • 87
  • 159
  • Nope, this happens with normal display. – iEamin Nov 02 '11 at 05:09
  • I've already created the view manually for pdf [since no solution found yet] and used the UIView to get points and color as references [so that if I change anything in the view I don't have to change the code]. But the original image link I've added is generated using "imageFromView" method and also a raster image. So, my point is the "renderInContext" should be able to generate the same quality pdf. Also I tried to create pdf from that image, but no luck . . . same problem. – iEamin Nov 02 '11 at 12:11
  • You need to create the PDF from calls to CGContextFillRect, CTFontDrawGlyphs etc. renderInContext will always create lousy looking screen resolution raster PDFs. – Rhythmic Fistman Nov 02 '11 at 13:19
0

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...

ikuramedia
  • 6,038
  • 3
  • 28
  • 31
0

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.

Community
  • 1
  • 1
nielsbot
  • 15,922
  • 4
  • 48
  • 73