2

I'm creating PDF files from UIViews using full screen iPad with Retina Display 2048x1536 resolution (2x scale). The resulting files are very large, I'm getting 6-10 mb files for 2 pages of fairly simple views. Ideally I would get to 4-5 pages per document, but the current approach would make the PDF size prohibitively large.

Is there a way to make the PDFs created this way smaller? Like creating a screenshot of view, converting it to JPG, then writing that to PDF context? Or is there some quality option that I'm missing?

Here's the code to create a PDF from a single view:

+(NSMutableData *)createPDFDatafromUIView:(UIView*)aView 
{
    // 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();

    return pdfData;
}

+(NSString*)createPDFfromUIView:(UIView*)aView saveToFilepath:(NSString*)filepath
{
    // Creates a mutable data object for updating with binary data, like a byte array
    NSMutableData *pdfData = [AppGraphics createPDFDatafromUIView:aView];


    // instructs the mutable data object to write its context to a file on disk
    [pdfData writeToFile:filepath atomically:YES];
    DLog(@"saving PDF to: %@",filepath);
    DLog(@"file exists: %@",[[NSFileManager defaultManager] fileExistsAtPath:filepath]?@"YES":@"NO");
    return filepath;
}

Here's what I'm using to join multiple PDFs together

+ (NSString *)joinPDF:(NSArray *)listOfPaths saveToPath:(NSString*)path
{


    CFURLRef pdfURLOutput = (  CFURLRef)CFBridgingRetain([NSURL fileURLWithPath:path]);

    NSInteger numberOfPages = 0;
    // Create the output context
    CGContextRef writeContext = CGPDFContextCreateWithURL(pdfURLOutput, NULL, NULL);

    for (NSString *source in listOfPaths) {
        CFURLRef pdfURL = (  CFURLRef)CFBridgingRetain([[NSURL alloc] initFileURLWithPath:source]);

        //file ref
        CGPDFDocumentRef pdfRef = CGPDFDocumentCreateWithURL((CFURLRef) pdfURL);
        numberOfPages = CGPDFDocumentGetNumberOfPages(pdfRef);

        // Loop variables
        CGPDFPageRef page;
        CGRect mediaBox;

        // Read the first PDF and generate the output pages
        DLog(@"GENERATING PAGES FROM PDF 1 (%@)...", source);
        for (int i=1; i<=numberOfPages; i++) {
            page = CGPDFDocumentGetPage(pdfRef, i);
            mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
            CGContextBeginPage(writeContext, &mediaBox);
            CGContextDrawPDFPage(writeContext, page);
            CGContextEndPage(writeContext);
        }

        CGPDFDocumentRelease(pdfRef);
        CFRelease(pdfURL);
    }
    CFRelease(pdfURLOutput);

    // Finalize the output file
    CGPDFContextClose(writeContext);
    CGContextRelease(writeContext);

    return path;
}
Alex Stone
  • 46,408
  • 55
  • 231
  • 407
  • 1
    [This is what you want](http://stackoverflow.com/questions/4334233/how-to-capture-uiview-to-uiimage-without-loss-of-quality-on-retina-display), the other way around. Pass `1.0` as the scale property. –  Apr 21 '13 at 04:38
  • Anything drawn with 1.0 scale would look blurry on retina devices, making text within PDF unreadable :( – Alex Stone Apr 21 '13 at 14:49

1 Answers1

3

The solution was indeed to render the image as JPG within the PDF context. The resulting compressed PDF size is smaller - around 300-400kb per page!

//aView is used for its bounds property
//screenshot is the screenshot of the view within its bounds
//compression quality 0.55 is 380kb/page 0.66 is 450kb/page
+(NSString*)createPDFDataFromUIView:(UIView*)aView withScreenshot:(UIImage*)screenshot compressionQuality:(float)compression writeToFile:(NSString*)filepath
{

    //prepare JPEG data for rendering into PDF content
    NSData *jpegData = UIImageJPEGRepresentation(screenshot, compression);
    CGDataProviderRef dp = CGDataProviderCreateWithCFData((__bridge CFDataRef)jpegData);
    CGImageRef cgImage = CGImageCreateWithJPEGDataProvider(dp, NULL, true, kCGRenderingIntentDefault);


    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


    //we need to change the coordinate system, otherwise the image is drawn upside down
    CGContextTranslateCTM(pdfContext, 0, aView.bounds.size.height);
    CGContextScaleCTM(pdfContext, 1.0, -1.0);

    //this creates pixels within PDF context
    CGContextDrawImage(pdfContext, aView.bounds, cgImage);

    // remove PDF rendering context
    UIGraphicsEndPDFContext();

    [pdfData writeToFile:filepath atomically:YES];

    //memory cleanup
    CGDataProviderRelease(dp);
    CGImageRelease(cgImage);
    return filepath;

}
Alex Stone
  • 46,408
  • 55
  • 231
  • 407
  • Super cool trick to control PDF sizes. Worked for me. (I was about to leave a comment to the author of the question asking why he had not accepted this answer ... lol) – fsaint Feb 03 '14 at 18:38