In one of my latest applications, I wanted to allow users to

A) Turn text into a PDF file (multi-page document if needed)

B) Optionally set a password to protect this very PDF file

C) Set permissions to copy/print the PDF file

Since I haven’t played with PDF content creation on the iPhone/iPod Touch/iPad before, I fired up google and discovered THIS source. It was very helpful for understanding multi-page PDF creation. However there were two problems:

  1. The created PDF document ignored the chosen font and size.
  2. It did not contain information on how to password protect the PDF file.

After playing with this code for a while, I discovered a loop hole. If you “Push” your context, you can use the following method to draw a string with the correct font/size:


UIGraphicsPushContext(pdfContext);
    [myNSString drawInRect:bounds withFont:[UIFont fontWithName:fontName size:fontSize]];
UIGraphicsPopContext();

Problem (1) was solved now and after reading Apple’s documentation on PDF file creation, I had an idea how password protection might work as well.

Ok, now that I had all the ingredients, it was time to get this thing up and running.

Before you read on, please keep in mind, that part of the code below has been thrown together, in order to get useful results real quick. I somehow got a feeling, that there is (must be) a more elegant way to do this.

Anyways, here we go…

I created 2 different methods:

1:


- (void) createPDF:(NSString *)fileName withContent:(NSString *)content forSize:(int)fontSize andFont:(NSString *)font andColor:(UIColor *)color:(BOOL)allowCopy:(BOOL)allowPrint:(NSString*)password;

This method accepts the following variables:

  • fileName: the name we want to assign to our PDF file (e.g. “myPDF.pdf”).
  • content: the text (string) we want to write to our PDF document.
  • fontSize: size of our font (9, 10, 12, 14, 16, etc…)
  • font: name of the font, we want to use (e.g. “Helvetica”).
  • color: the color, we want to use (black, blue, etc…)
  • allowCopy: YES/NO – whether we want to allow copy for this document
  • allowPrint: YES/NO – whether we want to allow print for this document
  • passoword: a user defined password to unlock content of the pdf file.

2:


- (NSString *)stringToDraw:(NSString*)fontName:(int)fontSize;

This method will be called from our method (1) and will return a NSString object. More on that later…

Before we take a closer look at method (1), we need to add the following lines to the .m file (right below #import lines), we’re working in:


//defines our default PDF page layout

#define LEFT_MARGIN 25

#define RIGHT_MARGIN 25

#define TOP_MARGIN 35

#define BOTTOM_MARGIN 50

#define BOTTOM_FOOTER_MARGIN 32

#define DOC_WIDTH 595

#define DOC_HEIGHT 842

We also need a NSString object, that will hold some text for us later, a BOOL value, named “done” and a NSMutableArray. Simply create those in your viewDidLoad method or wherever you initialize your class:


//in .h

NSString *tempContentString;

NSMutableArray *textArray;

BOOL done;

@property (nonatomic, retain) NSString *tempContentString;

@property (nonatomic, retain) NSString *textArray;

//in .m

@synthesize tempString;

@synthesize textArray;

- (void)viewDidLoad {

tempContentString = [[NSString alloc] init];

textArray = [[NSMutableArray] alloc] init];

}

- (void)dealloc {

[tempContentString release];

[textArray release];

[super dealloc];

}

Now, let’s assume, that we have a UITextView element, named “myTextView” and this UITextView is filled with text.

Before we call method (1), we need to prepare a few things


- (void)preparePDFCreation {

NSString *fileName = @"myPDF.pdf"; //set whatever name you like

NSString *content = myTextView.text; //this is the UITextView, containing our text.

int fontSize = 20; //set your fontSize here

NSString *font = @"Papyrus"; //set your font name here (Helvetica, Georgia, etc...)

UIColor *myColor = [UIColor blackColor]; //set your color here (blueColor, brownColor, etc...)

NSString *password = @"test"; //set your password

//prepare saving our PDF file to documents directory

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *saveDirectory = [paths objectAtIndex:0];

NSString *saveFileName = fileName;

NSString *newFilePath = [saveDirectory stringByAppendingPathComponent:saveFileName];

tempContentString = content; //we assign our content (text we want to draw to our PDF file) to the tempContentString, we created earlier

BOOL pdfAllowCopy = YES; //allow/forbid copy

BOOL pdfAllowPrint = NO; //allow/forbid print

//important: if you forbid copy or print, you definitely need to set a password. Otherwise it won't work!

done = NO;

[textArray removeAllObjects]; //clean the textArray

[textArray setArray:[content componentsSeparatedByString:@" "]]; //split our text into single words...we will find out why we do this in method (2)

//Now, let's call our method (1)

[self createPDF:newFilePath withContent:tempContentString forSize:fontSize andFont:font andColor:myColor:pdfAllowCopy:pdfAllowPrinting:password];

}

- (void) createPDF:(NSString *)fileName withContent:(NSString *)content forSize:(int)fontSize andFont:(NSString *)font andColor:(UIColor *)color:(BOOL)allowCopy:(BOOL)allowPrint:(NSString*)password {

CGContextRef pdfContext; //our pdfContext

CFStringRef path;

CFURLRef url;

CFStringRef passwordString = (CFStringRef)password;

CGRect pageRect = CGRectMake(0, 0, DOC_WIDTH, DOC_HEIGHT);

CFMutableDictionaryRef myDictionary = NULL; //the dictionary, which will later contain some important meta data, like password, etc...

const char *filename = [fileName UTF8String];

// Create a CFString from the filename we provide to this method when we call it

path = CFStringCreateWithCString (NULL, filename,

kCFStringEncodingUTF8);

// Create a CFURL using the CFString we just defined

url = CFURLCreateWithFileSystemPath (NULL, path,

kCFURLPOSIXPathStyle, 0);

// This dictionary contains extra options mostly for 'signing' the PDF

myDictionary = CFDictionaryCreateMutable(NULL, 0,

&kCFTypeDictionaryKeyCallBacks,

&kCFTypeDictionaryValueCallBacks);

CFDictionarySetValue(myDictionary, kCGPDFContextTitle, (CFStringRef)mainDelegate.rootViewController.writeViewController.titleTextField.text);

CFDictionarySetValue(myDictionary, kCGPDFContextCreator, (CFStringRef)mainDelegate.rootViewController.writeViewController.authorTextField.text);

if (![password isEqualToString:@""]) CFDictionarySetValue(myDictionary, kCGPDFContextOwnerPassword, passwordString);

if (![password isEqualToString:@""]) CFDictionarySetValue(myDictionary, kCGPDFContextUserPassword, passwordString);

if (!allowCopy) CFDictionarySetValue(myDictionary, kCGPDFContextAllowsCopying, kCFBooleanFalse); //kCGPDFContextAllowsCopying is set to TRUE by default

if (!allowPrint) CFDictionarySetValue(myDictionary, kCGPDFContextAllowsPrinting, kCFBooleanFalse); //kCGPDFContextAllowsPrinting is set to TRUE by default

// Create our PDF Context with the CFURL, the CGRect we provide, and the above defined dictionary

pdfContext = CGPDFContextCreateWithURL (url, &pageRect, myDictionary);

// Cleanup our mess

CFRelease(myDictionary);

CFRelease(url);

//Now, this is a tricky part. We make use of a do - while loop in order to create as many pages as needed

do {

CGContextBeginPage (pdfContext, &pageRect); //begins a new PDF page

//create layout for our page

CGRect bounds = CGRectMake(LEFT_MARGIN,

TOP_MARGIN,

DOC_WIDTH - RIGHT_MARGIN - LEFT_MARGIN,

DOC_HEIGHT - TOP_MARGIN - BOTTOM_MARGIN);

UIGraphicsPushContext(pdfContext); //pushing the context, as explained at the beginning of this post

CGContextSaveGState(pdfContext);

CGContextTranslateCTM(pdfContext, 0, bounds.origin.y);

CGContextScaleCTM(pdfContext, 1, -1);

CGContextTranslateCTM(pdfContext, 0, -(bounds.origin.y + bounds.size.height));

if ([tempContentString length] > 0) [[self stringToDraw] drawInRect:bounds withFont:[UIFont fontWithName:font size:fontSize]]; //THIS IS THE NASTY PART

CGContextRestoreGState(pdfContext);

UIGraphicsPopContext();

CGContextEndPage (pdfContext); //ends the current page

}

while (!done);

// We are done with our context now, so we release it

CGContextRelease (pdfContext);

CFRelease(path);

}

Now, it’s getting messy. We’re about to explore the “stringToDraw” method. Before we go into detail, let me explain, what this method does.

When we draw text to our page, we have a huge problem. We do not know how much text will fit on a single page. This depends on length of the words used, font and size.

So, I figured, there must be a way to calculate this. And here is what I came up with:

Step by step…

  1. create a CGSize, named tempSize.
  2. create a CGSize, named theTextSize.
  3. use  – sizeWithFont:constrainedToSize: method to determine, whether the text we want to draw, is too big for one page or not
  4. create if/else statements for results of (3)

Here is the complete method with documentation:


- (NSString *)stringToDraw:(NSString*)fontName:(int)fontSize {

CGSize tempSize;

CGSize theTextSize;

tempSize.width = DOC_WIDTH - RIGHT_MARGIN - LEFT_MARGIN; //define width of our document

tempSize.height = 10000000; //we use some unreal number, to make sure our text will definitely fit into this temporary "document"

//now we take the text we want to draw and use the method described in (3) to fit it on our temporary document

theTextSize = [tempContentString sizeWithFont: [UIFont fontWithName:fontName size:fontSize] constrainedToSize: tempSize];

//Depending on length of the text, it will fit on a single page or not.

if (theTextSize.height > DOC_HEIGHT - TOP_MARGIN - BOTTOM_MARGIN) { //the text we want to draw DOES NOT fit on a single page

BOOL pageFilled = NO;

int wordCount = 0;

float currentHeight = 0.0f;

float previousHeight = 0.0f;

NSString *returnString = [[[NSString alloc] init]autorelease];

NSString *tempReturnString = [[[NSString alloc] init]autorelease];

//Now, the following is a bit messy. However, it works :) 

do { //repeat this loop, till our page is filled with words...

if ([textArray count] > wordCount + 1) { //if there are more words inside our textArray than the current word count + 1

returnString = (wordCount > 0) ? [returnString stringByAppendingString:[NSString stringWithFormat:@" %@", [textArray objectAtIndex:wordCount]]] : [NSString stringWithFormat:@"%@", [textArray objectAtIndex:wordCount]];

theTextSize = [returnString sizeWithFont: [UIFont fontWithName:font size:fontSize] constrainedToSize: CGSizeMake(DOC_WIDTH - RIGHT_MARGIN - LEFT_MARGIN, DOC_HEIGHT - TOP_MARGIN - BOTTOM_MARGIN)];

currentHeight = theTextSize.height;

if (theTextSize.height >= DOC_HEIGHT - TOP_MARGIN - BOTTOM_MARGIN) {

pageFilled = YES; // our page is now filled with words

}

if (currentHeight == previousHeight && currentHeight > 700.0f) { //this is a workaround. sometimes the above if statement fails and does not correctly detect a filled page. i noticed, that it happens most of the time, when using large fonts, like zapfino. so, what we do here, is simply check, if the height of our text stays the same when comparing two loops - hence "currentHeight" and "previousHeight". if that's the case and our text height is greater than 700 (look at if statement), we have a filled page.

pageFilled = YES; //page is now filled with words

wordCount --; //we subtract one word from our wordCount, because the filled page has not been correctly detected above

returnString = tempReturnString;

}

wordCount ++; //increase the wordCount

}

else {

pageFilled = YES; //we now have a correctly filled page

}

previousHeight = theTextSize.height; //set previousHeight, so we can compare it when running our next loop

tempReturnString = returnString;

}

while (!pageFilled); //repeat this loop until pageFilled = YES

for (int i = 0; i < wordCount; i++) { //remove all words, we put on our page from our textArray

[textArray removeObjectAtIndex:0];

}

if ([textArray count]) { //if there are words in our textArray

tempContentString = [textArray componentsJoinedByString:@" "]; //update our content string

}

else {

tempContentString = @"";

done = YES;

}

if ([returnString length] == 0) returnString = @" ";

return returnString;

}

else {

done = YES;

return tempContentString;

}

return tempContentString;

}

I’ve successfully implemented this code into my latest app and so far it did a great job. I managed to create PDF files with hundred pages or more in about 3-5 seconds on my iPad.

If you find this source helpful, manage to improve it or have questions/feedback, please do not hesitate to leave a comment.

[UPDATE: I've created a little demo project, showing this code in action. Grab it here.]

32 Responses to “Creating a multi page PDF document on iPhone/iPod Touch/iPad with encryption”

  • Mr.Er:

    Dear friendlydeveloper:

    Thanks for your sharing. Can i try to generate a new pdf from original pdf (It’s like copy) through your solution?

    Mr.Er

  • friendlydeveloper:

    Well, my methods have been designed to create a new pdf, using text as content only. I’m pretty sure, that there would be problems, if the pdf you wanted to copy contained a more complex layout (e.g. images, different fonts, etc…).
    Maybe you can go a bit more into detail of what you want to accomplish. Is it really just making a copy of a pdf file or do you also want to modify it?

  • Mr.Er:

    Hi, friendlydeveloper,

    Actually, i try to generate PDF files and try to insert image to one page of pdf. But now, i use Quartz to generate PDF files, can copy one by one successfully, but the big problem is that displays white or garbled text. (My question is the same as http://stackoverflow.com/questions/2957800 ).

    This my renderer code, reference it.
    http://stackoverflow.com/questions/3606292/iphone-create-pdf-document-from-pdf-pages/3698352#3698352

  • Mr.Er:

    Original pdf first page,
    1) http://lh6.ggpht.com/_RbT93tRnz2A/TInLl_IxaGI/AAAAAAAAHkE/uLPZBL3y3_M/s800/c2.png
    New generated pdf first page, (through CGContextDrawPDFPage displays white or garbled text)
    2) http://lh6.ggpht.com/_RbT93tRnz2A/TInLmcvvcPI/AAAAAAAAHkI/yB4Nynlcxno/s800/c1.png

    If you are interesting on this topic, please mail to me, i will reply you soon, and attach for my code, i hope we can solve this problem by together.

  • friendlydeveloper:

    Hi there,

    sorry for the delay. Thanks for the detailed information. I’ll do a couple tests and will get back to you with results and hopefully some useful information :)

  • friendlydeveloper:

    One more question:

    Just so I get this straight. Are you assembling a new PDF file out of various PDF files (e.g. picking a page from several different documents) or are you really just trying to duplicate one PDF file?

  • Mr.Er:

    Yes, i just trying to duplicate one pdf file, but the different is that i want to insert an image into original page (just like modify pdf) and save as a newly-generated pdf file. As i know, apple SDK seem to have no provide this function.

  • friendlydeveloper:

    Ok. Does inserting an image also mean, that you need to re-arrange the text, so the picture will not mess with the layout? If that’s the case, the whole thing might get a bit more complicated. However, if you just want to insert an image, I’m pretty sure we can solve this real quick. Please keep me posted.

  • Mr.Er:

    Hi there,
    Sorry,I think you misunderstand my meaning. My problem is that fonts will mess as I copy one by one not insert an image. You can trying to run my example code, and saw the results that displays white or garbled text.

    Example code:
    https://docs0.google.com/document/edit?id=11Xin73FYNGr1e9VDtwyFayLwbacqzKeJghEmwsuqSnQ&hl=zh_TW#

    Iphone Simulator Debug Console Message:
    Tue Sep 21 14:47:50 XXXXXX.local QuartzDemo[1780] : CGContextClosePath: no current point.
    Tue Sep 21 14:47:50 XXXXXX.local QuartzDemo[1780] : invalid Type1 font: unable to stream font.

  • friendlydeveloper:

    Thanks for the sample code. I will play with it in a bit and report back.

  • Mr.Er:

    OK, Waiting for your good news ;)

  • aQb:

    Hi Mr Er, friendlydeveloper,

    Did you find any solutions to this problem ? In my app I am trying to create a new PDF by selecting few pages from the original PDF. I too am facing this white,garbled text problem. Any news of success will be nice.

  • friendlydeveloper:

    Working on it. Will post as soon as I figured it out.

  • jindos:

    I’ve been discovering this problem for several days and only one source found a possible solution … but unfortunately it’s not completely applicable:

    http://blog.nomzit.com/2010/08/18/annoying-bug-in-quartz-pdfcontext-font-handling/

  • friendlydeveloper:

    Hi, I’ve blogged about a possible solution here.

  • Sapana:

    Hello….I have used ur code …..pdf file generated successfully but we r able to write any thing ….
    so please reply me….

  • Sapana:

    sorry we r not able write any thing in that pdf file….

  • friendlydeveloper:

    Sorry for the delay.

    I can take a look at the code, if you like. Just send it to lindmandesignsupport[at]me.com.
    I’m sure it’s nothing major. However, without seeing the code, it’s hard to suggest, since the sample I provided works great in my app.
    Hope, to hear from you…

  • Hey there, just wanted to say thanks for sharing your code. Working on multipage pdf creation on the iphone at the mo (relatively new to iphone dev), and your ideas were a huge help. Thanks again!

  • friendlydeveloper:

    Hey there,

    thanks for your feedback! Glad, you enjoyed my post.

    Happy coding :)

  • Jerry:

    I have been trying to get this to work in simple application and cannot. It creates the PDF file but the file is blank. I am giving it a simple line of text and it does not work. I found several errors in this code and corrected them ,perhaps they were not errors but they gave me errors in Xcode. I commented out the password stuff for testing purposes.

    Do you have any idea why my text will not go in the file.

    Thanks Jerry

    #define LEFT_MARGIN 25

    #define RIGHT_MARGIN 25

    #define TOP_MARGIN 35

    #define BOTTOM_MARGIN 50

    #define BOTTOM_FOOTER_MARGIN 32

    #define DOC_WIDTH 595

    #define DOC_HEIGHT 842

    - (void)viewDidAppear:(BOOL)animated
    {

    [self PageLoad];

    tempContentString = [[NSString alloc] init];

    textArray = [[NSMutableArray alloc] init];

    [self preparePDFCreation];

    }
    - (void)viewWillDisappear:(BOOL)animated
    {

    [super viewDidDisappear:animated];
    }

    - (void)preparePDFCreation {

    NSString *fileName = @”myPDF.pdf”; //set whatever name you like

    // NSString *content = myTextView.text; //this is the UITextView, containing our text.

    NSString *content = @”My first PDF Document BizRatios”;

    CGFloat fontSize = 20; //set your fontSize here

    NSString *fontName = @”Papyrus”; //set your font name here (Helvetica, Georgia, etc…)

    UIColor *myColor = [UIColor blackColor]; //set your color here (blueColor, brownColor, etc…)

    NSString *password = @”test”; //set your password

    //prepare saving our PDF file to documents directory

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *saveDirectory = [paths objectAtIndex:0];

    NSString *saveFileName = fileName;

    NSString *newFilePath = [saveDirectory stringByAppendingPathComponent:saveFileName];

    NSLog(@” PDF Directory 2 %@ ” , newFilePath);
    NSLog(@” PDF File name 2 %@ ” , saveFileName);

    tempContentString = content; //we assign our content (text we want to draw to our PDF file)

    BOOL pdfAllowCopy = YES; //allow/forbid copy

    BOOL pdfAllowPrint = NO; //allow/forbid print

    //important: if you forbid copy or print, you definitely need to set a password. Otherwise it won’t work!

    done = NO;

    [textArray removeAllObjects]; //clean the textArray

    [textArray setArray:[content componentsSeparatedByString:@" "]]; //split our text into single words…we will find out why we do this in method (2)

    //Now, let’s call our method (1)

    NSLog(@” before call to createdPDF . %@ ” , tempContentString);

    [self createPDF:newFilePath withContent:tempContentString fontSize:fontSize andFont:fontName andColor:myColor:pdfAllowCopy:pdfAllowPrint:password];

    NSLog(@” PDF tempContentString after main call at end %@ ” , tempContentString);

    int i;

    i=1;
    i=2;

    }

    - (void) createPDF:(NSString *)fileName withContent:(NSString *)content fontSize:(CGFloat)fontSize andFont:(NSString *)fontName andColor:(UIColor *)color:(BOOL)allowCopy:(BOOL)allowPrint:(NSString*)password {

    CGContextRef pdfContext; //our pdfContext

    CFStringRef path;

    CFURLRef url;

    CFStringRef passwordString = (CFStringRef)password;

    CGRect pageRect = CGRectMake(0, 0, DOC_WIDTH, DOC_HEIGHT);

    CFMutableDictionaryRef myDictionary = NULL; //the dictionary, which will later contain some important meta data, like password, etc…

    const char *filename = [fileName UTF8String];

    // Create a CFString from the filename we provide to this method when we call it

    path = CFStringCreateWithCString (NULL, filename,

    kCFStringEncodingUTF8);

    // Create a CFURL using the CFString we just defined

    url = CFURLCreateWithFileSystemPath (NULL, path,

    kCFURLPOSIXPathStyle, 0);

    // This dictionary contains extra options mostly for ‘signing’ the PDF

    myDictionary = CFDictionaryCreateMutable(NULL, 0,

    &kCFTypeDictionaryKeyCallBacks,

    &kCFTypeDictionaryValueCallBacks);

    NSLog(@” Enter CreatePDF content %@ ” , content);
    NSLog(@” Enter CreatePDF path %@ ” , path);

    NSString *title;

    title = @” Financial “;

    NSString *author;

    author = @” Jerry”;

    // CFDictionarySetValue(myDictionary, kCGPDFContextTitle, (CFStringRef)mainDelegate.rootViewController.writeViewController.titleTextField.text);
    CFDictionarySetValue(myDictionary, kCGPDFContextTitle, (CFStringRef)title);

    //CFDictionarySetValue(myDictionary, kCGPDFContextCreator, (CFStringRef)mainDelegate.rootViewController.writeViewController.authorTextField.text);
    CFDictionarySetValue(myDictionary, kCGPDFContextCreator, (CFStringRef)author );

    // if (![password isEqualToString:@""]) CFDictionarySetValue(myDictionary, kCGPDFContextOwnerPassword, passwordString);

    // if (![password isEqualToString:@""]) CFDictionarySetValue(myDictionary, kCGPDFContextUserPassword, passwordString);

    // if (!allowCopy) CFDictionarySetValue(myDictionary, kCGPDFContextAllowsCopying, kCFBooleanFalse); //kCGPDFContextAllowsCopying is set to TRUE by default

    // if (!allowPrint) CFDictionarySetValue(myDictionary, kCGPDFContextAllowsPrinting, kCFBooleanFalse); //kCGPDFContextAllowsPrinting is set to TRUE by default

    // Create our PDF Context with the CFURL, the CGRect we provide, and the above defined dictionary

    pdfContext = CGPDFContextCreateWithURL (url, &pageRect, myDictionary);

    // Cleanup our mess

    CFRelease(myDictionary);

    CFRelease(url);

    //Now, this is a tricky part. We make use of a do – while loop in order to create as many pages as needed

    do {

    CGContextBeginPage (pdfContext, &pageRect); //begins a new PDF page

    //create layout for our page

    CGRect bounds = CGRectMake(LEFT_MARGIN,

    TOP_MARGIN,

    DOC_WIDTH – RIGHT_MARGIN – LEFT_MARGIN,

    DOC_HEIGHT – TOP_MARGIN – BOTTOM_MARGIN);

    UIGraphicsPushContext(pdfContext); //pushing the context, as explained at the beginning of this post

    CGContextSaveGState(pdfContext);

    CGContextTranslateCTM(pdfContext, 0, bounds.origin.y);

    CGContextScaleCTM(pdfContext, 1, -1);

    CGContextTranslateCTM(pdfContext, 0, -(bounds.origin.y + bounds.size.height));

    // if ([tempContentString length] > 0) [[self stringToDraw drawInRect:bounds withFont:[UIFont fontWithName:fontName size:fontSize]]]; //THIS IS THE NASTY PART

    //if ([tempContentString length] > 0) [self stringToDraw: (NSString *) fontName fontSize :( CGFloat) fontSize];

    if ([tempContentString length] > 0)[[self stringToDraw: (NSString *) fontName fontSize :( CGFloat) fontSize] drawInRect:bounds withFont:[UIFont fontWithName:fontName size:fontSize]];

    NSLog(@” return fron stringtoDraw tempContentString %@ ” , tempContentString);

    // NSLog(@” return fron stringtoDraw pdfContext %@ ” , pdfContext);

    CGContextRestoreGState(pdfContext);

    // NSLog(@” after CGContextRestoreGState pdfContext %@ ” , pdfContext);

    UIGraphicsPopContext();

    CGContextEndPage (pdfContext); //ends the current page

    }

    while (!done);

    // We are done with our context now, so we release it

    CGContextRelease (pdfContext);

    CFRelease(path);

    }
    - (NSString *)stringToDraw:(NSString *) fontName fontSize:(CGFloat)fontSize
    {

    CGSize tempSize;

    CGSize theTextSize;

    tempSize.width = DOC_WIDTH – RIGHT_MARGIN – LEFT_MARGIN; //define width of our document

    tempSize.height = 10000000; //we use some unreal number, to make sure our text will definitely fit into this temporary “document”

    NSLog(@” ttempSize.width %d” , tempSize.width);
    NSLog(@” tempSize.height %d” , tempSize.height);

    //now we take the text we want to draw and use the method described in (3) to fit it on our temporary document

    theTextSize = [tempContentString sizeWithFont: [UIFont fontWithName:fontName size:fontSize] constrainedToSize: tempSize];

    // CGSize size = [/text] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

    //Depending on length of the text, it will fit on a single page or not.

    NSLog(@” fontName %@ ” , fontName);
    NSLog(@” fontSize %f ” , fontSize);
    NSLog(@” theTextSize.height 2 %d” , theTextSize.height);
    NSLog(@” theTextSize.height 2 %f” , theTextSize.height);
    NSLog(@” theTextSize.height 2 %@” , theTextSize.height);

    NSLog(@” DOC_HEIGHT – TOP_MARGIN – BOTTOM_MARGIN %d ” , DOC_HEIGHT – TOP_MARGIN – BOTTOM_MARGIN);

    if (theTextSize.height > DOC_HEIGHT – TOP_MARGIN – BOTTOM_MARGIN) { //the text we want to draw DOES NOT fit on a single page

    BOOL pageFilled = NO;

    int wordCount = 0;

    float currentHeight = 0.0f;

    float previousHeight = 0.0f;

    NSString *returnString = [[[NSString alloc] init]autorelease];

    NSString *tempReturnString = [[[NSString alloc] init]autorelease];

    //Now, the following is a bit messy. However, it works

    do { //repeat this loop, till our page is filled with words…

    if ([textArray count] > wordCount + 1) { //if there are more words inside our textArray than the current word count + 1

    returnString = (wordCount > 0) ? [returnString stringByAppendingString:[NSString stringWithFormat:@" %@", [textArray objectAtIndex:wordCount]]] : [NSString stringWithFormat:@"%@", [textArray objectAtIndex:wordCount]];

    theTextSize = [returnString sizeWithFont: [UIFont fontWithName:fontName size:fontSize] constrainedToSize: CGSizeMake(DOC_WIDTH – RIGHT_MARGIN – LEFT_MARGIN, DOC_HEIGHT – TOP_MARGIN – BOTTOM_MARGIN)];

    currentHeight = theTextSize.height;

    if (theTextSize.height >= DOC_HEIGHT – TOP_MARGIN – BOTTOM_MARGIN) {

    pageFilled = YES; // our page is now filled with words

    }

    if (currentHeight == previousHeight && currentHeight > 700.0f) { //this is a workaround. sometimes the above if statement fails and does not correctly detect a filled page. i noticed, that it happens most of the time, when using large fonts, like zapfino. so, what we do here, is simply check, if the height of our text stays the same when comparing two loops – hence “currentHeight” and “previousHeight”. if that’s the case and our text height is greater than 700 (look at if statement), we have a filled page.

    pageFilled = YES; //page is now filled with words

    wordCount –; //we subtract one word from our wordCount, because the filled page has not been correctly detected above

    returnString = tempReturnString;

    }

    wordCount ++; //increase the wordCount

    }

    else {

    pageFilled = YES; //we now have a correctly filled page

    }

    previousHeight = theTextSize.height; //set previousHeight, so we can compare it when running our next loop

    tempReturnString = returnString;

    }

    while (!pageFilled); //repeat this loop until pageFilled = YES

    for (int i = 0; i < wordCount; i++) { //remove all words, we put on our page from our textArray

    [textArray removeObjectAtIndex:0];

    }

    if ([textArray count]) { //if there are words in our textArray

    tempContentString = [textArray componentsJoinedByString:@" "]; //update our content string

    }

    else {

    tempContentString = @"";

    NSLog(@" PDF tempContentString null %@ " , tempContentString);

    done = YES;

    }

    if ([returnString length] == 0) returnString = @" ";

    NSLog(@" PDF tempContentString null %@ " , tempContentString);

    return returnString;

    }

    else {

    done = YES;

    NSLog(@" PDF tempContentString yes ok %@ " , tempContentString);

    return tempContentString;

    }

    NSLog(@" PDF tempContentString yes ok %@ " , tempContentString);
    return tempContentString;

    }

  • friendlydeveloper:

    Hi Jerry,

    thanks for your feedback! I’ll check this out and get back to you asap.

  • Rainman:

    I also had errors in XCode and fixed them. It creates the PDF file but it is blank… any help is greatly appreciated.

  • Rainman:

    By the grace of God I just figured out why the pdf was being created blank.

    I changed this line
    //[[self stringToDraw] drawInRect:bounds withFont:[UIFont fontWithName:font size:fontSize]]; //THIS IS THE NASTY PART

    to this line instead and it works
    [[self stringToDraw:font :fontSize] drawInRect:bounds withFont:[UIFont fontWithName:font size:fontSize]];

    Thank you so much for posting such a great tutorial on multipage pdf creation.

    One question, is there a way to change the font and font color in text in different part of the document?

  • friendlydeveloper:

    Hello Rainman,

    thanks for your comment. Sorry for the delay. WordPress actually didn’t notify me about your comment, so I discovered it “by accident” :)

    Glad you got it to work. My code is actually a workaround for the pdf framework bug, Apple has been ignoring for a couple years now. All we can do, is keep reporting this bug and hope, they maybe get it fixed for upcoming iOS 5.

    I think it should be possible to change both font and font color in different parts of the document, but I’ve never tried it.

    I’m currently super busy, but if I get a chance to give it a shot, I’ll update my post here.

    Cheers

  • Rainman:

    Thanks and I will be checking back just in case you find some time :) your help is appreciated.

  • prerna:

    hey thanks for this tutorial.i have one problem in this.this code is working fine for ipad but not for iphone.for iphone it create only one page.please help me with this. thanks

  • friendlydeveloper:

    Hi there,

    sorry to hear, that you’re having problems. What font are you using on the iPhone? I’ll look into this and report back asap.

  • friendlydeveloper:

    Just created a little demo project and the code works fine on iPhone. Please check end of original post to download the source code. Hope, this is helpful…

    Cheers,
    Friendlydeveloper

  • prerna:

    hey thanks alot..i was using “Papyrus” font but i think it is not supported by iphone.thanks for help

  • friendlydeveloper:

    Hi,

    yes that’s right. “Papyrus” doesn’t currently (iOS 4.x) seem to be available on iPhone.

  • Rainman:

    I have noticed that on multiple pages, on the last line of the page it only shows one word… any ideas on how to fix this? It happens even in the Demo project. Look at the first page, the last line only has the word “Duis”

    Thanks

Leave a Reply

An App, you might like:
We recommend…
You might also like…
Get Adobe Flash playerPlugin by wpburn.com wordpress themes