|
@@ -3,7 +3,8 @@ import PDFKit
|
|
|
import UniformTypeIdentifiers
|
|
import UniformTypeIdentifiers
|
|
|
|
|
|
|
|
enum PrintService {
|
|
enum PrintService {
|
|
|
- static func print(urls: [URL]) {
|
|
|
|
|
|
|
+ static func print(urls: [URL], from window: NSWindow? = nil) {
|
|
|
|
|
+ _ = window
|
|
|
guard !urls.isEmpty else { return }
|
|
guard !urls.isEmpty else { return }
|
|
|
|
|
|
|
|
for url in urls {
|
|
for url in urls {
|
|
@@ -16,72 +17,145 @@ enum PrintService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private static func printFile(at url: URL) {
|
|
private static func printFile(at url: URL) {
|
|
|
- let type = UTType(filenameExtension: url.pathExtension) ?? .data
|
|
|
|
|
|
|
+ let type = contentType(for: url) ?? UTType(filenameExtension: url.pathExtension) ?? .data
|
|
|
|
|
|
|
|
if type.conforms(to: .pdf), let document = PDFDocument(url: url) {
|
|
if type.conforms(to: .pdf), let document = PDFDocument(url: url) {
|
|
|
- printPDF(document)
|
|
|
|
|
|
|
+ printPDF(document, title: url.lastPathComponent)
|
|
|
} else if type.conforms(to: .image), let image = NSImage(contentsOf: url) {
|
|
} else if type.conforms(to: .image), let image = NSImage(contentsOf: url) {
|
|
|
- printImage(image)
|
|
|
|
|
|
|
+ printImage(image, title: url.lastPathComponent)
|
|
|
|
|
+ } else if let image = NSImage(contentsOf: url) {
|
|
|
|
|
+ printImage(image, title: url.lastPathComponent)
|
|
|
} else if type.conforms(to: .plainText) || type.conforms(to: .text),
|
|
} else if type.conforms(to: .plainText) || type.conforms(to: .text),
|
|
|
let text = try? String(contentsOf: url, encoding: .utf8) {
|
|
let text = try? String(contentsOf: url, encoding: .utf8) {
|
|
|
- printText(text)
|
|
|
|
|
|
|
+ printText(text, title: url.lastPathComponent)
|
|
|
} else if type.conforms(to: .rtf),
|
|
} else if type.conforms(to: .rtf),
|
|
|
let data = try? Data(contentsOf: url),
|
|
let data = try? Data(contentsOf: url),
|
|
|
let attributed = NSAttributedString(rtf: data, documentAttributes: nil) {
|
|
let attributed = NSAttributedString(rtf: data, documentAttributes: nil) {
|
|
|
- printAttributedText(attributed)
|
|
|
|
|
|
|
+ printAttributedText(attributed, title: url.lastPathComponent)
|
|
|
} else if type.conforms(to: .html),
|
|
} else if type.conforms(to: .html),
|
|
|
let data = try? Data(contentsOf: url),
|
|
let data = try? Data(contentsOf: url),
|
|
|
let attributed = NSAttributedString(html: data, documentAttributes: nil) {
|
|
let attributed = NSAttributedString(html: data, documentAttributes: nil) {
|
|
|
- printAttributedText(attributed)
|
|
|
|
|
|
|
+ printAttributedText(attributed, title: url.lastPathComponent)
|
|
|
} else {
|
|
} else {
|
|
|
showUnsupportedAlert(for: url)
|
|
showUnsupportedAlert(for: url)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static func printPDF(_ document: PDFDocument) {
|
|
|
|
|
|
|
+ private static func contentType(for url: URL) -> UTType? {
|
|
|
|
|
+ if let values = try? url.resourceValues(forKeys: [.contentTypeKey]),
|
|
|
|
|
+ let type = values.contentType {
|
|
|
|
|
+ return type
|
|
|
|
|
+ }
|
|
|
|
|
+ let ext = url.pathExtension.lowercased()
|
|
|
|
|
+ guard !ext.isEmpty else { return nil }
|
|
|
|
|
+ return UTType(filenameExtension: ext)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static func printPDF(_ document: PDFDocument, title: String) {
|
|
|
let printInfo = configuredPrintInfo()
|
|
let printInfo = configuredPrintInfo()
|
|
|
guard let operation = document.printOperation(
|
|
guard let operation = document.printOperation(
|
|
|
for: printInfo,
|
|
for: printInfo,
|
|
|
scalingMode: .pageScaleToFit,
|
|
scalingMode: .pageScaleToFit,
|
|
|
autoRotate: true
|
|
autoRotate: true
|
|
|
) else { return }
|
|
) else { return }
|
|
|
- operation.run()
|
|
|
|
|
|
|
+ runPrintOperation(operation, title: title)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static func printImage(_ image: NSImage) {
|
|
|
|
|
- let size = image.size
|
|
|
|
|
- guard size.width > 0, size.height > 0 else { return }
|
|
|
|
|
-
|
|
|
|
|
- let imageView = NSImageView(frame: NSRect(origin: .zero, size: size))
|
|
|
|
|
- imageView.image = image
|
|
|
|
|
- imageView.imageScaling = .scaleProportionallyUpOrDown
|
|
|
|
|
-
|
|
|
|
|
- let operation = NSPrintOperation(view: imageView, printInfo: configuredPrintInfo())
|
|
|
|
|
- operation.run()
|
|
|
|
|
|
|
+ private static func printImage(_ image: NSImage, title: String) {
|
|
|
|
|
+ guard let document = pdfDocument(from: image) else { return }
|
|
|
|
|
+ printPDF(document, title: title)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static func printText(_ text: String) {
|
|
|
|
|
- printAttributedText(NSAttributedString(string: text))
|
|
|
|
|
|
|
+ private static func printText(_ text: String, title: String) {
|
|
|
|
|
+ printAttributedText(NSAttributedString(string: text), title: title)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static func printAttributedText(_ text: NSAttributedString) {
|
|
|
|
|
- let textView = NSTextView(frame: NSRect(x: 0, y: 0, width: 612, height: 792))
|
|
|
|
|
|
|
+ private static func printAttributedText(_ text: NSAttributedString, title: String) {
|
|
|
|
|
+ let printInfo = configuredPrintInfo()
|
|
|
|
|
+ let pageSize = printInfo.paperSize
|
|
|
|
|
+ let textView = NSTextView(frame: NSRect(origin: .zero, size: pageSize))
|
|
|
textView.textStorage?.setAttributedString(text)
|
|
textView.textStorage?.setAttributedString(text)
|
|
|
textView.isEditable = false
|
|
textView.isEditable = false
|
|
|
|
|
|
|
|
- let operation = NSPrintOperation(view: textView, printInfo: configuredPrintInfo())
|
|
|
|
|
|
|
+ let operation = NSPrintOperation(view: textView, printInfo: printInfo)
|
|
|
|
|
+ runPrintOperation(operation, title: title)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static func runPrintOperation(_ operation: NSPrintOperation, title: String) {
|
|
|
|
|
+ operation.showsPrintPanel = true
|
|
|
|
|
+ operation.showsProgressPanel = true
|
|
|
|
|
+ operation.jobTitle = title
|
|
|
|
|
+ operation.printPanel.options.insert([
|
|
|
|
|
+ .showsCopies,
|
|
|
|
|
+ .showsPageRange,
|
|
|
|
|
+ .showsPaperSize,
|
|
|
|
|
+ .showsOrientation,
|
|
|
|
|
+ .showsScaling,
|
|
|
|
|
+ ])
|
|
|
operation.run()
|
|
operation.run()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private static func configuredPrintInfo() -> NSPrintInfo {
|
|
private static func configuredPrintInfo() -> NSPrintInfo {
|
|
|
let printInfo = NSPrintInfo.shared.copy() as! NSPrintInfo
|
|
let printInfo = NSPrintInfo.shared.copy() as! NSPrintInfo
|
|
|
|
|
+
|
|
|
let printerName = AppSettings.effectiveDefaultPrinter
|
|
let printerName = AppSettings.effectiveDefaultPrinter
|
|
|
if let printer = NSPrinter(name: printerName) {
|
|
if let printer = NSPrinter(name: printerName) {
|
|
|
printInfo.printer = printer
|
|
printInfo.printer = printer
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ switch AppSettings.defaultPaperSize {
|
|
|
|
|
+ case .a4: printInfo.paperName = NSPrinter.PaperName("iso-a4")
|
|
|
|
|
+ case .letter: printInfo.paperName = NSPrinter.PaperName("na-letter")
|
|
|
|
|
+ case .legal: printInfo.paperName = NSPrinter.PaperName("na-legal")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ printInfo.horizontalPagination = .fit
|
|
|
|
|
+ printInfo.verticalPagination = .fit
|
|
|
|
|
+ printInfo.isHorizontallyCentered = true
|
|
|
|
|
+ printInfo.isVerticallyCentered = true
|
|
|
|
|
+ printInfo.topMargin = 36
|
|
|
|
|
+ printInfo.bottomMargin = 36
|
|
|
|
|
+ printInfo.leftMargin = 36
|
|
|
|
|
+ printInfo.rightMargin = 36
|
|
|
|
|
+
|
|
|
return printInfo
|
|
return printInfo
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private static func pdfDocument(from image: NSImage) -> PDFDocument? {
|
|
|
|
|
+ let printInfo = configuredPrintInfo()
|
|
|
|
|
+ let pageSize = printInfo.paperSize
|
|
|
|
|
+ guard pageSize.width > 0, pageSize.height > 0 else { return nil }
|
|
|
|
|
+
|
|
|
|
|
+ let pdfData = NSMutableData()
|
|
|
|
|
+ var mediaBox = CGRect(origin: .zero, size: pageSize)
|
|
|
|
|
+
|
|
|
|
|
+ guard let consumer = CGDataConsumer(data: pdfData as CFMutableData),
|
|
|
|
|
+ let context = CGContext(consumer: consumer, mediaBox: &mediaBox, nil) else {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ context.beginPDFPage(nil)
|
|
|
|
|
+
|
|
|
|
|
+ let imageSize = image.size
|
|
|
|
|
+ if imageSize.width > 0, imageSize.height > 0,
|
|
|
|
|
+ let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) {
|
|
|
|
|
+ let scale = min(pageSize.width / imageSize.width, pageSize.height / imageSize.height)
|
|
|
|
|
+ let drawSize = CGSize(width: imageSize.width * scale, height: imageSize.height * scale)
|
|
|
|
|
+ let drawRect = CGRect(
|
|
|
|
|
+ x: (pageSize.width - drawSize.width) / 2,
|
|
|
|
|
+ y: (pageSize.height - drawSize.height) / 2,
|
|
|
|
|
+ width: drawSize.width,
|
|
|
|
|
+ height: drawSize.height
|
|
|
|
|
+ )
|
|
|
|
|
+ context.draw(cgImage, in: drawRect)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ context.endPDFPage()
|
|
|
|
|
+ context.closePDF()
|
|
|
|
|
+
|
|
|
|
|
+ return PDFDocument(data: pdfData as Data)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private static func showUnsupportedAlert(for url: URL) {
|
|
private static func showUnsupportedAlert(for url: URL) {
|
|
|
let alert = NSAlert()
|
|
let alert = NSAlert()
|
|
|
alert.messageText = "Unsupported File"
|
|
alert.messageText = "Unsupported File"
|