Create PDF in iOS

By | September 7, 2016

The UIKit framework provides a set of functions for generating PDF content using native drawing code.
These functions let you create a graphics context that targets a PDF file or PDF data object. You can then create one or more PDF pages and draw into those pages using the same UIKit and Core Graphics drawing routines you use when drawing to the screen. When you are done, what you are left with is a PDF version of what you drew.

PDF iOS

PDF iOS

The overall drawing process is similar to the process for creating any other image.

You create a PDF graphics context using either the UIGraphicsBeginPDFContextToData or UIGraphicsBeginPDFContextToFile function. These functions create the graphics context and associate it with a destination for the PDF data. For the UIGraphicsBeginPDFContextToData function, the destination is an NSMutableData object that you provide. And for the UIGraphicsBeginPDFContextToFile function, the destination is a file in your app’s home directory.

PDF documents organize their content using a page-based structure. This structure imposes two restrictions on any drawing you do:

There must be an open page before you issue any drawing commands.
You must specify the size of each page.
The functions you use to create a PDF graphics context allow you to specify a default page size but they do not automatically open a page. After creating your context, you must explicitly open a new page using either the UIGraphicsBeginPDFPage or UIGraphicsBeginPDFPageWithInfo function. And each time you want to create a new page, you must call one of these functions again to mark the start of the new page. The UIGraphicsBeginPDFPage function creates a page using the default size, while the UIGraphicsBeginPDFPageWithInfo function lets you customize the page size and other page attributes.

When you are done drawing, you close the PDF graphics context by calling the UIGraphicsEndPDFContext. This function closes the last page and writes the PDF content to the file or data object you specified at creation time. This function also removes the PDF context from the graphics context stack.

Below Code Shows the processing loop used by an app to create a PDF file from the text in a text view. Aside from three function calls to configure and manage the PDF context, most of the code is related to drawing the desired content. The app uses the Core Text framework (and more specifically a CTFramesetterRef data type) to handle the text layout and management on successive pages.

CREATE PDF

Add the “CoreGraphics.framework”.

Here is complete code for creating a PDF.



#import "ViewController.h"
#import <Coretext /CoreText.h>


@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    [self createPDFNamed:@"MyPdf"];
    
}

-(NSString *) getPDFPath :(NSString *) name{
    
    NSString *newPDFName = [NSString stringWithFormat:@"%@.pdf", name];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];

    NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:newPDFName];
    
    return pdfPath;
}

- (void)createPDFNamed:(NSString*)name{

    NSString *text = @"YOUR_LONG_TEXT";
    
    // Prepare the text using a Core Text Framesetter.
    CFAttributedStringRef currentText = CFAttributedStringCreate(NULL, (CFStringRef)text, NULL);
    
    if (currentText) {
        
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);
        
        if (framesetter) {
            
            NSString *pdfFileName = [self getPDFPath : name];
            // Create the PDF context using the default page size of 612 x 792.
            UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);
            
            CFRange currentRange = CFRangeMake(0, 0);
            NSInteger currentPage = 0;
            BOOL done = NO;
            
            do {
                // Mark the beginning of a new page.
                UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);
                
                // Draw a page number at the bottom of each page.
                currentPage++;
                
                [self drawPageNumber:currentPage];
                
                // Render the current page and update the current range to
                // point to the beginning of the next page.
                currentRange = [self renderPagewithTextRange:currentRange andFramesetter:framesetter];
                
                // If we're at the end of the text, exit the loop.
                if (currentRange.location == CFAttributedStringGetLength((CFAttributedStringRef)currentText))
                    done = YES;
            } while (!done);
            
            // Close the PDF context and write the contents out.
            UIGraphicsEndPDFContext();
            
            // Release the framewetter.
            CFRelease(framesetter);
            
        } else {
            NSLog(@"Could not create the framesetter needed to lay out the atrributed string.");
        }
        // Release the attributed string.
        CFRelease(currentText);
    } else {
        NSLog(@"Could not create the attributed string for the framesetter");
    }
}

// Use Core Text to draw the text in a frame on the page.
- (CFRange)renderPagewithTextRange:(CFRange)currentRange
       andFramesetter:(CTFramesetterRef)framesetter
{
    // Get the graphics context.
    CGContextRef    currentContext = UIGraphicsGetCurrentContext();
    
    // Put the text matrix into a known state. This ensures
    // that no old scaling factors are left in place.
    CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
    
    // Create a path object to enclose the text. Use 72 point
    // margins all around the text.
    CGRect    frameRect = CGRectMake(72, 72, 468, 648);
    CGMutablePathRef framePath = CGPathCreateMutable();
    CGPathAddRect(framePath, NULL, frameRect);
    
    // Get the frame that will do the rendering.
    // The currentRange variable specifies only the starting point. The framesetter
    // lays out as much text as will fit into the frame.
    CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
    CGPathRelease(framePath);
    
    // Core Text draws from the bottom-left corner up, so flip
    // the current transform prior to drawing.
    CGContextTranslateCTM(currentContext, 0, 792);
    CGContextScaleCTM(currentContext, 1.0, -1.0);
    
    // Draw the frame.
    CTFrameDraw(frameRef, currentContext);
    
    // Update the current range based on what was drawn.
    currentRange = CTFrameGetVisibleStringRange(frameRef);
    currentRange.location += currentRange.length;
    currentRange.length = 0;
    CFRelease(frameRef);
    
    return currentRange;
}

- (void)drawPageNumber:(NSInteger)pageNum
{
    NSString *pageString = [NSString stringWithFormat:@"Page %d", (int)pageNum];
    UIFont *theFont = [UIFont systemFontOfSize:12];
    
    CGSize pageStringSize = [pageString sizeWithAttributes:
                   @{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];

    CGRect stringRect = CGRectMake(((612.0 - pageStringSize.width) / 2.0),
                                   720.0 + ((72.0 - pageStringSize.height) / 2.0),
                                   pageStringSize.width,
                                   pageStringSize.height);
    
    /// Make a copy of the default paragraph style
    NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    /// Set line break mode
    paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
    /// Set text alignment
    paragraphStyle.alignment = NSTextAlignmentRight;
    
    NSDictionary *attributes = @{ NSFontAttributeName: theFont,
                                  NSParagraphStyleAttributeName: paragraphStyle };
    
    [pageString drawInRect:stringRect withAttributes:attributes];
}


@end

Check the Documents directory to see the created File.

Open a PDF

The below code opens the PDF file from the Documents folder in a WebView.

-(NSString *) getPDFPath :(NSString *) name{
    
    NSString *newPDFName = [NSString stringWithFormat:@"%@.pdf", name];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];

    NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:newPDFName];
    
    return pdfPath;
}

-(void) openPDF{
    
    NSURL *targetURL = [NSURL fileURLWithPath:[self getPDFPath:@"MyPdf"]];
    if(nil != targetURL){
        NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
        [webView loadRequest:request];
        self.title = @"PDF Opened...";
    }else{
        NSLog(@"PDF File not created...");
    }
}

You can download the complete Source code of the above example from here.

Send your comments to coderzheaven@gmail.com.

2 thoughts on “Create PDF in iOS

  1. Suresh

    Using your method created PDF. But my requirement is I want to use the same text attributes like color and size of the text in the textview in the PDF too. How to make it possible? any suggestion?

    Reply
    1. James Post author

      Suresh, Did you try using HTML with colors and converted to PDF?

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *