I am trying to export a PDF with a password protection.
Currently, My app directly exports the PDF File to the iOS Files app...
How do I password protect it before exporting it?
Here's my current code, Please do let me know what I have to change to achive this.
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}
var arrImages = [UIImage]()
for i in 0...scan.pageCount-1 {
let originalImage = scan.imageOfPage(at: i)
let fixedImage = reloadedImage(originalImage)
arrImages.append(fixedImage)
}
controller.dismiss(animated: true)
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let docURL = documentDirectory.appendingPathComponent("ScannedImage.pdf")
let data = createNewPDF(arrImage: arrImages)
do{
try data?.write(to: docURL)
print("Success")
}catch(let error){
print("error is \(error.localizedDescription)")
}
}
The PDF Creation Functions
func createPDF(image: UIImage) -> NSData? {
let pdfData = NSMutableData()
let pdfConsumer = CGDataConsumer(data: pdfData as CFMutableData)!
var mediaBox = CGRect.init(x: 0, y: 0, width: image.size.width, height: image.size.height)
let pdfContext = CGContext(consumer: pdfConsumer, mediaBox: &mediaBox, nil)!
pdfContext.beginPage(mediaBox: &mediaBox)
pdfContext.draw(image.cgImage!, in: mediaBox)
pdfContext.endPage()
let activityViewController = UIActivityViewController(activityItems: [pdfData], applicationActivities: nil)
present(activityViewController, animated: true, completion: nil)
return pdfData
}
func createNewPDF(arrImage: [UIImage]) -> Data? {
let pdfDocument = PDFDocument()
for i in 0...arrImage.count-1 {
let pdfPage = PDFPage(image: arrImage[i])
pdfDocument.insert(pdfPage!, at: i)
}
let pdfData = pdfDocument.dataRepresentation()
let activityViewController = UIActivityViewController(activityItems: [pdfData!], applicationActivities: nil)
present(activityViewController, animated: true, completion: nil)
return pdfData
}
Please do let me know if you need anything else....
Thanks in advance!
I am trying to create PDF from UITableView for sharing data to outside.
Problem is what the code I have used is working great on device version below iOS 13 but on version above 13 it only render data which is on screen.
Now I am expecting to render whole UITableView for version above 13.x.
If anyone have suggestions please let me know.
Thanks in advance :)
Here is my Code:
func createPDFDataFromTableView(tableView: UITableView) -> NSURL {
let priorBounds = tableView.bounds
let fittedSize = tableView.sizeThatFits(CGSize(width:priorBounds.size.width, height:tableView.contentSize.height))
tableView.bounds = CGRect(x:0, y:0, width:fittedSize.width, height:fittedSize.height)
let pdfPageBounds = CGRect(x:0, y:0, width:tableView.frame.width,height:tableView.contentSize.height) //height:self.view.frame.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds,nil)
var pageOriginY: CGFloat = 0
while pageOriginY < fittedSize.height {
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
UIGraphicsGetCurrentContext()!.saveGState()
UIGraphicsGetCurrentContext()!.translateBy(x: 0, y: -pageOriginY)
tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsGetCurrentContext()!.restoreGState()
pageOriginY += pdfPageBounds.size.height
print(pageOriginY)
}
UIGraphicsEndPDFContext()
tableView.bounds = priorBounds
//try saving in doc dir to confirm:
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
self.ticksnew = getCurrentDate()
if annDetail.announTitle != nil
{
FileNameToDelete=annDetail.announTitle
}
let fileName:String!="\(FileNameToDelete).pdf"
let path = dir?.appendingPathComponent(fileName!)
do {
try pdfData.write(to: path!, options: NSData.WritingOptions.atomic)
} catch {
print("error catched")
}
return path! as NSURL
}
I currently have a UITableViewController that displays information I would like to have the option of saving as a PDF and printing later.
Currently I have the following function, but I cannot figure out where it is saving the file, hw can I make this easily accessible from the Files App under Downloads:
func pdfDataWithTableView(tableView: UITableView) {
let priorBounds = tableView.bounds
let fittedSize = tableView.sizeThatFits(CGSize(width:priorBounds.size.width, height:tableView.contentSize.height))
tableView.bounds = CGRect(x:0, y:0, width:fittedSize.width, height:fittedSize.height)
let pdfPageBounds = CGRect(x:0, y:0, width:tableView.frame.width, height:self.view.frame.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds,nil)
var pageOriginY: CGFloat = 0
while pageOriginY < fittedSize.height {
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
UIGraphicsGetCurrentContext()!.saveGState()
UIGraphicsGetCurrentContext()!.translateBy(x: 0, y: -pageOriginY)
tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsGetCurrentContext()!.restoreGState()
pageOriginY += pdfPageBounds.size.height
}
UIGraphicsEndPDFContext()
tableView.bounds = priorBounds
var docURL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
docURL = docURL.appendingPathComponent("myDocument.pdf")
pdfData.write(to: docURL as URL, atomically: true)
}
I am creating a PDF and sharing it via UIActivityViewController.
func createFlyer() -> Data {
// 1
let pdfMetaData = [
kCGPDFContextCreator: "Flyer",
]
let format = UIGraphicsPDFRendererFormat()
format.documentInfo = pdfMetaData as [String: Any]
// 2
let pageWidth = 8.5 * 72.0
let pageHeight = 11 * 72.0
let pageRect = CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
// 3
let renderer = UIGraphicsPDFRenderer(bounds: pageRect, format: format)
// 4
let data = renderer.pdfData { (context) in
// 5
context.beginPage()
// 6
let attributes = [
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 72)
]
let text = "I'm a PDF!"
text.draw(at: CGPoint(x: 0, y: 0), withAttributes: attributes)
}
return data
}
func sharePDF () {
let activityViewController = UIActivityViewController(
activityItems: [data], applicationActivities: nil)
activityViewController.excludedActivityTypes = [UIActivity.ActivityType.saveToCameraRoll]
if let popoverPresentationController = activityViewController.popoverPresentationController {
popoverPresentationController.barButtonItem = self.sharePDFButton
}
self.present(activityViewController, animated: true, completion: {
})
}
Everything is fine, except for the file name is generated by the system ('PDF Document.pdf').
Is there a way to have a custom file name?
Please try to use below code while presenting activityViewController, I hope it works.
let fileManager = FileManager.default
let documentoPath = getDirectoryPath().appendingPathComponent(url.lastPathComponent) as URL
print("documentoPath :\(documentoPath)")
if fileManager.fileExists(atPath: documentoPath.path) {
DispatchQueue.main.async {
let activityViewController: UIActivityViewController = UIActivityViewController(activityItems: [documentoPath], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.downloadNavController?.present(activityViewController, animated: true, completion: { () in
UIBarButtonItem.appearance().tintColor = UIColor.systemBlue
UINavigationBar.appearance().barTintColor = UIColor.white
})
}
}
You can find getDirectoryPath() here:
func getDirectoryPath() -> URL {
var nestedFolderURL: URL?
do{
let rootFolderURL = try FileManager.default.url(
for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false
)
nestedFolderURL = rootFolderURL
if !FileManager.default.fileExists(atPath: nestedFolderURL!.relativePath) {
try FileManager.default.createDirectory(
at: nestedFolderURL!,
withIntermediateDirectories: false,
attributes: nil
)
}
return nestedFolderURL!
} catch (let error) {
print(error)
}
return nestedFolderURL!
}
I have create pdf of tableview all cells. but i have create the pdf it does not show all data of tableview & also when pdf page height set according to cells data. I dont know how to resolve this issue. I am using this code for create pdf.please help me.
let _: NSData!
do {
let priorBounds = table_view.bounds
let fittedSize = table_view.sizeThatFits(CGSizeMake(priorBounds.size.width, .infinity))
table_view.bounds = CGRectMake(0, 0, fittedSize.width, fittedSize.height)
let pdfPageBounds = CGRectMake(0, 0, 612, 800)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds, nil)
do {
var pageOriginY = 0
while pageOriginY < Int(fittedSize.height) {
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
CGContextSaveGState(UIGraphicsGetCurrentContext()!)
do {
CGContextTranslateCTM(UIGraphicsGetCurrentContext()!, 0, -CGFloat(pageOriginY))
table_view.layer.renderInContext(UIGraphicsGetCurrentContext()!)
}
CGContextRestoreGState(UIGraphicsGetCurrentContext()!)
pageOriginY += Int(pdfPageBounds.size.height)
}
}
UIGraphicsEndPDFContext()
table_view.bounds = priorBounds
if let documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
let documentsFileName = documentDirectories + "/" + (filename as String)
debugPrint(documentsFileName)
pdfData.writeToFile(documentsFileName, atomically: true)
}
}
Try this.
func createPdfFromTableView()
{
let priorBounds: CGRect = self.tblView.bounds
let fittedSize: CGSize = self.tblView.sizeThatFits(CGSize(width: priorBounds.size.width, height: self.tblView.contentSize.height))
self.tblView.bounds = CGRect(x: 0, y: 0, width: fittedSize.width, height: fittedSize.height)
self.tblView.reloadData()
let pdfPageBounds: CGRect = CGRect(x: 0, y: 0, width: fittedSize.width, height: (fittedSize.height))
let pdfData: NSMutableData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
self.tblView.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsEndPDFContext()
let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
let documentsFileName = documentDirectories! + "/" + "pdfName"
pdfData.write(toFile: documentsFileName, atomically: true)
print(documentsFileName)
}
One addition to Rajeskumar's answer below is as follows:
If your UITableView does not occupy the entire device screen with its original bounds, you might want to insert the following line:
self.tblview.bounds = priorBounds
after the render(in:) step. Otherwise, the table will be drawn on the device in a manner in which it will cover more space that originally intended.
Above answers did not work for creating PDF after tableview is scrolled at the bottom.
While reloading tableview(reloadData()) after scrolling, correct PDF is not generated and removing, it won't generate PDF for unloaded items.
So I have added extra condition and refactored as below:
extension UITableView {
func convertToPDF() -> Data? {
let priorBounds = self.bounds
setBoundsForAllItems()
self.layoutIfNeeded()
let pdfData = createPDF()
self.bounds = priorBounds
return pdfData.copy() as? Data
}
private func getContentFrame() -> CGRect {
return CGRect(x: 0, y: 0, width: self.contentSize.width, height: self.contentSize.height)
}
private func createPDF() -> NSMutableData {
let pdfPageBounds: CGRect = getContentFrame()
let pdfData: NSMutableData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageBounds, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageBounds, nil)
self.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsEndPDFContext()
return pdfData
}
private func setBoundsForAllItems() {
if self.isEndOfTheScroll() {
self.bounds = getContentFrame()
} else {
self.bounds = getContentFrame()
self.reloadData()
}
}
private func isEndOfTheScroll() -> Bool {
let contentYoffset = contentOffset.y
let distanceFromBottom = contentSize.height - contentYoffset
return distanceFromBottom < frame.size.height
}
}
Create PDF of tableview using this way
#IBAction func onClickBtnGeneratePDF(_ sender: Any) {
if enterPDFNAME.text?.isEmpty() == true{
Utility.showToast(message: "Please Enter PDF name")
}else{
self.viewOverlaySavePDF.isHidden = true
Utility.showLoading()
let pdfFilePath = tableview.exportAsPdfFromTable(pdfName: enterPDFNAME.text!)
if pdfFilePath != nil {
self.enterPDFNAME.text = ""
Utility.showToast(message: "Your PDF file saved")
Utility.hideLoading()
}
print(pdfFilePath)
}
}
}
extension UITableView {
// Export pdf from UITableView and save pdf in drectory and return pdf file path
func exportAsPdfFromTable(pdfName:String) -> String {
/* 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();
*/
let originalBounds = self.bounds
self.bounds = CGRect(x:originalBounds.origin.x, y: originalBounds.origin.y, width: self.contentSize.width, height: self.contentSize.height)
let pdfPageFrame = CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.contentSize.height)
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfPageFrame, nil)
UIGraphicsBeginPDFPageWithInfo(pdfPageFrame, nil)
guard let pdfContext = UIGraphicsGetCurrentContext() else { return "" }
self.layer.render(in: pdfContext)
UIGraphicsEndPDFContext()
self.bounds = originalBounds
// Save pdf data
return self.saveTablePdf(data: pdfData,name:pdfName)
}
// Save pdf file in document directory
func saveTablePdf(data: NSMutableData,name:String) -> String {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("\(name).pdf")
if data.write(to: pdfPath, atomically: true) {
return pdfPath.path
} else {
return ""
}
}
}
Only one solution for exporting all tableView items: make snapshot of every part of tableview as an image -> merge them into an imageView -> render imageView to pdf.
NOTE: It's the same way with UIScrollView & UICollectionView
func exportPDF() {
// 1. estimate tableview contentsize
UIGraphicsBeginImageContextWithOptions(tableView.contentSize, false, 0)
tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false)
tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
// 2. calculate number of part of tableview, loop and get snapshot
let iterationCount = Int(ceil(tableView.contentSize.height / tableView.bounds.size.height))
for i in 0..<iterationCount {
tableView.setContentOffset(CGPoint(x: 0, y: Int(tableView.bounds.size.height) * i), animated: false)
tableView.layer.render(in: UIGraphicsGetCurrentContext()!)
}
// 3. merge and make UIImageView from snapshot
let image = UIGraphicsGetImageFromCurrentImageContext()
let pdfImageView = UIImageView(image: image)
//4. render imageView to PDF
let pdfData: NSMutableData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, pdfImageView.bounds, nil)
UIGraphicsBeginPDFPageWithInfo(pdfImageView.bounds, nil)
pdfImageView.layer.render(in: UIGraphicsGetCurrentContext()!)
UIGraphicsEndPDFContext()
let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
let documentsFileName = documentDirectories! + "/" + "MyPDF.pdf"
pdfData.write(toFile: documentsFileName, atomically: true)
// 5. We have pdf file at documentsFileName
}