Stopping NSPrintOperation From Crashing Application - swift

I have the following lines of code to print the webView content.
let directoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let printOpts: NSDictionary = [NSPrintJobDisposition: NSPrintSaveJob, NSPrintSaveJob: directoryURL, NSPrintScalingFactor: 2.0]
let printInfo = NSPrintInfo(dictionary: printOpts as! [String : AnyObject])
printInfo.horizontalPagination = .autoPagination
printInfo.verticalPagination = .autoPagination
let printOperation = NSPrintOperation(view: webView.mainFrame.frameView, printInfo: printInfo)
printOperation.printPanel.options = [.showsOrientation, .showsPaperSize, .showsPreview, .showsPageSetupAccessory, .showsScaling, .showsPrintSelection]
printOperation.run()
The problem is that it sometimes causes the application to crash when the user interacts with the print operation panel.
If I just click on the cancel button, the application can crash. And if I try to set a different scale rate, it can sometimes crash. I don't know why the application will crash. It's always the last line. What am I doing wrong? Thanks.

There's a bug in the print panel code when .showsCopies is not in the options. It has a use-after-free bug with the Copies-related subviews. It removes them from the view hierarchy, allowing them to be deallocated. However, it keeps a dangling reference to the text field and tries to operate on it in various circumstances. For example, it sometimes tries to make it the first responder.
You basically should just never try to suppress the Copies field.

my code for print a pdf from URL for OS X:
let printInfo = NSPrintInfo.shared
let manager = FileManager.default
do{
let directoryURL = try manager.url(for: .documentDirectory, in:.userDomainMask, appropriateFor:nil, create:true)
let docURL = NSURL(string:"XX.pdf", relativeTo:directoryURL)
let pdfDoc = PDFDocument.init(url: docURL! as URL)
let page = CGRect(x: 0, y: 0, width: 595.2, height: 1841.8) // A4, 72 dpi
let pdfView : PDFView = PDFView.init(frame: page)
pdfView.document = pdfDoc
let operation: NSPrintOperation = NSPrintOperation(view: pdfView, printInfo: printInfo)
operation.printPanel.options.insert(NSPrintPanel.Options.showsPaperSize)
operation.printPanel.options.insert(NSPrintPanel.Options.showsOrientation)
operation.run()
}catch{
}

Related

Swift: Loss of resolution when redrawing PDF

I'm trying to use a PDF file (contains a form with table for several 'records') as a template.
My idea is to read in the original file as a template, and then use Swifts drawing features to insert the text at the relevant positions before saving as a new pdf file and printing.
My problem is that i'm seeing a small loss of resolution (fonts are slightly wooly, gridlines are no longer crisp) when re-saving the output.
I've tried two approaches, the first with my 'template' as a file in the project, and the second with it as an Asset (Scales: Single Scale, Resizing: Preserve Vector Data).
Here is my code:
func createCompletedForm(records: [MyDataObject]) -> URL? {
let directoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = URL(fileURLWithPath: "pdfToPrint", relativeTo: directoryURL).appendingPathExtension("pdf")
guard let templateUrl = Bundle.main.url(forResource: "MyPdfTemplate", withExtension: "pdf") else { return nil }
guard let document = CGPDFDocument(templateUrl as CFURL) else { return nil }
guard let page = document.page(at: 1) else { return nil }
let pageTemplate = page.getBoxRect(.mediaBox)
UIGraphicsBeginPDFContextToFile(fileURL.path, pageTemplate, nil)
guard let pdfContext = UIGraphicsGetCurrentContext() else {
print("Unable to access PDF Context.")
return nil
}
// Mark the beginning of the page.
pdfContext.beginPDFPage(nil)
// 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: StandardPageDimensions.ISO216_A4.height)
pdfContext.scaleBy(x: 1, y: -1)
// Option 1: Draw PDF from a file added to my project
DrawingHelper.drawPDFfromCGPDF(page: page, drawingArea: pageTemplate)
// Option 2: Draw PDF from Assets
//let baseTemplate = UIImage(named: "MyPdfTemplate")
//baseTemplate?.draw(at: CGPoint(x: 0, y: 0))
// Draw the records over the template - NOT the source of the problem, happens even when commented out
//addRecordsToTemplate(records: records)
// Restoring the context back to its original state.
pdfContext.restoreGState()
// Mark the end of the current page.
pdfContext.endPDFPage()
UIGraphicsEndPDFContext()
// Useful to find and open the file produced on the simulator
print("pdf created at : \(fileURL.path)")
return fileURL
}
// And the drawing function from my helper class
static func drawPDFfromCGPDF(page: CGPDFPage, drawingArea: CGRect) {
let renderer = UIGraphicsImageRenderer(size: drawingArea.size)
let img = renderer.image { ctx in
UIColor.white.set()
ctx.fill(drawingArea)
ctx.cgContext.translateBy(x: 0.0, y: drawingArea.size.height)
ctx.cgContext.scaleBy(x: 1.0, y: -1.0)
ctx.cgContext.drawPDFPage(page)
}
img.draw(at: CGPoint(x: 0, y: 0))
}

Swift 4: PHAsset to Image reduces quality

I'm using a framework called OpalImagePicker, it allows me to pick several images instead of one. It returns an array of PHAssets.
I want to get these PHAssets, turn them into image, then convert them to base64 string so that i can send them to my data base.
But there's a problem: the images have really low quality when I try to get them from the PHAsset array.
Here's my code:
let requestOptions = PHImageRequestOptions()
requestOptions.version = .current
requestOptions.deliveryMode = .opportunistic
requestOptions.resizeMode = .exact
requestOptions.isNetworkAccessAllowed = true
let imagePicker = OpalImagePickerController()
imagePicker.maximumSelectionsAllowed = 4
imagePicker.allowedMediaTypes = Set([PHAssetMediaType.image])
self.presentOpalImagePickerController(imagePicker, animated: true,
select: { (assets) in
for a in assets{
// print(a)
// self.img.append(a.image)
self.img.append(a.imagehd(targetSize: CGSize(width: a.pixelWidth, height: a.pixelHeight), contentMode: PHImageContentMode.aspectFill, options: requestOptions))
and the function:
func imagehd(targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?) -> UIImage {
var thumbnail = UIImage()
let imageManager = PHCachingImageManager()
imageManager.requestImage(for: self, targetSize: targetSize, contentMode: contentMode, options: options, resultHandler: { image, _ in
thumbnail = image!
})
return thumbnail
}
I tried to give "request options.version" the ".original" value, or even high quality to delivery Mode, but then it just gives me nothing (image is nil)
I'm really lost. Can someone help?
Thanks a lot.

How to Save PDF (and print) from UITableView (or cell) easy way?

I read and search a lot on internet and stack, but I have a problem
I've used this extension
extension UITableView {
// Export pdf from UITableView and save pdf in drectory and return pdf file path
func exportAsPdfFromTable() -> String {
self.showsVerticalScrollIndicator = false
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)
}
// Save pdf file in document directory
func saveTablePdf(data: NSMutableData) -> String {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("myPDF.pdf")
if data.write(to: pdfPath, atomically: true) {
return pdfPath.path
} else {
return ""
}
}
}
Then i save the path in this way
myPdfPath = self.tableView.exportAsPdfFromTable()
I tried several ways to show the pdf:
- UIActivityViewController (with this work but not well)
- WKWebView
- SFSafariViewController (only http / https, path file not work)
- UIDocumentInteractionController (not show preview and not work)
The extension used to save the pdf does not work well (not fill print preview page). Generate a 125MB pdf with a title I don't want.
Is there an easy way to do this?
1) TableView (generate pdf on a white background (always even if the tableview is red for example)
2) Print the generated pdf.
Thanks

How do I write image to file?

I am using Swift 3, Xcode 8.2, iOS 10
For debugging purposes, I am trying to take an image, crop it and show what the cropped image looks like. I am trying to write this image to my computer (Mac OS X) desktop but all the tutorials I've found has me writing this to my documents directory which I incorrectly thought would actually be a directory on my computer but turns out it's this location on the phone itself that I can't figure out how to access.
I have this so far:
let finalImage : UIImage
let crop_section = CGRect(x: 0.0, y: 0.0, width: 1000.0, height: 1000.0)
let cg_image = screenshot.cgImage?.cropping(to: crop_section)
finalImage = UIImage(cgImage: cg_image!)
let documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
// create a name for your image
print(documentsDirectoryURL)
let fileURL = documentsDirectoryURL.appendingPathComponent("cropped.png")
if !FileManager.default.fileExists(atPath: fileURL.path) {
do {
try UIImagePNGRepresentation(finalImage)!.write(to: fileURL)
print("Image Added Successfully")
} catch {
print(error)
}
} else {
print("Image Not Added")
}
I want to replace documentDirectory with something like ../Desktop/cropped.png but I can't figure out how to do this. Any help would be greatly appreciated!
I want to replace documentDirectory with something like ../Desktop/cropped.png
You can't. This is an iOS program. An iOS app is sandboxed within its own set of directories on the iOS device / simulator; it cannot reach out and see the Mac OS X desktop. They are two different universes.

NSPrintOperation—save to location without prompt

I have a GitHub repository that allows the user to save a paginated PDF on Mac from some HTML by loading it into a WebView and using an NSPrintOperation (specifically, an NSPrintSaveJob with showsPrintPanel set to false) lets the user save that PDF to any location on their Mac with a save panel similar to the default NSSavePanel. However, I'm experimenting with the code and I'd like to instead save the created PDF to a particular folder (/Users/owlswipe/Downloads/) without the save panel.
My code to save a PDF from a WebView (with a save panel) is currently this:
let printOpts: [String : AnyObject] = [NSPrintJobDisposition:NSPrintSaveJob as AnyObject]
let printInfo: NSPrintInfo = NSPrintInfo(dictionary: printOpts)
printInfo.paperSize = NSMakeSize(595.22, 841.85)
let printOp: NSPrintOperation = NSPrintOperation(view: webView.mainFrame.frameView.documentView, printInfo: printInfo)
printOp.showsPrintPanel = false
printOp.showsProgressPanel = false
printOp.run()
How can I adapt that code to save the PDF to a preset folder instead of to the user's choice of folders from a save panel?
This piece of code is working for me in Swift 4/Cocoa to accomplish what you want, however there's a bit more code in there as it is rendering the contents of a WKWebView into 8.5" x 11" pages for a PDF.
So for your app, there would be appropriate adjustments for your content stream / print object, but the configuration of the print operation will be the same to get the "no dialog" results you're wanting.
However, to test it out you could just dump your string into the webview and use the code as is. Generated files appear in the root of your "user/documents" directory.
static func createPDF(htmlString: String, streamId: String = "someStream") {
let webView = WebView()
webView.mainFrame.loadHTMLString(htmlString, baseURL: nil)
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
let directoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directoryURLStr = directoryURL.absoluteString+"\(streamId).pdf"
let outputFilePath = URL(string: directoryURLStr)
let printOpts: [NSPrintInfo.AttributeKey : Any] = [
NSPrintInfo.AttributeKey.jobDisposition : NSPrintInfo.JobDisposition.save,
NSPrintInfo.AttributeKey.jobSavingURL : outputFilePath
]
let printInfo: NSPrintInfo = NSPrintInfo(dictionary: printOpts)
let baseMargin: CGFloat = 9.0; // .125"
printInfo.paperSize = NSMakeSize(612, 792); // 8.5" x 11/2"
printInfo.topMargin = baseMargin
printInfo.leftMargin = baseMargin
printInfo.rightMargin = baseMargin
printInfo.bottomMargin = baseMargin
let printOp: NSPrintOperation = NSPrintOperation(view: webView.mainFrame.frameView.documentView, printInfo: printInfo)
printOp.showsPrintPanel = false
printOp.showsProgressPanel = false
printOp.run()
Swift.print("Document complete: \(outputFilePath!.absoluteString)")
}
}