micah
micah

Reputation: 1218

Swift PDFkit put image in image field

I have a pdf that has an Image Field and in swift I have a UIImage, how do I draw the UIImage on the pdf where the Image Field annotation is?

Attempt 1

I have tried setting some fields on the annotation to the image by converting it to a base64 string with no luck.

let image: UIImage = UIImage.shapeImageWithBezierPath(bezierPath: path, fillColor: .white, strokeColor: .black)
let data = image.jpegData(compressionQuality: 0.5)
annotation.widgetStringValue = String(data?.base64EncodedString() ?? "")
annotation.contents = String(data?.base64EncodedString() ?? "")

Attempt 2

I also found this similar question Cannot add image and save to pdf file using PDFKit, but it creates a new annotation and places it on top of the page, while I need to place my image in or on top of an annotation on my PDF.

Using the above question I tried the following without any luck.

let image: UIImage = UIImage.shapeImageWithBezierPath(bezierPath: path, fillColor: .white, strokeColor: .black)
let pageBounds = page.bounds(for: .cropBox)
let imageBounds = CGRect(x: pageBounds.midX, y: pageBounds.midY, width: image.size.width, height: image.size.height)
let imageStamp = PDFImageAnnotation(image, bounds: imageBounds, properties: nil)
imageStamp.shouldDisplay = true
imageStamp.shouldPrint = true
annotation.page?.addAnnotation(imageStamp)

Attempt 3

I have also tried drawing the image onto the annotation like the following, but it doesn't appear on the pdf.

let image: UIImage = UIImage.shapeImageWithBezierPath(bezierPath: path, fillColor: .white, strokeColor: .black)
image.draw(in: annotation.accessibilityFrame)

Attempt 4

I discovered I can draw a UIBezierPath on my annotation with the following code, but my image has to be saved as base64 in my database which cannot be converted back to an UIBezierPath, so I still need to determine how to draw an image on an annotation.

annotation.type = "Ink"
annotation.color = UIColor(.black)
annotation.add(path) //path being my `UIBezierPath`

Thanks for any help!!!!

Upvotes: 1

Views: 784

Answers (1)

micah
micah

Reputation: 1218

I was able to draw my image inside the annotation image bounds and then replace the annotation by writing this class

final private class PDFImageAnnotation: PDFAnnotation {

    var image: UIImage?

    convenience init(image: UIImage?, properties: [AnyHashable: Any]?, rect: CGRect) {
        //set bounds & image
        self.init(bounds: rect, forType: .stamp, withProperties: properties)
        self.image = image
    }

    override func draw(with box: PDFDisplayBox, in context: CGContext) {
        //flip image context if upside down
        //context.translateBy(x: 0.0, y: 235) //y depends on image height, image will disappear (out of bounds) if not set correctly
        //context.scaleBy(x: 1.0, y: -1.0)

        //draw image
        guard let cgImage = image?.cgImage else { return }
        context.draw(cgImage, in: self.bounds)
    }
}

And calling this during my pdf generation

//make UIImage from base64
let uiImage = UIImage(data: Data(base64Encoded: String(data?.base64EncodedString() ?? ""))!)
//get image bounds from current pdf annotation
let imageBounds = CGRect(x: (annotation.startPoint.x * -1), y: (annotation.startPoint.y * -1), width: 100, height: 20)
//create image annotation with class
let imageStamp = PDFImageAnnotation(image: uiImage, properties: nil, rect: imageBounds)
//remove old annotation
page.removeAnnotation(annotation)
//add new annotation
imageStamp.fieldName = "CustomerSignature"
page.addAnnotation(imageStamp)

A better solution may be to write an extension class for PDFAnnotation

extension PDFAnnotation {
    func draw(with box: PDFDisplayBox, in context: CGContext, cgImage: CGImage) {
        //flip image context if upside down
        //context.translateBy(x: 0.0, y: 235) //y depends on image height, image will disappear (out of bounds) if not set correctly
        //context.scaleBy(x: 1.0, y: -1.0)
        context.scaleBy(x: 1.0, y: -1.0)
        context.draw(cgImage, in: self.bounds)
    }
}

But I am unsure how to pass in the CGContext. . .

annotation.draw(with: pdfView.displayBox, in ????, cgImage: cgImage)

Upvotes: 1

Related Questions