I want to create multipage PDF of self.view. I have following viewController with tableView now I want to create the pdf of the view.
This is what I have done but it's create single page pdf only
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("myPassBook.pdf")
pdfData.write(to: docURL as URL, atomically: true)
share()
}
On save Button Call Below function
func createImg() -> UIImage {
guard tblView.numberOfSections > 0, tblView.numberOfRows(inSection: 0) > 0 else {
let errorImage = UIImage(named: "Error Image")
return errorImage!
}
UIGraphicsBeginImageContextWithOptions(CGSize(width: tblView.contentSize.width, height: tblView.contentSize.height), false, 0.0)
let context = UIGraphicsGetCurrentContext()
let previousFrame = tblView.frame
tblView.frame = CGRect(x: tblView.frame.origin.x, y: tblView.frame.origin.y, width: tblView.contentSize.width, height: tblView.contentSize.height)
// Draw view in that context
tblView.layer.render(in: context!)
tblView.frame = previousFrame
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
createPDFDataFromImage(image: image)
return image;
}
Create PDF from image and Share
func createPDFDataFromImage(image: UIImage){
let pdfData = NSMutableData()
let imgView = UIImageView.init(image: image)
let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
UIGraphicsBeginPDFContextToData(pdfData, imageRect, nil)
UIGraphicsBeginPDFPage()
let context = UIGraphicsGetCurrentContext()
imgView.layer.render(in: context!)
UIGraphicsEndPDFContext()
//try saving in doc dir to confirm:
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
let path = dir?.appendingPathComponent("file.pdf")
do {
try pdfData.write(to: path!, options: NSData.WritingOptions.atomic)
} catch {
print("error catched")
}
let activityViewController = UIActivityViewController(activityItems: [pdfData] , applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
}
Related
I got an AsyncImage and want to use UIImageWriteToSavedPhotosAlbum for save my AsyncImage to user library.
I used Paul Hudson course for find what to do but it downloads an empty white picture on library on my iPhone and on simulator.
Here's the code:
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .blue
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}`
On the view:
`Button("Save to image") {
let image = AstronomyImageView(astronomy: article) //AsyncImage
let uiImage: UIImage = image.snapshot()
UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil)
}```
I'm overlaying a CGImage on a video through AVMutableComposition and that works great (see Screenshot A below). The problem is when I export the video with AVAssetExportSession, I get some weird outlines and colors (see Screenshot B). How do I export this video retaining the proper colors of the CGImage and without the odd yellow/black outlines?
Here's the code I'm using to export:
func saveVideo(composition: AVMutableComposition, vidComp: AVMutableVideoComposition, audioMix: AVMutableAudioMix) {
let preset = "AVAssetExportPreset1920x1080"
guard let exportSession = AVAssetExportSession(asset: composition, presetName: preset) else {
return
}
let videoSize: CGSize = vidComp.renderSize
let parentLayer = CALayer()
parentLayer.isGeometryFlipped = true
parentLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)
let videoLayer = CALayer()
videoLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)
parentLayer.addSublayer(videoLayer)
let url = URL(fileURLWithPath: Bundle.main.path(forResource: "Fire1", ofType: "mov")!)
let asset = AVURLAsset(url: url)
let generator = AVAssetImageGenerator(asset: asset)
generator.appliesPreferredTrackTransform = true
generator.requestedTimeToleranceBefore = .zero
generator.requestedTimeToleranceAfter = .zero
do {
let cgImg = try generator.copyCGImage(at: CMTime(seconds: 0.5, preferredTimescale: 1000), actualTime: nil)
let imgLayer = CALayer()
imgLayer.contentsGravity = .resizeAspect
imgLayer.contents = cgImg
imgLayer.frame = parentLayer.bounds
parentLayer.addSublayer(imgLayer)
} catch {
print("No thumbnail")
}
vidComp.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer)
exportSession.videoComposition = vidComp
exportSession.audioMix = audioMix
exportSession.outputURL = self.uniqueURLForSave()
exportSession.outputFileType = AVFileType.mov
exportSession.shouldOptimizeForNetworkUse = true
exportSession.exportAsynchronously {
switch exportSession.status {
case .completed:
UISaveVideoAtPathToSavedPhotosAlbum(exportSession.outputURL!.relativePath, self, nil, nil)
default:
print("Error")
}
}
}
Screenshot A (proper image):
Screenshot B (faulty image with yellow and black outlines):
I want to generate pdf from textview after user enters the data. The textview contains paragraphs and attributed texts.
I have Followed
this procedure
and my code is:
func createPdf() {
createPDFNamed("test")
}
func getPDFPath(_ name: String) -> String {
let newPDFName = "\(name).pdf"
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
let pdfPath: String = (documentsDirectory as String).appending(newPDFName);
print(pdfPath)
return pdfPath
}
func createPDFNamed(_ name: String) {
let text = myTextView.text
let currentText: CFAttributedString = CFAttributedStringCreate(nil, (text as CFString?), nil)
if currentText != nil {
let framesetter: CTFramesetter = CTFramesetterCreateWithAttributedString(currentText)
if framesetter != nil {
let pdfFileName: String = getPDFPath(name)
UIGraphicsBeginPDFContextToFile(pdfFileName, CGRect.zero, nil)
var currentRange: CFRange! = CFRangeMake(0, 0)
var currentPage: Int = 0
var done = false
repeat {
UIGraphicsBeginPDFPageWithInfo(CGRect(x: 0, y: 0, width: 612, height: 792), nil)
currentPage += 1
drawPageNbr(currentPage)
currentRange = renderPagewithTextRange(currentRange, andFramesetter: framesetter)
if currentRange.location == CFAttributedStringGetLength((currentText as? CFAttributedString)) {
done = true
}
} while !done
UIGraphicsEndPDFContext()
}
else {
print("Could not create the framesetter needed to lay out the atrributed string.")
}
}
else {
print("Could not create the attributed string for the framesetter")
}
}
func renderPagewithTextRange(_ currentRange: CFRange, andFramesetter framesetter: CTFramesetter) -> CFRange {
var currentRange: CFRange! = CFRangeMake(0, 0)
let currentContext: CGContext? = UIGraphicsGetCurrentContext()
currentContext?.textMatrix = .identity
let frameRect = CGRect(x: 72, y: 72, width: 468, height: 648)
let framePath: CGMutablePath = CGMutablePath()
framePath.addRect(frameRect, transform: .identity)
let frameRef: CTFrame = CTFramesetterCreateFrame(framesetter, currentRange, framePath, nil)
currentContext?.translateBy(x: 0, y: 792)
currentContext?.scaleBy(x: 1.0, y: -1.0)
CTFrameDraw(frameRef, currentContext!)
currentRange = CTFrameGetVisibleStringRange(frameRef)
currentRange.location += currentRange.length
currentRange.length = 0 as? CFIndex ?? CFIndex()
return currentRange
}
func drawPageNumber(_ pageNum: Int) {
let pageString = "Page \(Int(pageNum))"
let theFont = UIFont.systemFont(ofSize: 12)
let pageStringSize: CGSize = pageString.size(withAttributes: [.font: UIFont.systemFont(ofSize: 17.0)])
let stringRect = CGRect(x: ((612.0 - pageStringSize.width) / 2.0), y: 720.0 + ((72.0 - pageStringSize.height) / 2.0), width: pageStringSize.width, height: pageStringSize.height)
let paragraphStyle = NSParagraphStyle.default.mutableCopy() as? NSMutableParagraphStyle
paragraphStyle?.lineBreakMode = .byTruncatingTail
paragraphStyle?.alignment = .right
let attributes = [NSAttributedStringKey.font: theFont, .paragraphStyle: paragraphStyle as Any] as [NSAttributedStringKey : Any]
pageString.draw(in: stringRect, withAttributes: attributes)
}
The pdf generates and never ends even it went upto 2Gb file size. But not opening. Could you please advice where I am doing mistake to finish the pdf generation? Also after pdf creation, would like to send by mail
Thanks in advance
EDIT:
If I change the code to while done it stops but prints one page only. If I change to while !done it goes on generating pdf. Past two days searching and not end up with any solution. Also the pdf prints black only. I want to reflect the attributes to pdf. Any suggestions.
if (currentRange.location == CFAttributedStringGetLength(currentText)){
done = true
}
} while done
UIGraphicsEndPDFContext()
}
else {
print("Could not create the framesetter needed to lay out the atrributed string.")
}
}
else {
print("Could not create the attributed string for the framesetter")
}
}
I have the following objective C Code to write text onto an Image. I'm new to Swift. How can I do this in Swift?
float width = 10.0;
float height = 10.0;
NSImage *finalImage = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
// obtain images - your sources may vary
NSImage *overlay = [[NSImage alloc] initWithData:[NSData dataWithContentsOfFile:#"/path/to/overlay_image.jpg"]];
NSImage *mainImage = [[NSImage alloc] initWithData:[NSData dataWithContentsOfFile:#"/path/to/main_image.jpg"]];
[finalImage lockFocus];
// draw the base image
[mainImage drawInRect:NSMakeRect(0, 0, width, height)
fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
// draw the overlay image at some offset point
[overlay drawInRect:NSMakeRect(10, 10, [overlay size].width, [overlay size].height)
fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
[finalImage unlockFocus];
NSData *finalData = [finalImage TIFFRepresentation];
[[[NSBitmapImageRep imageRepWithData:finalData] representationUsingType:NSJPEGFileType properties:nil] writeToFile:[NSString stringWithFormat:#"/path/to/folder/new_image.jpg"] atomically:YES];
UPDATE:
I have the following method to draw a string onto an Image..But it when I use this method I get cropped out image portions..Seems something is wrong with it..Please advice.
func drawText(image :NSImage) ->NSImage
{
let text = "Sample Text"
let font = NSFont.boldSystemFont(ofSize: 18)
let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
let textRect = CGRect(x: 5, y: 5, width: image.size.width - 5, height: image.size.height - 5)
let textStyle = NSMutableParagraphStyle.default().mutableCopy() as! NSMutableParagraphStyle
let textFontAttributes = [
NSFontAttributeName: font,
NSForegroundColorAttributeName: NSColor.white,
NSParagraphStyleAttributeName: textStyle
]
let im:NSImage = NSImage(size: image.size)
let rep:NSBitmapImageRep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(image.size.width), pixelsHigh: Int(image.size.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSCalibratedRGBColorSpace, bytesPerRow: 0, bitsPerPixel: 0)!
im.addRepresentation(rep)
im.lockFocus()
image.draw(in: imageRect)
text.draw(in: textRect, withAttributes: textFontAttributes)
im.unlockFocus()
return im
}
Here's this code in Swift 3
let width: CGFloat = 10.0
let height: CGFloat = 10.0
let finalImage = NSImage(size: NSMakeSize(width, height))
// obtain images - your sources may vary
var overlay: NSImage?
var mainImage: NSImage?
if let url = URL(string: "/path/to/overlay_image.jpg") {
do {
let data = try Data(contentsOf: url)
overlay = NSImage(data: data)
} catch {
print("Unable to get data")
}
}
if let url = URL(string: "/path/to/main_image.jpg") {
do {
let data = try Data(contentsOf: url)
mainImage = NSImage(data: data)
} catch {
print("Unable to get data")
}
}
finalImage.lockFocus()
// draw the base image
mainImage?.draw(in: NSMakeRect(0, 0, width, height), from: NSZeroRect, operation: NSCompositingOperation.sourceOver, fraction: 1.0)
// draw the overlay image at some offset point
if overlay != nil {
overlay?.draw(in: NSMakeRect(10, 10, overlay!.size.width, overlay!.size.height), from: NSZeroRect, operation: NSCompositingOperation.sourceOver, fraction: 1.0)
}
finalImage.unlockFocus()
if let finalData = finalImage.tiffRepresentation, let url = URL(string: "/path/to/folder/new_image.jpg") {
do {
try NSBitmapImageRep(data: finalData)?.representation(using: NSJPEGFileType, properties: [:])?.write(to: url)
} catch {
print("Failed to write")
}
}
I want just a specific part of my captured image to be cropped in a circular shape right before it's viewed.
Here is the code for capturing still images:
#IBAction func takePhoto(sender: UIButton) {
self.stillImageOutput!.captureStillImageAsynchronouslyFromConnection(self.stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo)) { (buffer:CMSampleBuffer!, error:NSError!) -> Void in
var image = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer)
var data_image = UIImage(data: image)
self.previewImage.image = data_image
}
}
Thank you
if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer) {
if let image = UIImage(data: imageData) {
let square = image.size.width < image.size.height ? CGSize(width: image.size.width, height: image.size.width) : CGSize(width: image.size.height, height: image.size.height)
let imageView = UIImageView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: square))
imageView.contentMode = UIViewContentMode.ScaleAspectFill
imageView.image = image
imageView.layer.cornerRadius = square.width/2
imageView.layer.masksToBounds = true
UIGraphicsBeginImageContext(imageView.bounds.size)
imageView.layer.renderInContext(UIGraphicsGetCurrentContext())
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.previewImage.image = result
}
}