Skip to content

Instantly share code, notes, and snippets.

@vin-the-dev
Last active November 29, 2018 16:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vin-the-dev/899869df4f1538ffc7d6 to your computer and use it in GitHub Desktop.
Save vin-the-dev/899869df4f1538ffc7d6 to your computer and use it in GitHub Desktop.
PDF Creation from iOS swift
//
// CustomPageRenderer.swift
// (c) 2016 Vineeth Vijayan, licensed under the MIT License
// Inspired by https://gist.github.com/mattt/bd5e48ae461848cdbd1e#file-recipepagerenderer-swift
import UIKit
import AVFoundation
/// Units for printing content insets
let POINTS_PER_INCH: CGFloat = 72
/// The alignment for drawing an NSString inside a bounding rectangle.
enum NCStringAlignment {
case LeftTop
case CenterTop
case RightTop
case LeftCenter
case Center
case RightCenter
case LeftBottom
case CenterBottom
case RightBottom
}
extension NSString {
/// Draw the `NSString` inside the bounding rectangle with a given alignment.
func drawAtPointInRect(rect: CGRect, withAttributes attributes: [String: AnyObject]?, andAlignment alignment: NCStringAlignment) {
let size = self.sizeWithAttributes(attributes)
var x, y: CGFloat
switch alignment {
case .LeftTop, .LeftCenter, .LeftBottom:
x = CGRectGetMinX(rect)
case .CenterTop, .Center, .CenterBottom:
x = CGRectGetMidX(rect) - size.width / 2
case .RightTop, .RightCenter, .RightBottom:
x = CGRectGetMaxX(rect) - size.width
}
switch alignment {
case .LeftTop, .CenterTop, .RightTop:
y = CGRectGetMinY(rect)
case .LeftCenter, .Center, .RightCenter:
y = CGRectGetMidY(rect) - size.height / 2
case .LeftBottom, .CenterBottom, .RightBottom:
y = CGRectGetMaxY(rect) - size.height
}
self.drawAtPoint(CGPoint(x: x, y: y), withAttributes: attributes)
}
}
class CustomPrintPageRenderer: UIPrintPageRenderer {
let authorName: NSString
let pageNumberAttributes = [NSFontAttributeName: UIFont(name: "Georgia-Italic", size: 11)!]
let nameAttributes = [NSFontAttributeName: UIFont(name: "Georgia", size: 11)!]
init(authorName: String, html: String) {
self.authorName = "Vineeth" // authorName
super.init()
self.headerHeight = 0.5 * POINTS_PER_INCH
self.footerHeight = POINTS_PER_INCH * 1.5
}
override func drawFooterForPageAtIndex(pageIndex: Int, var inRect headerRect: CGRect) {
print("top : " + CGRectGetMinY(headerRect).description)
print("bottom : " + (CGRectGetMaxY(paperRect) - CGRectGetMaxY(headerRect)).description)
let headerInsets = UIEdgeInsets(top: CGRectGetMinY(headerRect), left: POINTS_PER_INCH * 2, bottom: 20, right: POINTS_PER_INCH)
headerRect = UIEdgeInsetsInsetRect(paperRect, headerInsets)
// Image left
let img = UIImage(named: "flag")
img?.drawAtPoint(CGPoint(x: 70, y: 760))
authorName.drawAtPointInRect(headerRect, withAttributes: nameAttributes, andAlignment: .LeftCenter)
// page number on right
let pageNumberString: NSString = "\(pageIndex + 1)"
pageNumberString.drawAtPointInRect(headerRect, withAttributes: pageNumberAttributes, andAlignment: .RightCenter)
}
func drawImages(images: [UIImage], var inRect sourceRect: CGRect) {
// we'll use 1/8 of an inch of vertical padding between each image
let imagePadding = UIEdgeInsets(top: POINTS_PER_INCH / 8, left: 0, bottom: 0, right: 0)
for image in images {
// get the aspect-fit size of the image
var sizedRect = AVMakeRectWithAspectRatioInsideRect(image.size, sourceRect)
// if the new width of the image doesn't match the source rect, there wasn't enough vertical room: bail
if sizedRect.width != sourceRect.width {
return
}
// use divide to separate the image rect from the rest of the column
CGRectDivide(sourceRect, &sizedRect, &sourceRect, sizedRect.height, .MinYEdge)
// draw the image
image.drawInRect(sizedRect)
// inset the source rect to make a little padding before the next image
sourceRect = UIEdgeInsetsInsetRect(sourceRect, imagePadding)
}
}
}
//http://stackoverflow.com/questions/6553540/merge-pdf-files-on-ios/35028513#35028513
//http://stackoverflow.com/a/35028513/2100226
//Using
func combinePDF() {
let file = "fileName.pdf"
let documentPaths = Path.temporaryDir[file].path_string
print(documentPaths)
let fullPDFOutput: CFURLRef = NSURL(fileURLWithPath: documentPaths)
let writeContext = CGPDFContextCreateWithURL(fullPDFOutput, nil, nil)
var pdfPagesURLArray = [String]()
for i in 0...(htmlURLs.count - 1) {
pdfPagesURLArray.insert(Path.temporaryDir["file" + i.description + ".pdf"].path_string, atIndex: i)
}
for pdfURL in pdfPagesURLArray {
let pdfPath: CFURLRef = NSURL(fileURLWithPath: pdfURL)
let pdfReference = CGPDFDocumentCreateWithURL(pdfPath)
let numberOfPages = CGPDFDocumentGetNumberOfPages(pdfReference)
var page: CGPDFPageRef
var mediaBox: CGRect
for index in 1...numberOfPages {
guard let getCGPDFPage = CGPDFDocumentGetPage(pdfReference, index) else {
NSLog("Error occurred in creating page")
return
}
page = getCGPDFPage
mediaBox = CGPDFPageGetBoxRect(page, .MediaBox)
CGContextBeginPage(writeContext, &mediaBox)
CGContextDrawPDFPage(writeContext, page)
CGContextEndPage(writeContext)
}
}
NSLog("DONE!")
CGPDFContextClose(writeContext);
NSLog(documentPaths)
}
//To generate pdf from html,
//htmlURLs is a list of paths to multipe htmls
//I have created multiple html's because page break was not working as required
func webViewDidFinishLoad(webView: UIWebView) {
let htmlString = Path.documentsDir["web" + webView.tag.description + ".html"].readString()
let render = UIPrintPageRenderer(authorName: "Vineeth", html: htmlString!)
let fmt = _webVeiw.viewPrintFormatter() // UIMarkupTextPrintFormatter(markupText: htmlString!)
fmt.contentInsets = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72)
render.addPrintFormatter(fmt, startingAtPageAtIndex: 0)
let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
let printable = CGRectInset(page, 0, 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, CGRectZero, nil)
for i in 1 ... render.numberOfPages() {
UIGraphicsBeginPDFPage();
let bounds = UIGraphicsGetPDFContextBounds()
render.drawPageAtIndex(i - 1, inRect: bounds)
}
UIGraphicsEndPDFContext();
// 5. Save PDF file
let path = "\(NSTemporaryDirectory())file" + _webVeiw.tag.description + ".pdf"
pdfData.writeToFile(path, atomically: true)
print("open \(path)")
if webView.tag < htmlURLs.count - 1 {
_webVeiw.tag = _webVeiw.tag + 1
_webVeiw.loadRequest(NSURLRequest(URL: NSURL(fileURLWithPath: Path.documentsDir[htmlURLs[_webVeiw.tag]].path_string)))
}
else {
combinePDF()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment