Creating a PDF From UITextView - ios

I'm currently creating and saving a PDF of a UIView. It is working great, but now I'm trying to create a PDF from text data saved to the disk rather than from the UIView. Ideally, I would like to create a PDF based on the text in the UITextView. The below code writes the first part of the text view just fine, but it doesn't create the PDF from the entire text view. It is quite a bit of text, so it only created the PDF from the amount of text that would fit on the view. The code that I'm currently using is this:
- (void)createPDFfromUIView:(UITextView*)view saveToDocumentsWithFileName:(NSString*)aFilename
{
NSMutableDictionary *myDictionary = CFBridgingRelease(CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
// 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, textview.bounds, myDictionary);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[textview.layer renderInContext:pdfContext]; // this line
// 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:#"filename"];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
}
And to call it / save it, I put this message send in an IBAction: [self createPDFfromUIView:textview saveToDocumentsWithFileName:#"pdflocation"];
Now, I've been parsing through this code and the docs for hours, and cannot figure out how to create a PDF based upon a saved text file rather than the view. I'm still new to programming, so a lot of this is going over my head. Does anyone know how ot change the above code to have it create a PDF based upon saved text rather than the view?
Options: I could create a PDF based on output of an NSString. I could also throw the saved text into a UITextView and create the PDF based upon that (original plan) I however don't know how to do either.

This article from Apple explains how to create Pdf files and draw text on Pdf pages.

For those still looking for some sample code, this works:
- (NSData *)createPDFforText: (NSString *)text
{
NSMutableData *pdfData = [NSMutableData data];
NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
UIGraphicsBeginPDFContextToData(pdfData, CGRectZero, nil);
UIGraphicsBeginPDFPage();
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
paragraphStyle.alignment = NSTextAlignmentLeft;
[attributes setObject: paragraphStyle forKey: NSParagraphStyleAttributeName];
[attributes setObject: [UIFont fontWithName: LATO_REGULAR size: 20.f] forKey: NSFontAttributeName];
[attributes setObject: [UIColor blackColor] forKey: NSForegroundColorAttributeName];
[text drawAtPoint: CGPointMake(20.f, 44.f) withAttributes: attributes];
UIGraphicsEndPDFContext();
return pdfData;
}
The CGRectZero bounds value sets the PDF document to the default page size of 8.5 by 11 inches (612 by 792 points).

Related

How to convert text entered in the uitextview in to pdf

when I add some text in the textview, onclick of convert to pdf,then i have to convert textview text to pdf file.
can anyone give some solution for this problem.
Thanks for quick response
Refer this code.
NSString *text = #"this is textField's text";
NSMutableData *pdfData = [[NSMutableData alloc] init];
UIGraphicsBeginPDFContextToData(pdfData, CGRectMake(0, 0, 320, 480), nil);
UIGraphicsBeginPDFPage();
[text drawInRect:UIGraphicsGetPDFContextBounds() withAttributes:#{NSFontAttributeName:[UIFont boldSystemFontOfSize:17]}];
UIGraphicsEndPDFContext();
NSString *file = #"file.pdf"; //this is the file name.
NSArray<NSURL *> *dir = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
if (dir.count > 0){
NSURL *path = [[dir firstObject] URLByAppendingPathComponent:file];
[pdfData writeToFile:path.path atomically:false];
}
You can provide any formatting options you want in the withAttributes dictionary in the draw method.
And if you want multiple pages in your PDF just put this code in a loop
UIGraphicsBeginPDFPage();
[text drawInRect:UIGraphicsGetPDFContextBounds() withAttributes:#{NSFontAttributeName:[UIFont boldSystemFontOfSize:17]}];
UIGraphicsEndPDFContext();
and change the text that draws. You can draw images too if you want them in your PDF.
Check the created PDF
Hope this helps.

trouble saving NSAttributedString, with image, to an RTF file

I have some output that is a very simple RTF file. When I generate this document, the user can email it. All this works fine. The document looks good. Once I have the NSAttributedString, I make an NSData block, and write it to a file, like this:
NSData* rtfData = [attrString dataFromRange:NSMakeRange(0, [attrString length]) documentAttributes:#{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType} error:&error];
This file can be emailed. When I check the email all is good.
Now, I'm tasked with adding a UIImage at the top of the document. Great, so I'm creating an attributed string like this:
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
UIImage* image = [UIImage imageNamed:#"logo"];
attachment.image = image;
attachment.bounds = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
NSMutableAttributedString *imageAttrString = [[NSAttributedString attributedStringWithAttachment:attachment] mutableCopy];
// sets the paragraph styling of the text attachment
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init] ;
[paragraphStyle setAlignment:NSTextAlignmentCenter]; // centers image horizontally
[paragraphStyle setParagraphSpacing:10.0f]; // adds some padding between the image and the following section
[imageAttrString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [imageAttrString length])];
[imageAttrString appendAttributedString:[[NSAttributedString alloc] initWithString:#"\n\n"]];
At this point, in Xcode, I can do a QuickLook on imageAttrString, and it draws just fine.
Once this string is built, I'm doing this:
[attrString appendAttributedString:imageAttrString];
And then adding in all the rest of the attributed text that I originally generated.
When I look at the file now, there is no image. QuickLook looks good in the debugger, but no image in the final output.
Thanks in advance for any help with this.
Although, RTF does support embedded images on Windows, apparently it doesn't on OS X. RTF was developed by Microsoft and they added embedded images in version 1.5 (http://en.wikipedia.org/wiki/Rich_Text_Format#Version_changes). I think that Apple took earlier version of the format and their solution to images in documents was RTFD. Here is what Apple documentation says about RTF:
Rich Text Format (RTF) is a text formatting language devised by Microsoft Corporation. You can represent character, paragraph, and document format attributes using plain text with interspersed RTF commands, groups, and escape sequences. RTF is widely used as a document interchange format to transfer documents with their formatting information across applications and computing platforms. Apple has extended RTF with custom commands, which are described in this chapter.
So no images are mentioned. Finally to prove that RTF doesn't support images on mac, download this RTF document - it will show photo in Windows WordPad and won't show it in OS X TextEdit.
So as Larme mentioned - you should choose RTFD file type when adding attachments. From Wikipedia:
Rich Text Format Directory, also known as RTFD (due to its extension .rtfd), or Rich Text Format with Attachments
Although you will be able to get NSData object that contains both the text and the image (judging by its size), via dataFromRange:documentAttributes:#{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType} error:] you probably won't be able to save it so that it could be opened successfully. At least - I wasn't able to do that.
That's probably because actually RTFD is not a file format - it's a format of a bundle. To check it, you could use TextEdit on your mac to create a new document, add image and a text to it and save it as a file. Then right click on that file and choose Show Package Contents and you'll notice that the directory contains both your image and the text in RTF format.
However you will be able to save this document successfully with this code:
NSFileWrapper *fileWrapper = [imageAttrString fileWrapperFromRange:NSMakeRange(0, [imageAttrString length]) documentAttributes:#{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType} error:&error];
[fileWrapper writeToURL:yourFileURL options:NSFileWrapperWritingAtomic originalContentsURL:nil error:&error];
Because apparently NSFileWrapper knows how to deal with RTFD documents while NSData has no clue of what it contains.
However the main problem still remains - how to send it in email? Because RTFD document is a directory not a file, I'd say it's not very well suited for sending by email, however you can zip it and send with an extension .rtfd.zip. The extension here is the crucial because it will tell Mail app how to display contents of the attachment when user taps on it. Actually it will work also in Gmail and probably other email apps on iOS because it's the UIWebView that knows how to display .rtfd.zip. Here is a technical note about it: https://developer.apple.com/library/ios/qa/qa1630/_index.html#//apple_ref/doc/uid/DTS40008749
So the bottom line is - it can be done but the RTFD document will be an attachment to the email not the email content itself. If you want to have it as an email content you should probably look into embedding your image into HTML and sending the mail as HTML.
As Andris mentioned, Apple RTF implementation does not support embedded images.
RTFD isn't a real alternative, as only a few OS X apps can open RTFD files. For example MS Office can't.
Creating a HTML file with embedded images might help in some cases, but - for example - most email clients don't support HTML with embedded images (Apple Mail does, Outlook however doesn't).
But fortunately there is a solution to create real RTF files with embedded images!
As the RTF format of course supports embedded images (only Apples implementation doesn't), images in a NSAttributedStrings (NSTextAttachments) can be (hand) coded into the RTF stream.
The following category does all the work needed:
/**
NSAttributedString (MMRTFWithImages)
*/
#interface NSAttributedString (MMRTFWithImages)
- (NSString *)encodeRTFWithImages;
#end
/**
NSAttributedString (MMRTFWithImages)
*/
#implementation NSAttributedString (MMRTFWithImages)
/*
encodeRTFWithImages
*/
- (NSString *)encodeRTFWithImages {
NSMutableAttributedString* stringToEncode = [[NSMutableAttributedString alloc] initWithAttributedString:self];
NSRange strRange = NSMakeRange(0, stringToEncode.length);
//
// Prepare the attributed string by removing the text attachments (images) and replacing them by
// references to the images dictionary
NSMutableDictionary* attachmentDictionary = [NSMutableDictionary dictionary];
while (strRange.length) {
// Get the next text attachment
NSRange effectiveRange;
NSTextAttachment* textAttachment = [stringToEncode attribute:NSAttachmentAttributeName
atIndex:strRange.location
effectiveRange:&effectiveRange];
strRange = NSMakeRange(NSMaxRange(effectiveRange), NSMaxRange(strRange) - NSMaxRange(effectiveRange));
if (textAttachment) {
// Text attachment found -> store image to image dictionary and remove the attachment
NSFileWrapper* fileWrapper = [textAttachment fileWrapper];
UIImage* image = [[UIImage alloc] initWithData:[fileWrapper regularFileContents]];
// Kepp image size
UIImage* scaledImage = [self imageFromImage:image
withSize:textAttachment.bounds.size];
NSString* imageKey = [NSString stringWithFormat:#"_MM_Encoded_Image#%zi_", [scaledImage hash]];
[attachmentDictionary setObject:scaledImage
forKey:imageKey];
[stringToEncode removeAttribute:NSAttachmentAttributeName
range:effectiveRange];
[stringToEncode replaceCharactersInRange:effectiveRange
withString:imageKey];
strRange.length += [imageKey length] - 1;
} // if
} // while
//
// Create the RTF stream; without images but including our references
NSData* rtfData = [stringToEncode dataFromRange:NSMakeRange(0, stringToEncode.length)
documentAttributes:#{
NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType
}
error:NULL];
NSMutableString* rtfString = [[NSMutableString alloc] initWithData:rtfData
encoding:NSASCIIStringEncoding];
//
// Replace the image references with hex encoded image data
for (id key in attachmentDictionary) {
NSRange keyRange = [rtfString rangeOfString:(NSString*)key];
if (NSNotFound != keyRange.location) {
// Reference found -> replace with hex coded image data
UIImage* image = [attachmentDictionary objectForKey:key];
NSData* pngData = UIImagePNGRepresentation(image);
NSString* hexCodedString = [self hexadecimalRepresentation:pngData];
NSString* encodedImage = [NSString stringWithFormat:#"{\\*\\shppict {\\pict \\pngblip %#}}", hexCodedString];
[rtfString replaceCharactersInRange:keyRange withString:encodedImage];
}
}
return rtfString;
}
/*
imageFromImage:withSize:
Scales the input image to pSize
*/
- (UIImage *)imageFromImage:(UIImage *)pImage
withSize:(CGSize)pSize {
UIGraphicsBeginImageContextWithOptions(pSize, NO, 0.0);
[pImage drawInRect:CGRectMake(0, 0, pSize.width, pSize.height)];
UIImage* resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
/*
hexadecimalRepresentation:
Returns a hex codes string for all bytes in a NSData object
*/
- (NSString *) hexadecimalRepresentation:(NSData *)pData {
static const char* hexDigits = "0123456789ABCDEF";
NSString* result = nil;
size_t length = pData.length;
if (length) {
NSMutableData* tempData = [NSMutableData dataWithLength:(length << 1)]; // double length
if (tempData) {
const unsigned char* src = [pData bytes];
unsigned char* dst = [tempData mutableBytes];
if ((src) &&
(dst)) {
// encode nibbles
while (length--) {
*dst++ = hexDigits[(*src >> 4) & 0x0F];
*dst++ = hexDigits[(*src++ & 0x0F)];
} // while
result = [[NSString alloc] initWithData:tempData
encoding:NSASCIIStringEncoding];
} // if
} // if
} // if
return result;
}
#end
The basic idea was taken from this article.

Create PDF from UIView in iOS 5.1 Landscape doesnt work

I'm trying to create a PDF from a UIView and works well, as "How to Convert UIView to PDF within iOS?" did.
But my iPad is landscape and is a form, lengthy form, with height 3k.
When I convert to PDF, it converts only the size of the height of the iPad, namely 748 and not 3k I need.
What is wrong? The iOS version?
EDITED: Heres the code like #wain ask
-(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();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[aView drawRect:aView.bounds];
// 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 find the solution from here "UIGraphicsGetCurrentContext and UIScrollView" and adaptate to my code and now grab all form and print a nice PDF.
If someone need my code, please let me know and i post the code.

Print NSString in CGContextRef and PDF with a custom font

I'm trying to create a new PDF with custom font.
I load my custom font and I can use it in all app, but when I tried to use it creating my PDF, the System font is printed.
I'm using NSString UIKit Additions.
+(UIFont *)imagoBook:(float)size{
return [UIFont fontWithName:#"Imago-Book" size:size];
}
-(NSData *)createPDFfromUIView
{
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData, self.bounds, nil);
UIGraphicsBeginPDFPage();
UIFont *imagoBook13 = [ROCTextsInformation imagoBook:13];
[#"Test" drawAtPoint:CGPointZero withFont:imagoBook13];
UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:#"t.pdf"];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
return pdfData;
}
In fact I'm drawing the same context in a view and the font is written perfectly but not in the pdf.
I'm using otf files for the font.
Thankssss
Finally I have solved the problem converting the file .otf to .ttf, and it works!

Create Multi Page PDF with UIImage at the Bottom

I need to create a multipage PDF with signature (UIImage) at the bottom. I would also like to have a line created so that the signature could be on a line. Here's the code I have so far:
- (IBAction)saveAsPDF:(id)sender
{
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData, CGRectMake(0, 0, 612, 792), nil);
UIGraphicsBeginPDFPage();
[nameTextField.text drawInRect:CGRectMake(50, 40, 512, 30) withFont:[UIFont systemFontOfSize:16] lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentCenter];
[TextView.text drawInRect:CGRectMake(50, 90, 512, 672) withFont:[UIFont systemFontOfSize:12]];
[SignatureImageView.image drawInRect:CGRectMake(30, 500, 190, 71)];
UIGraphicsEndPDFContext();
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentNameWithExtention = [NSString stringWithFormat:#"%#.pdf", nameTextField.text];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:documentNameWithExtention];
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
}
So, I need to know how to make it multipage and I need to know how to add the signature at the end of the body text.
Any help would be appreciated.
From the docs
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.

Resources