How properly to add a password to programmatically created PDF in Swift? - ios

Currently I'm creating my PDF in the next way:
func drawPDFUsingPrintPageRenderer(printPageRenderer: UIPrintPageRenderer) -> NSData! {
let data = NSMutableData()
UIGraphicsBeginPDFContextToData(data, CGRect(x: 0, y: 0, width: A4PageHeight, height: A4PageWidth), nil)
UIGraphicsBeginPDFPage()
printPageRenderer.drawPage(at: 0, in: UIGraphicsGetPDFContextBounds())
UIGraphicsEndPDFContext()
return data
}
by using Page Renderer:
let printPageRenderer = CustomPrintPageRenderer()
let printFormatter = UIMarkupTextPrintFormatter(markupText: self.getHTML())
printPageRenderer.addPrintFormatter(printFormatter, startingAtPageAt: 0)
and try to create the PDF from data as:
let pdfData = printPageRenderer.drawPDFUsingPrintPageRenderer(printPageRenderer: printPageRenderer)
mailComposer.addAttachmentData(pdfData as Data, mimeType: "application/pdf", fileName: "My PDF")
When I'm creating a new instance for my Page Renderer:
class CustomPrintPageRenderer: UIPrintPageRenderer {
let A4PageWidth: CGFloat = 595.2
let A4PageHeight: CGFloat = 841.8
var html: String?
override init() {
super.init()
// Specify the frame of the A4 page.
let pageFrame = CGRect(x: 0.0, y: 0.0, width: A4PageHeight, height: A4PageWidth)
// Set the page frame.
self.setValue(NSValue(cgRect: pageFrame), forKey: "paperRect")
// Set the horizontal and vertical insets (that's optional).
self.setValue(NSValue(cgRect: pageFrame), forKey: "printableRect")
self.setValue(1234, forKey: kCGPDFContextUserPassword as String)
self.setValue(1234, forKey: kCGPDFContextOwnerPassword as String)
}
I'm trying to set password to it via:
self.setValue(1234, forKey: kCGPDFContextUserPassword as String)
self.setValue(1234, forKey: kCGPDFContextOwnerPassword as String)
but when I run my code it crashes with the error:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason:
setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key kCGPDFContextUserPassword.'
What do I do wrong and how can I fix it? I was googling but could not find anything useful

According to the documentation for UIGraphicsBeginPDFContextToData, the last parameter is a dictionary that takes the same auxiliary keys as used by CGPDFContext.
kCGPDFContextUserPassword and kCGPDFContextOwnerPassword are among those auxiliary keys.
So it would seem you need to set the password in your call to UIGraphicsBeginPDFContextToData.
let info: [AnyHashable: Any] = [kCGPDFContextUserPassword as String : "1234", kCGPDFContextOwnerPassword as String : "1234"]
UIGraphicsBeginPDFContextToData(data, CGRect(x: 0, y: 0, width: A4PageHeight, height: A4PageWidth), info)

Related

Inserting an Image into a PDF with Swift

I want insert company logo image into pdf of every page. So we are referring following site
Tutorial Link
I want to insert logo right bottom page of pdf, I am unable do to that process.
Please find the tried both way code:
let documentURL = url
// Create a `CGPDFDocument` object for accessing the PDF pages.
// We need these pages in order to draw the original/existing content, because `UIGraphicsBeginPDFContextToFile` creates a file with a clean slate.
// We will have the original file contents in memory as long as the `CGPDFDocument` object is around, even after we have started rewriting the file at the path.
guard let originalDocument = CGPDFDocument(documentURL as CFURL) else {
print("Unable to create read document.")
return
}
// Create a new PDF at the same path to draw the contents into.
UIGraphicsBeginPDFContextToFile(documentURL.path, CGRect.zero, nil)
let image = UIImage(named: "inactive")!
guard let pdfContext = UIGraphicsGetCurrentContext() else {
print("Unable to access PDF Context.")
return
}
let pageSize = UIGraphicsGetPDFContextBounds().size
for pageIndex in 0..<originalDocument.numberOfPages {
// Mark the beginning of the page.
pdfContext.beginPDFPage(nil)
// Pages are numbered starting from 1.
// Access the `CGPDFPage` object with the original contents.
guard let currentPage = originalDocument.page(at: pageIndex + 1) else {
return
}
// Draw the existing page contents.
pdfContext.drawPDFPage(currentPage)
// Save the context state to restore after we are done drawing the image.
pdfContext.saveGState()
// Change the PDF context to match the UIKit coordinate system.
pdfContext.translateBy(x: 0, y: pageSize.height)
pdfContext.scaleBy(x: 1, y: -1)
// Location of the image to be drawn in UIKit coordinates.
let imagePosition = CGRect(x: 100, y: 0, width: 50, height: 50)
image.draw(in: imagePosition)
// UIColor.orange.set()
// UIRectFill(CGRect(x: 100, y: 0, width: 50, height: 50))
// Restoring the context back to its original state.
pdfContext.restoreGState()
// Mark the end of the current page.
pdfContext.endPDFPage()
}
// End the PDF context, essentially closing the PDF document context.
UIGraphicsEndPDFContext()
Updated Code:
let documentURL = url
guard let originalDocument = CGPDFDocument(documentURL as CFURL) else {
print("Unable to create read document.")
return
}
UIGraphicsBeginPDFContextToFile(documentURL.path, CGRect.zero, nil)
// let image = UIImage(named: "verified_kuwy")
let image = UIImage(named: "watermark")
guard let pdfContext = UIGraphicsGetCurrentContext() else {
print("Unable to access PDF Context.")
return
}
let pageSize = UIGraphicsGetPDFContextBounds().size
for pageIndex in 0..<originalDocument.numberOfPages {
pdfContext.beginPDFPage(nil)
guard let currentPage = originalDocument.page(at: pageIndex + 1) else {
return
}
pdfContext.drawPDFPage(currentPage)
pdfContext.saveGState()
pdfContext.translateBy(x: 0, y: pageSize.height)
pdfContext.scaleBy(x: 1, y: -1)
let imagePosition = CGRect(x: pageSize.width - 150, y: pageSize.height - 150, width: 100, height: 100)
image!.draw(in: imagePosition)
pdfContext.restoreGState()
pdfContext.endPDFPage()
}
// End the PDF context, essentially closing the PDF document context.
UIGraphicsEndPDFContext()

Swift iOS - Overlay text onto PDF with PDFKit and UI

I'm working in Swift 5 and on iOS. I'm trying to overlay text onto a current PDF I have. I'm essentially porting code I made from an app for macOS. This is the code from the Mac version:
func executeContext(at srcURL: URL, to dstURL: URL) {
// Confirm there is a document there
if let doc: PDFDocument = PDFDocument(url: srcURL) {
// Create a document, get the first page, and set the size of the page
let page: PDFPage = doc.page(at: 0)!
var mediaBox: CGRect = CGRect(x: 0, y: 0, width: 792, height: 612)
// This is where the magic happens. Create the drawing context on the PDF
let context = CGContext(dstURL as CFURL, mediaBox: &mediaBox, nil)
let graphicsContext = NSGraphicsContext(cgContext: context!, flipped: false)
NSGraphicsContext.current = graphicsContext
context!.beginPDFPage(nil)
// Draws the PDF into the context
page.draw(with: .mediaBox, to: context!)
// Parse and Draw Text on the context
//drawText()
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)
context!.saveGState()
context!.restoreGState()
context!.endPDFPage()
NSGraphicsContext.current = nil
context?.closePDF()
}
}
The drawText() function did most of the text overlaying that was needed, but I put another "draw "method below it to test it out.
I'm understandably getting an error Cannot find 'NSGraphicsContext' in scope since NSGraphicsContext doesn't exist on iOS. I've tried to find an equivalent translation with UIGraphicsPDFRenderer or UIGraphicsBeginPDFContextToData, and using some code from a Ray Wenderlich tutorial, I was able to create a new PDF and place text on it with the below code:
func createDocument(url: URL) -> Data {
//let pdfData = try? Data.init(contentsOf: url)
// 1
let pdfMetaData = [
kCGPDFContextCreator: "Timecard App",
kCGPDFContextAuthor: "Timecard App"
]
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
}
...but I couldn't find a way to load in current PDF "data" to the renderer and then draw from there. Does anyone have any suggestions on the proper way to do this?
Here is possible solution - actually you just need to operate with CoreGraphics context directly, set current, flip transform, etc. (style and conventions of original code preserved).
Tested with Xcode 12 / iOS 14.
func executeContext(at srcURL: URL, to dstURL: URL) {
// Confirm there is a document there
if let doc: PDFDocument = PDFDocument(url: srcURL) {
// Create a document, get the first page, and set the size of the page
let page: PDFPage = doc.page(at: 0)!
var mediaBox: CGRect = page.bounds(for: .mediaBox)
// This is where the magic happens. Create the drawing context on the PDF
let context = CGContext(dstURL as CFURL, mediaBox: &mediaBox, nil)
UIGraphicsPushContext(context!)
context!.beginPDFPage(nil)
// Draws the PDF into the context
page.draw(with: .mediaBox, to: context!)
let flipVertical: CGAffineTransform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: mediaBox.size.height)
context!.concatenate(flipVertical)
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)
context!.endPDFPage()
context?.closePDF()
UIGraphicsPopContext()
}
}
Edit additional pages using the following function
// add a new page
func addPage(number: Int){
// index is one less than document page number
let index = number - 1
context.endPDFPage()
if let page = document.page(at: index) {
context.beginPDFPage(nil)
page.draw(with: .mediaBox, to: context)
context.concatenate(flipVertical)
}
}
Where document is the PDF document you wish to edit.
Then start editing that new page. The X and Y coordinates reset to 0,0 again for the new page.

change the contrast and brightness of image using slider

I am trying to change the contrast and brightness of an image using the slider, and I have created a slider programatically. When I am trying to vary the contrast by the slider, am getting an error like
reason: '-[UISlider floatValue]: unrecognized selector sent to instance 0x103c4ffa0'`func viewforslide(){
vieew.frame = CGRect.init(x: 10, y:view.frame.size.height - 180, width: self.view.frame.size.width - 20, height: 40)
vieew.backgroundColor = UIColor.lightGray
vieew.layer.cornerRadius = vieew.frame.size.height/2
view.addSubview(vieew)
createslider()
}
func createslider(){
var sliderDemo = UISlider(frame:CGRect(x: 15, y: 5, width: vieew.frame.size.width - 30, height: 30))
sliderDemo.minimumValue = 0.0
sliderDemo.maximumValue = 1000.0
sliderDemo.isContinuous = true
sliderDemo.tintColor = UIColor.black
sliderDemo.value = 500.0
sliderDemo.addTarget(self, action: #selector(_sldComponentChangedValue),for: .valueChanged)
vieew.addSubview(sliderDemo)
}
#IBAction func _sldComponentChangedValue(sender: UISlider) {
// Set value to the nearest int
sender.setValue(Float(roundf(sender.value)), animated: false)
let newvalforslider = sender
print("\(newvalforslider)")
let displayinPercentage: Int = Int((sender.value/200) * 10000)
// contrastValueLabel.text = ("\(displayinPercentage)")
self.imageView.image = results.enhancedImage
let beginImage = CIImage(image: self.imageView.image!)
let filter = CIFilter(name: "CIColorControls")
filter?.setValue(beginImage, forKey: kCIInputImageKey)
filter?.setValue(sender.value, forKey: kCIInputContrastKey)
var filteredImage = filter?.outputImage
var context = CIContext(options: nil)
imageView.image = UIImage(cgImage: context.createCGImage(filteredImage!, from: (filteredImage?.extent)!)!)
var sliderValue = sender.value
}
`
If anyone helps me to do this, would be great. Thanks in advance.
While adding target method to sliderDemo you have given selector (_sldComponentChangedValue), which is not right, your actual method does receives an argument. That's the difference in methods name, so on slide you gets crash saying "unrecognized selector sent to instance"
instead do as follows
sliderDemo.addTarget(self, action: #selector(_sldComponentChangedValue(sender:)), for: .valueChanged);
#anisha If you complicate previous Ans. try this new code.
func increaseContrast(_ image: UIImage) -> UIImage {
let inputImage = CIImage(image: image)!
let parameters =
[
"inputContrast": NSNumber(value: 2) // set how many contrast you want
]
let outputImage = inputImage.applyingFilter("CIColorControls",
parameters: parameters)
let context = CIContext(options: nil)
let img = context.createCGImage(outputImage, from: outputImage.extent)!
return UIImage(cgImage: img)
}

Swift 4 Can you annotate a PDF with an image

I'm fairly new to Swift programming and I've created an app for work to simplify a task where I programmatically fill-in fields on an existing PDF. I've captured a signature as a UIImage and I'd like to add this to the PDF as an annotation like the rest of the fields. Is this possible?
// Annotate Signature
let signatureFieldBounds = CGRect(x:390, y:142, width:100, height:30)
let signatureField = PDFAnnotation(bounds: signatureFieldBounds, forType: .stamp, withProperties: nil)
signatureField.fieldName = FieldNames.signature.rawValue
signatureField.backgroundColor = UIColor.clear
sigImage?.draw(in: signatureFieldBounds)
page.addAnnotation(signatureField)
I've also tried: signatureField.stampName = sigImage as! UIImage instead of the draw function but this gives the error 'Cannot assign value of type 'UIImage' to type 'String?''
The screenshot shows what I get annotated:
Any help at all would be greatly appreciated!!
This was tricky for me too, but I figured it out.
Create a custom PDFAnnotation, then override the draw method to draw the image in the pdf context.
class ImageStampAnnotation: PDFAnnotation {
var image: UIImage!
// A custom init that sets the type to Stamp on default and assigns our Image variable
init(with image: UIImage!, forBounds bounds: CGRect, withProperties properties: [AnyHashable : Any]?) {
super.init(bounds: bounds, forType: PDFAnnotationSubtype.stamp, withProperties: properties)
self.image = image
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(with box: PDFDisplayBox, in context: CGContext) {
// Get the CGImage of our image
guard let cgImage = self.image.cgImage else { return }
// Draw our CGImage in the context of our PDFAnnotation bounds
context.draw(cgImage, in: self.bounds)
}
}
Then, just add it to the document
guard let signatureImage = signatureImage, let page = pdfContainerView.currentPage else { return }
let pageBounds = page.bounds(for: .cropBox)
let imageBounds = CGRect(x: pageBounds.midX, y: pageBounds.midY, width: 200, height: 100)
let imageStamp = ImageStampAnnotation(with: signatureImage, forBounds: imageBounds, withProperties: nil)
page.addAnnotation(imageStamp)
I wrote a medium article on how I did it, added a gesture recognizer to it and a canvas to grab a signature:
https://medium.com/#rajejones/add-a-signature-to-pdf-using-pdfkit-with-swift-7f13f7faad3e

How to get a value from NSValue in Swift?

Here's what I've tried so far
func onKeyboardRaise(notification: NSNotification) {
var notificationData = notification.userInfo
var duration = notificationData[UIKeyboardAnimationDurationUserInfoKey] as NSNumber
var frame = notificationData[UIKeyboardFrameBeginUserInfoKey]! as NSValue
var frameValue :UnsafePointer<(CGRect)> = nil;
frame.getValue(frameValue)
}
But I always seem to crash at frame.getValue(frameValue).
It's a little bit confusing because the documentation for UIKeyboardFrameBeginUserInfoKey says it returns a CGRect object, but when I log frame in the console, it states something like NSRect {{x, y}, {w, h}}.
getValue() must be called with a pointer to an (initialized) variable
of the appropriate size:
var frameValue = CGRect(x: 0, y: 0, width: 0, height: 0)
frame.getValue(&frameValue)
But it is simpler to use the convenience method:
let frameValue = frame.CGRectValue() // Swift 1, 2
let frameValue = frame.cgRectValue() // Swift 3

Resources