In the article, I will discuss, how we can create PDF programmatically using the PDFKit framework.
First of all, The PDFKit is a framework available in AppKit since iOS 11.0 and macOS 10.4. Please click here to read more about PDFKit.
PDFView is mainly used to show the pdf file in our application and also it allows users to set zoom level, select content, navigate through a document, and copy the textual content to the Pasteboard, and also keeps track of page history.
Getting Started
Swift version: 5
iOS version: 13
Xcode: 11.3
First Create a project from Xcode.
File ▸ New ▸ Project…. Choose the iOS ▸ Application ▸ Single View App template and create a new project.
Then create a PDFCreator class where we will add all functions and variables which helps us to create the PDF.
1 2 3 4 |
class PDFCreator { } |
Now we will add basic variables that manage the PDF layout.
pageWidth : To define page width of PDF.
pageHeight : To define page Height of PDF.
marginPoint : To define the page margin of PDF.
currentPage : To Store page count of the PDF.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
lazy var pageWidth : CGFloat = { return 8.5 * 72.0 }() lazy var pageHeight : CGFloat = { return 11 * 72.0 }() lazy var pageRect : CGRect = { CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight) }() lazy var marginPoint : CGPoint = { return CGPoint(x: 10, y: 10) }() lazy var marginSize : CGSize = { return CGSize(width: self.marginPoint.x * 2 , height: self.marginPoint.y * 2) }() var currentPage = 0 |
Now create a function that creates the actual PDF data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
func prepareData(dataList:[String]) -> Data { let pdfMetaData = [ kCGPDFContextCreator: "Demo creator", kCGPDFContextAuthor: "Webkul", kCGPDFContextTitle: "Simple PDF" ] let format = UIGraphicsPDFRendererFormat() format.documentInfo = pdfMetaData as [String: Any] let renderer = UIGraphicsPDFRenderer(bounds: pageRect, format: format) let data = renderer.pdfData { (context) in for obj in dataList{ self.addText(obj, context: context) } } return data } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@discardableResult func addText(_ text : String, context : UIGraphicsPDFRendererContext) -> CGFloat { let textFont = UIFont.init(name: "Georgia", size: 27) let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .natural paragraphStyle.lineBreakMode = .byWordWrapping let textAttributes = [ NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.font: textFont ] let textString = CFAttributedStringCreate(nil, text as CFString, textAttributes as CFDictionary) let frame = CTFramesetterCreateWithAttributedString(textString!) var currentRange = CFRangeMake(0, 0) var done = false repeat { context.beginPage() currentPage += 1 drawPageNumber(currentPage) currentRange = renderPage(currentPage, withTextRange: currentRange,andFramesetter: frame) if currentRange.location == CFAttributedStringGetLength(textString) { done = true } } while !done return CGFloat(currentRange.location + currentRange.length) } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
func renderPage(_ pageNum: Int, withTextRange currentRange: CFRange, andFramesetter framesetter: CTFramesetter?) -> CFRange { var currentRange = currentRange let currentContext = UIGraphicsGetCurrentContext() currentContext?.textMatrix = .identity let frameRect = CGRect(x: self.marginPoint.x, y: self.marginPoint.y, width: self.pageWidth - self.marginSize.width, height: self.pageHeight - self.marginSize.height - 20) let framePath = CGMutablePath() framePath.addRect(frameRect, transform: .identity) let frameRef = CTFramesetterCreateFrame(framesetter!, currentRange, framePath, nil) currentContext?.translateBy(x: 0, y: self.pageHeight) currentContext?.scaleBy(x: 1.0, y: -1.0) CTFrameDraw(frameRef, currentContext!) currentRange = CTFrameGetVisibleStringRange(frameRef) currentRange.location += currentRange.length currentRange.length = CFIndex(0) return currentRange } |
Now add a page footer in the PDF.
1 2 3 4 5 6 7 8 9 |
func drawPageNumber(_ pageNum: Int) { let theFont = UIFont.systemFont(ofSize: 20) let pageString = NSMutableAttributedString(string: "Page \(pageNum)") pageString.addAttribute(NSAttributedString.Key.font, value: theFont, range: NSRange(location: 0, length: pageString.length)) let pageStringSize = pageString.size() let stringRect = CGRect(x: (pageRect.width - pageStringSize.width) / 2.0, y: pageRect.height - (pageStringSize.height) / 2.0 - 15, width: pageStringSize.width, height: pageStringSize.height) pageString.draw(in: stringRect) } |
And, Now show this PDF Data in the ViewController.
1 2 3 4 5 6 7 8 |
let pdfView = PDFView(frame: view.frame) if let pdfDocument = PDFDocument(data:url) { pdfView.displayMode = .singlePageContinuous pdfView.autoScales = true pdfView.displayDirection = .vertical pdfView.document = pdfDocument view.addSubview(pdfView) } |
I hope this code will help you better to understand how PDF generates. If you feel any doubt or query please comment below.
Thank you.