Create and Store PDF Document Programmatically Using Swift for iOS

Create and store PDF document programmatically using Swift for iOS

I used this code to create and save the file (using HTML)

func createPDF() {
let html = "<b>Hello <i>World!</i></b> <p>Generate PDF file from HTML in Swift</p>"
let fmt = UIMarkupTextPrintFormatter(markupText: html)

// 2. Assign print formatter to UIPrintPageRenderer

let render = UIPrintPageRenderer()
render.addPrintFormatter(fmt, startingAtPageAt: 0)

// 3. Assign paperRect and printableRect

let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
let printable = page.insetBy(dx: 0, dy: 0)

render.setValue(NSValue(cgRect: page), forKey: "paperRect")
render.setValue(NSValue(cgRect: printable), forKey: "printableRect")

// 4. Create PDF context and draw

let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, .zero, nil)

for i in 1...render.numberOfPages {
UIGraphicsBeginPDFPage();
let bounds = UIGraphicsGetPDFContextBounds()
render.drawPage(at: i - 1, in: bounds)
}

UIGraphicsEndPDFContext();

// 5. Save PDF file

let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]

pdfData.write(toFile: "\(documentsPath)/file.pdf", atomically: true)
}

Then I loaded it into UIWebView from the documents directory with this code:

func loadPDF(filename: String) {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let url = URL(fileURLWithPath: documentsPath, isDirectory: true).appendingPathComponent(filename).appendingPathExtension("pdf")
let urlRequest = URLRequest(url: url)
webView.loadRequest(urlRequest)
}

Saving PDF Files with Swift in iOS and display them

Since several people requested this, here is the equivalent to the first answer in Swift:

//The URL to Save
let yourURL = NSURL(string: "http://somewebsite.com/somefile.pdf")
//Create a URL request
let urlRequest = NSURLRequest(URL: yourURL!)
//get the data
let theData = NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: nil, error: nil)

//Get the local docs directory and append your local filename.
var docURL = (NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)).last as? NSURL

docURL = docURL?.URLByAppendingPathComponent( "myFileName.pdf")

//Lastly, write your file to the disk.
theData?.writeToURL(docURL!, atomically: true)

Also, since this code uses a synchronous network request, I highly recommend dispatching it to a background queue:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
//The URL to Save
let yourURL = NSURL(string: "http://somewebsite.com/somefile.pdf")
//Create a URL request
let urlRequest = NSURLRequest(URL: yourURL!)
//get the data
let theData = NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: nil, error: nil)

//Get the local docs directory and append your local filename.
var docURL = (NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)).last as? NSURL

docURL = docURL?.URLByAppendingPathComponent( "myFileName.pdf")

//Lastly, write your file to the disk.
theData?.writeToURL(docURL!, atomically: true)
})

And the answer to second question in Swift:

//Getting a list of the docs directory
let docURL = (NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last) as? NSURL

//put the contents in an array.
var contents = (NSFileManager.defaultManager().contentsOfDirectoryAtURL(docURL!, includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions.SkipsHiddenFiles, error: nil))
//print the file listing to the console
println(contents)

_x0013_

Generate PDF with Swift

Here is my approach. Use a template view. It has all the static texts and images. And then for every page in pdf use this template view. Here is the function that I use:

func renderAsPDF(demandEntry: ParsedDemandEntry, inView view: UIView) -> NSData? {
let entries = demandEntry.demands
let pageCount = Int(ceil(Double(entries.count) / Double(demandCountForPage)))
if pageCount != 0 {
let views = (1...pageCount).map { (pageNumber: Int) -> UIView in
let pdfPageView = createTemplatePageViewWithParsedEntry(demandEntry, inView: view)

let pageRange = ((pageNumber - 1) * demandCountForPage)..<(min(pageNumber * demandCountForPage, entries.count))
let entriesForPage = Array(entries[pageRange])

addEntries(entriesForPage, toView: pdfPageView)

pdfPageView.removeFromSuperview()

return pdfPageView
}

return toPDF(views)
} else {
return nil
}
}

The ParsedDemandEntry is my model object. The view parameter is a container view to prepare pdf view in it. This is necessary because I use auto layout to position all labels in pdf view. Without a super view layout process won't work.

Let's walk into function. First I get entries from model object. Think these are rows that needs to populate in pdf. After that I calculate how many pages I need. Then the loop begins. In every loop I create a tamplate view. (pdfPageView). And then fill it with entries. (addEntries(_:toView:) function call).

After the loop I give all views to toPDF function. It creates NSData that represents pdf. Here is how toPDF function look like:

private func toPDF(views: [UIView]) -> NSData? {

if views.isEmpty {
return nil
}

let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, CGRect(x: 0, y: 0, width: 612, height: 792), nil)

let context = UIGraphicsGetCurrentContext()

for view in views {
UIGraphicsBeginPDFPage()
view.layer.renderInContext(context)
}

UIGraphicsEndPDFContext()

return pdfData
}

It is fairly simple. First I create pdf context. And then loop through views array. For each view It renders view into pdf context.

I hope this will help.

iOS SDK - Programmatically generate a PDF file

A couple things...

First, there is a bug with CoreGraphics PDF generation in iOS that results in corrupted PDFs. I know this issue exists up to and including iOS 4.1 (I haven't tested iOS 4.2). The issue is related to fonts and only shows up if you include text in your PDF. The symptom is that, when generating the PDF, you'll see errors in the debug console that look like this:

<Error>: can't get CIDs for glyphs for 'TimesNewRomanPSMT'

The tricky aspect is that the resulting PDF will render fine in some PDF readers, but fail to render in other places. So, if you have control over the software that will be used to open your PDF, you may be able to ignore this issue (e.g., if you only intend to display the PDF on the iPhone or Mac desktops, then you should be fine using CoreGraphics). However, if you need to create a PDF that works anywhere, then you should take a closer look at this issue. Here's some additional info:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/15505-pdf-font-problem-cant-get-cids-glyphs.html#post97854

As a workaround, I've used libHaru successfully on iPhone as a replacement for CoreGraphics PDF generation. It was a little tricky getting libHaru to build with my project initially, but once I got my project setup properly, it worked fine for my needs.

Second, depending on the format/layout of your PDF, you might consider using Interface Builder to create a view that serves as a "template" for your PDF output. You would then write code to load the view, fill in any data (e.g., set text for UILabels, etc.), then render the individual elements of the view into the PDF. In other words, use IB to specify coordinates, fonts, images, etc. and write code to render various elements (e.g., UILabel, UIImageView, etc.) in a generic way so you don't have to hard-code everything. I used this approach and it worked out great for my needs. Again, this may or may not make sense for your situation depending on the formatting/layout needs of your PDF.

EDIT: (response to 1st comment)

My implementation is part of a commercial product meaning that I can't share the full code, but I can give a general outline:

I created a .xib file with a view and sized the view to 850 x 1100 (my PDF was targeting 8.5 x 11 inches, so this makes it easy to translate to/from design-time coordinates).

In code, I load the view:

- (UIView *)loadTemplate
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"ReportTemplate" owner:self options:nil];
for (id view in nib) {
if ([view isKindOfClass: [UIView class]]) {
return view;
}
}

return nil;
}

I then fill in various elements. I used tags to find the appropriate elements, but you could do this other ways. Example:

UILabel *label = (UILabel *)[templateView viewWithTag:TAG_FIRST_NAME];
if (label != nil) {
label.text = (firstName != nil) ? firstName : @"None";

Then I call a function to render the view to the PDF file. This function recursively walks the view hierarchy and renders each subview. For my project, I need to support only Label, ImageView, and View (for nested views):

- (void)addObject:(UIView *)view
{
if (view != nil && !view.hidden) {
if ([view isKindOfClass:[UILabel class]]) {
[self addLabel:(UILabel *)view];
} else if ([view isKindOfClass:[UIImageView class]]) {
[self addImageView:(UIImageView *)view];
} else if ([view isKindOfClass:[UIView class]]) {
[self addContainer:view];
}
}
}

As an example, here's my implementation of addImageView (HPDF_ functions are from libHaru):

- (void)addImageView:(UIImageView *)imageView
{
NSData *pngData = UIImagePNGRepresentation(imageView.image);
if (pngData != nil) {
HPDF_Image image = HPDF_LoadPngImageFromMem(_pdf, [pngData bytes], [pngData length]);
if (image != NULL) {
CGRect destRect = [self rectToPDF:imageView.frame];

float x = destRect.origin.x;
float y = destRect.origin.y - destRect.size.height;
float width = destRect.size.width;
float height = destRect.size.height;

HPDF_Page page = HPDF_GetCurrentPage(_pdf);
HPDF_Page_DrawImage(page, image, x, y, width, height);
}
}
}

Hopefully that gives you the idea.

Create and save PDF that contain image in swift

I have been at this for some time, and finally some breakthrough

Since you are loading the html string, it won't work for images, you have to load the html in a web view and then give UIViewPrintFormatter format as _webVeiw.viewPrintFormatter()

Also don't forget to set baseURL for UIWebView else it won't load the local images.

please look at my gist for more help.

Generate PDF from html iOS swift

I also had troubles when creating page breaks, so I created multiple html's to accommodate that too.

Hope it helps, please let me know if you need any more information :)

How to download PDF and store it locally on iPhone?

I have found one method which I tried myself:

// Get the PDF Data from the url in a NSData Object
NSData *pdfData = [[NSData alloc] initWithContentsOfURL:[
NSURL URLWithString:@"http://www.example.com/info.pdf"]];

// Store the Data locally as PDF File
NSString *resourceDocPath = [[NSString alloc] initWithString:[
[[[NSBundle mainBundle] resourcePath] stringByDeletingLastPathComponent]
stringByAppendingPathComponent:@"Documents"
]];

NSString *filePath = [resourceDocPath
stringByAppendingPathComponent:@"myPDF.pdf"];
[pdfData writeToFile:filePath atomically:YES];

// Now create Request for the file that was saved in your documents folder
NSURL *url = [NSURL fileURLWithPath:filePath];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];

[webView setUserInteractionEnabled:YES];
[webView setDelegate:self];
[webView loadRequest:requestObj];

This will store your PDF locally and load it into your UIWebView.



Related Topics



Leave a reply



Submit