Swift PDF create Rectangle? - ios

I would like to create an rectangle on an PDF File, but i dont how to do that. Its simple to create some text or images, but i would like to add some shapes like rectangles and circles.
Is the UIGraphicsBeginPDFPageWithInfo the same as the CGContext?
At the moment i am using this:
UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);
// add some text
let mainTitle = "..."
mainTitle.drawInRect(CGRectMake(30, 110, 552, 40), withAttributes: textAttributesBoldLargeHeader)
But how to add a custom rectangle?

Just get the PDF drawing context with UIGraphicsGetCurrentContext()
and draw anything to it. Simple example:
UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612, 792), nil);
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, UIColor.blueColor().CGColor)
let rect = CGRect(x: 10, y: 10, width: 100, height: 200)
CGContextFillRect(context, rect)
UIGraphicsEndPDFContext()

You can also extend UIView to render the current context and save the data as pdf file:
Swift 3 or later
extension UIView {
var pdfData: Data {
let result = NSMutableData()
UIGraphicsBeginPDFContextToData(result, frame, nil)
guard let context = UIGraphicsGetCurrentContext() else { return result as Data }
UIGraphicsBeginPDFPage()
layer.render(in: context)
UIGraphicsEndPDFContext()
return result as Data
}
}
Testing:
class ViewController: UIViewController {
let rectangle = UIBezierPath(rect: CGRect(x: 30, y: 110, width: 350, height: 40))
let shapeLayer = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
shapeLayer.path = rectangle.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.red.cgColor
view.layer.addSublayer(shapeLayer)
do {
try view.pdfData.write(to: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("view.pdf"), options: .atomic)
} catch {
print(error)
}
}
}

Related

How to Cut image from path in swift

I am trying to crop my image using UIBezierPath (the user draw the path) I want the path area to be removed from the UIImage, I have this code implementation but the cut is not working in the path area
let originalImage = treaCameraImage!
UIGraphicsBeginImageContext(originalImage.size)
originalImage.draw(at: .zero)
let context = UIGraphicsGetCurrentContext()
context!.addPath(path.cgPath)
context!.clip()
context!.setFillColor(UIColor.red.cgColor)
context!.clear(CGRect(x: 0, y: 0, width: originalImage.size.width, height: originalImage.size.width))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
imageCut.image = newImage
Well for the solution you need first add this code in the viewDidAppear
override func viewDidAppear(_ animated: Bool) {
//Implemented to overcome trackerview problem(UIBreizerPath is not correct) when working direcly on an image bigger than the screen(For brackets cut image).
if (self.treaCameraImage!.size.width != self.view.bounds.size.width || self.treaCameraImage!.size.height != self.view.bounds.size.width) {
UIGraphicsBeginImageContext(self.imageCut.bounds.size);
let context = UIGraphicsGetCurrentContext()
if let context = context {
imageCut.layer.render(in: context)
}
let screenShot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
self.treaCameraImage = screenShot;
//self.tempOriginalImage = screenShot;
}
}
The treaCameraImage is the default image and imageCut is the image container
In my method to cut the image i changed:
context!.clear(CGRect(x: 0, y: 0, width: originalImage.size.width, height: originalImage.size.width))
to this:
context!.clear(CGRect(x: 0, y: 0, width: originalImage.size.width * 2, height: originalImage.size.width * 2))
and that works for me!

Swift PDFKit change background color

I'd like to change the background color of a pdf with apples PDFKit framework in Swift, but it doesn't work. It's not a problem to create some controls like a text or image and then use it but I want to change the color of the document itself.
Does anyone have any idea or solution?
let renderer = UIGraphicsPDFRenderer(bounds: pageRect, format: format)
let data = renderer.pdfData { (context) in
context.beginPage()
let attributes = [
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 12),
NSAttributedString.Key.backgroundColor : UIColor.green
]
let text = "My pdf"
text.draw(in: CGRect(x: 0, y: 0, width: 200, height: 20), withAttributes: attributes)
//?
UIColor.blue.setFill()
UIColor.blue.setStroke()
//?
context.cgContext.setFillColor(cyan: 1.0, magenta: 1.0, yellow: 0.6, black: 1.0, alpha: 1.0)
}
return data
Use this
let currentContext = UIGraphicsGetCurrentContext()
currentContext?.setFillColor(UIColor.blue.cgColor)
currentContext?.fill(CGRect(x: x, y: y, width: Width, height: Height))
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(currentContext, [UIColor blueColor].CGColor );
CGContextFillRect(currentContext, CGRectMake(0, 110.5, pageSize.width, pageSize.height));
for PDFKit
import PDFKit
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// load pdf file
let fileUrl = URL(fileURLWithPath: Bundle.main.path(forResource: "test", ofType: "pdf")!)
let document = PDFDocument(url: fileUrl)
document?.delegate = self
// if you want save
let tempPath = NSTemporaryDirectory() + "test.pdf"
document?.write(toFile: tempPath)
}
}
extension TestViewController: PDFDocumentDelegate {
func classForPage() -> AnyClass {
return TestPage.self
}
}
class TestPage: PDFPage {
override func draw(with box: PDFDisplayBox, to context: CGContext) {
// Draw original content
super.draw(with: box, to: context)
// get page bounds
let pageBounds = bounds(for: box)
// change backgroud color
context.setFillColor(UIColor.systemPink.cgColor)
// set blend mode
context.setBlendMode(.multiply)
var rect = pageBounds
// you need to switch the width and height for horizontal pages
// the coordinate system for the context is the lower left corner
if rotation == 90 || rotation == 270 {
rect = CGRect(origin: .zero, size: CGSize(width: pageBounds.height, height: pageBounds.width))
}
context.fill(rect)
}
}

PDF Reader night mode

I am working on a pdf reader but I am facing problem in creating night mode for a PDF Document. I have successfully created the new PDF Document with Black background and white foreground. but can not do for an already created document I am doing for new PDF Document is
func show(text:NSAttributedString) {
// let attributedString = NSAttributedString(string: text, attributes: [NSAttributedString.Key.foregroundColor: UIColor.white])
// text.attribute(NSAttributedString.Key.foregroundColor, at: 0, effectiveRange: NSRange(location: 0, length: text.length))
let printFormatter = UISimpleTextPrintFormatter(attributedText: text)
let renderer = UIPrintPageRenderer()
renderer.addPrintFormatter(printFormatter, startingAtPageAt: 0)
// A4 size
let pageSize = CGSize(width: 595.2, height: 841.8)
// Use this to get US Letter size instead
// let pageSize = CGSize(width: 612, height: 792)
// create some sensible margins
let pageMargins = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72)
// calculate the printable rect from the above two
let printableRect = CGRect(x: pageMargins.left, y: pageMargins.top, width: pageSize.width - pageMargins.left - pageMargins.right, height: pageSize.height - pageMargins.top - pageMargins.bottom)
// and here's the overall paper rectangle
let paperRect = CGRect(x: 0, y: 0, width: pageSize.width, height: pageSize.height)
renderer.setValue(NSValue(cgRect: paperRect), forKey: "paperRect")
renderer.setValue(NSValue(cgRect: printableRect), forKey: "printableRect")
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, paperRect, nil)
renderer.prepare(forDrawingPages: NSMakeRange(0, renderer.numberOfPages))
let bounds = UIGraphicsGetPDFContextBounds()
for i in 0 ..< renderer.numberOfPages {
UIGraphicsBeginPDFPage()
let currentContext = UIGraphicsGetCurrentContext()
currentContext?.setFillColor(UIColor.yellow.cgColor)
currentContext?.fill(bounds)
renderer.drawPage(at: i, in: bounds)
}
UIGraphicsEndPDFContext()
var pdfUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
pdfUrl?.appendPathComponent("text")
pdfUrl?.appendPathExtension("pdf")
do {
try pdfData.write(to: pdfUrl!)
} catch {
print(error.localizedDescription)
}
let pdfdocemrnt = PDFDocument(url: pdfUrl!)
self.baseView.document = pdfdocemrnt!
}
Please help if someone knows the issue.

How do I set the line width of a UIBezierPath when drawing to a CGContext?

I'm trying to create a UIImage using a provided UIBezierPath. Unfortunately, no matter what I set setLineWidth to, the result is always a stroke of 1 point:
extension UIBezierPath {
func image(fillColor: UIColor, strokeColor: UIColor) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, 1.0)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.setLineWidth(10)
context.setFillColor(fillColor.cgColor)
context.setStrokeColor(strokeColor.cgColor)
self.fill()
self.stroke()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
Trying this out in a test project with a circle, for example:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView()
imageView.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
view.addSubview(imageView)
let bezierPath = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 100, height: 100))
let image = bezierPath.image(fillColor: UIColor.blue, strokeColor: UIColor.red)
imageView.image = image
}
}
No matter what I set setLineWidth to, it always appears to be 1 point.
You are calling stroke on the UIBezierPath, so you need to set the lineWidth property of that using self.lineWidth = 10.
extension UIBezierPath {
func image(fillColor: UIColor, strokeColor: UIColor) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, 1.0)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.setFillColor(fillColor.cgColor)
self.lineWidth = 10
context.setStrokeColor(strokeColor.cgColor)
self.fill()
self.stroke()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}

How to get a screenshot of the View (Drawing View) used for drawing using UIBezeir path

I have a Drawing View which is on a Scroll View. After drawing is completed I need a screenshot of the drawing which I will be uploading to the server.
I am using UIBezeir path to draw on the view.
let path = UIBezierPath()
for i in self.drawView.path{
path.append(i)
}
self.drawView.path is an NSArray with all the bezeir paths of the drawing.
But when I use the bounding box of this path and get max and min values of coordinates and try to capture a screenshot I get this
var rect:CGRect = CGRect(x: path.bounds.minX, y: path.bounds.minY, width: path.bounds.maxX, height: path.bounds.maxY)
I also tried to give the bounds of the path itself
let rect:CGRect = CGRect(x: path.bounds.origin.x - 5, y: path.bounds.origin.y - 5, width: path.bounds.size.width + 5, height: path.bounds.size.height + 5)
Just for reference I tried using this rect and create a view (clear color with border layer) and placed it over the Drawing, it work pretty fine but when I try to capture an image it goes out of bounds
This is the function I am using to capture the screen
func imgScreenShot(bounds:CGRect) -> UIImage{
let rect: CGRect = bounds
self.drawView.isOpaque = false
self.drawView.backgroundColor = UIColor.clear
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
var context: CGContext? = UIGraphicsGetCurrentContext()
if let aContext = context {
self.drawView.layer.render(in: aContext)
}
var capturedImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//let finalImage = scaleImage(image: capturedImage)
return capturedImage!
}
I am also tried getting a UIView with this function
let vw = self.drawView.resizableSnapshotView(from: rect, afterScreenUpdates: true, withCapInsets: UIEdgeInsets.zero)
This gives me a perfect UIView with the drawing in that, but again when I try to convert the UIView to UIImage using the function giving the views bounds, I get a blank image.
Can anyone suggest what I am doing wrong or any other solution for how I can get this, bounds of image starting right exactly at the bounds of the drawing
let vw = self.drawView.resizableSnapshotView(from: rect2, afterScreenUpdates: true, withCapInsets: UIEdgeInsets.zero)
vw?.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
vw?.layer.borderColor = UIColor.red.cgColor
vw?.layer.borderWidth = 1
self.drawView.addSubview(vw!)
let image = vw?.snapshotImage
let imgView = UIImageView(frame: CGRect(x: 250, y: 50, width: 100, height: 100))
imgView.layer.borderColor = UIColor.gray.cgColor
imgView.layer.borderWidth = 1
self.drawView.addSubview(imgView)
Make an extension of UIView and UIImage , so in whole application lifecycle you can use those methods(which one i will be describe at below) for capture the screenshort of any perticular UIView and resize the existing image(if needed).
Here is the extension of UIView :-
extension UIView {
var snapshotImage : UIImage? {
var snapShotImage:UIImage?
UIGraphicsBeginImageContext(self.frame.size)
if let context = UIGraphicsGetCurrentContext() {
self.layer.render(in: context)
if let image = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
snapShotImage = image
}
}
return snapShotImage
}
}
Here is the extension of UIImage :-
extension UIImage {
func resizeImage(newSize:CGSize) -> UIImage? {
var newImage:UIImage?
let horizontalRatio = newSize.width / size.width
let verticalRatio = newSize.height / size.height
let ratio = max(horizontalRatio, verticalRatio)
let newSize = CGSize(width: size.width * ratio, height: size.height * ratio)
UIGraphicsBeginImageContext(newSize)
if let _ = UIGraphicsGetCurrentContext() {
draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: newSize))
if let image = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
newImage = image
}
}
return newImage
}
}
How to use those functions in our desired class ?
if let snapImage = yourUIView.snapshotImage {
///... snapImage is the desired image you want and its dataType is `UIImage`.
///... Now resize the snapImage into desired size by using this one
if let resizableImage = snapImage.resizeImage(newSize: CGSize(width: 150.0, height: 150.0)) {
print(resizableImage)
}
}
here yourUIView means , the one you have taken for drawing some inputs. it can be IBOutlet as well as your UIView (which you have taken programmatically)

Resources