-
-
Save RockfordWei/310829e2b59fb9e3d6ecad5401bbc002 to your computer and use it in GitHub Desktop.
iOS, Swift: Create a PDF file from an HTML string with local image file support
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ViewController.swift | |
// hrender | |
// | |
// Created by Rockford Wei on 2021-08-25. | |
// | |
import UIKit | |
import WebKit | |
class ViewController: UIViewController { | |
@IBOutlet weak var browser: WKWebView! | |
let home = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first ?? "/" | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
let path = "\(home)/treefrog.png" | |
if let logo = UIImage(named: "logo")?.pngData(), | |
let root = URL(string: "file://\(home)/") { | |
let written = logo.writeToFile(path: path) | |
NSLog("file saved: \(written)") | |
let htm = """ | |
<html> | |
<title>test</title> | |
<style> | |
p { | |
font-size: 16px; | |
} | |
</style> | |
<body> | |
<img src='treefrog.png' width='100%'/> | |
<hr> | |
<p> | |
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse dignissim odio vitae mattis vestibulum. Curabitur eget sapien laoreet, fermentum nisl id, feugiat felis. Ut varius mollis arcu eget fermentum. Donec pellentesque metus tellus, eu luctus mi posuere in. Ut a posuere turpis, vel sagittis dolor. Donec venenatis, enim non tincidunt tempus, libero lectus varius lacus, ac consectetur lectus felis eu ipsum. Nam id nisl ac sem sagittis aliquet nec sollicitudin urna. Fusce fringilla est ac turpis venenatis suscipit. Quisque pellentesque arcu est, quis vehicula diam tempus quis. Sed aliquet semper nisi, ut porttitor orci fermentum et. Lorem ipsum dolor sit amet, consectetur adipiscing elit. | |
</p> | |
<p> | |
Suspendisse iaculis aliquet dapibus. Curabitur bibendum justo sed tellus rutrum, sit amet vehicula sem scelerisque. Aenean hendrerit, turpis ut faucibus ultrices, mauris justo viverra libero, id venenatis velit libero non elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean fermentum nisl at ante vestibulum venenatis. Fusce venenatis, tortor non varius lobortis, sem lorem porta tortor, vitae consectetur odio metus nec urna. Nam ut leo nec libero tincidunt sollicitudin. Phasellus aliquam mauris quis orci facilisis hendrerit. Donec mollis sit amet ligula vitae volutpat. Aenean quis libero non mauris viverra egestas. Donec laoreet metus enim, sed gravida diam fringilla vitae. | |
</p> | |
</body> | |
</html> | |
""" | |
browser.navigationDelegate = self | |
browser.loadHTMLString(htm, baseURL: root) | |
} else { | |
NSLog("logo not found") | |
} | |
} | |
} | |
extension ViewController: WKNavigationDelegate { | |
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { | |
let path = browser.exportAsPdf() | |
NSLog("pdf exported: \(path)") | |
} | |
} | |
extension Data { | |
func writeToFile(path: String) -> Int { | |
guard count > 0 else { return 0 } | |
guard let file = fopen(path, "wb") else { return -2 } | |
defer { | |
fclose(file) | |
} | |
return withUnsafeBytes { buffered in | |
guard let pointer = buffered.baseAddress else { return -1 } | |
return fwrite(pointer, 1, count, file) | |
} | |
} | |
} | |
extension WKWebView { | |
// Call this function when WKWebView finish loading | |
func exportAsPdf() -> String { | |
let pdfData = createPdfFile(printFormatter: self.viewPrintFormatter()) | |
return self.saveWebViewPdf(data: pdfData) | |
} | |
func createPdfFile(printFormatter: UIViewPrintFormatter) -> NSMutableData { | |
let originalBounds = self.bounds | |
self.bounds = CGRect(x: originalBounds.origin.x, y: bounds.origin.y, width: self.bounds.size.width, height: self.scrollView.contentSize.height) | |
let pdfPageFrame = CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.scrollView.contentSize.height) | |
let printPageRenderer = UIPrintPageRenderer() | |
printPageRenderer.addPrintFormatter(printFormatter, startingAtPageAt: 0) | |
printPageRenderer.setValue(NSValue(cgRect: UIScreen.main.bounds), forKey: "paperRect") | |
printPageRenderer.setValue(NSValue(cgRect: pdfPageFrame), forKey: "printableRect") | |
self.bounds = originalBounds | |
return printPageRenderer.generatePdfData() | |
} | |
// Save pdf file in document directory | |
func saveWebViewPdf(data: NSMutableData) -> String { | |
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) | |
let docDirectoryPath = paths[0] | |
let pdfPath = docDirectoryPath.appendingPathComponent("webViewPdf.pdf") | |
if data.write(to: pdfPath, atomically: true) { | |
return pdfPath.path | |
} else { | |
return "" | |
} | |
} | |
} | |
extension UIPrintPageRenderer { | |
func generatePdfData() -> NSMutableData { | |
let pdfData = NSMutableData() | |
UIGraphicsBeginPDFContextToData(pdfData, self.paperRect, nil) | |
self.prepare(forDrawingPages: NSMakeRange(0, self.numberOfPages)) | |
let printRect = UIGraphicsGetPDFContextBounds() | |
for pdfPage in 0 ..< self.numberOfPages { | |
UIGraphicsBeginPDFPage() | |
self.drawPage(at: pdfPage, in: printRect) | |
} | |
UIGraphicsEndPDFContext(); | |
return pdfData | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment