|
|
@@ -3,7 +3,8 @@ import PDFKit
|
|
3
|
3
|
import UniformTypeIdentifiers
|
|
4
|
4
|
|
|
5
|
5
|
enum PrintService {
|
|
6
|
|
- static func print(urls: [URL]) {
|
|
|
6
|
+ static func print(urls: [URL], from window: NSWindow? = nil) {
|
|
|
7
|
+ _ = window
|
|
7
|
8
|
guard !urls.isEmpty else { return }
|
|
8
|
9
|
|
|
9
|
10
|
for url in urls {
|
|
|
@@ -16,72 +17,145 @@ enum PrintService {
|
|
16
|
17
|
}
|
|
17
|
18
|
|
|
18
|
19
|
private static func printFile(at url: URL) {
|
|
19
|
|
- let type = UTType(filenameExtension: url.pathExtension) ?? .data
|
|
|
20
|
+ let type = contentType(for: url) ?? UTType(filenameExtension: url.pathExtension) ?? .data
|
|
20
|
21
|
|
|
21
|
22
|
if type.conforms(to: .pdf), let document = PDFDocument(url: url) {
|
|
22
|
|
- printPDF(document)
|
|
|
23
|
+ printPDF(document, title: url.lastPathComponent)
|
|
23
|
24
|
} else if type.conforms(to: .image), let image = NSImage(contentsOf: url) {
|
|
24
|
|
- printImage(image)
|
|
|
25
|
+ printImage(image, title: url.lastPathComponent)
|
|
|
26
|
+ } else if let image = NSImage(contentsOf: url) {
|
|
|
27
|
+ printImage(image, title: url.lastPathComponent)
|
|
25
|
28
|
} else if type.conforms(to: .plainText) || type.conforms(to: .text),
|
|
26
|
29
|
let text = try? String(contentsOf: url, encoding: .utf8) {
|
|
27
|
|
- printText(text)
|
|
|
30
|
+ printText(text, title: url.lastPathComponent)
|
|
28
|
31
|
} else if type.conforms(to: .rtf),
|
|
29
|
32
|
let data = try? Data(contentsOf: url),
|
|
30
|
33
|
let attributed = NSAttributedString(rtf: data, documentAttributes: nil) {
|
|
31
|
|
- printAttributedText(attributed)
|
|
|
34
|
+ printAttributedText(attributed, title: url.lastPathComponent)
|
|
32
|
35
|
} else if type.conforms(to: .html),
|
|
33
|
36
|
let data = try? Data(contentsOf: url),
|
|
34
|
37
|
let attributed = NSAttributedString(html: data, documentAttributes: nil) {
|
|
35
|
|
- printAttributedText(attributed)
|
|
|
38
|
+ printAttributedText(attributed, title: url.lastPathComponent)
|
|
36
|
39
|
} else {
|
|
37
|
40
|
showUnsupportedAlert(for: url)
|
|
38
|
41
|
}
|
|
39
|
42
|
}
|
|
40
|
43
|
|
|
41
|
|
- private static func printPDF(_ document: PDFDocument) {
|
|
|
44
|
+ private static func contentType(for url: URL) -> UTType? {
|
|
|
45
|
+ if let values = try? url.resourceValues(forKeys: [.contentTypeKey]),
|
|
|
46
|
+ let type = values.contentType {
|
|
|
47
|
+ return type
|
|
|
48
|
+ }
|
|
|
49
|
+ let ext = url.pathExtension.lowercased()
|
|
|
50
|
+ guard !ext.isEmpty else { return nil }
|
|
|
51
|
+ return UTType(filenameExtension: ext)
|
|
|
52
|
+ }
|
|
|
53
|
+
|
|
|
54
|
+ private static func printPDF(_ document: PDFDocument, title: String) {
|
|
42
|
55
|
let printInfo = configuredPrintInfo()
|
|
43
|
56
|
guard let operation = document.printOperation(
|
|
44
|
57
|
for: printInfo,
|
|
45
|
58
|
scalingMode: .pageScaleToFit,
|
|
46
|
59
|
autoRotate: true
|
|
47
|
60
|
) else { return }
|
|
48
|
|
- operation.run()
|
|
|
61
|
+ runPrintOperation(operation, title: title)
|
|
49
|
62
|
}
|
|
50
|
63
|
|
|
51
|
|
- private static func printImage(_ image: NSImage) {
|
|
52
|
|
- let size = image.size
|
|
53
|
|
- guard size.width > 0, size.height > 0 else { return }
|
|
54
|
|
-
|
|
55
|
|
- let imageView = NSImageView(frame: NSRect(origin: .zero, size: size))
|
|
56
|
|
- imageView.image = image
|
|
57
|
|
- imageView.imageScaling = .scaleProportionallyUpOrDown
|
|
58
|
|
-
|
|
59
|
|
- let operation = NSPrintOperation(view: imageView, printInfo: configuredPrintInfo())
|
|
60
|
|
- operation.run()
|
|
|
64
|
+ private static func printImage(_ image: NSImage, title: String) {
|
|
|
65
|
+ guard let document = pdfDocument(from: image) else { return }
|
|
|
66
|
+ printPDF(document, title: title)
|
|
61
|
67
|
}
|
|
62
|
68
|
|
|
63
|
|
- private static func printText(_ text: String) {
|
|
64
|
|
- printAttributedText(NSAttributedString(string: text))
|
|
|
69
|
+ private static func printText(_ text: String, title: String) {
|
|
|
70
|
+ printAttributedText(NSAttributedString(string: text), title: title)
|
|
65
|
71
|
}
|
|
66
|
72
|
|
|
67
|
|
- private static func printAttributedText(_ text: NSAttributedString) {
|
|
68
|
|
- let textView = NSTextView(frame: NSRect(x: 0, y: 0, width: 612, height: 792))
|
|
|
73
|
+ private static func printAttributedText(_ text: NSAttributedString, title: String) {
|
|
|
74
|
+ let printInfo = configuredPrintInfo()
|
|
|
75
|
+ let pageSize = printInfo.paperSize
|
|
|
76
|
+ let textView = NSTextView(frame: NSRect(origin: .zero, size: pageSize))
|
|
69
|
77
|
textView.textStorage?.setAttributedString(text)
|
|
70
|
78
|
textView.isEditable = false
|
|
71
|
79
|
|
|
72
|
|
- let operation = NSPrintOperation(view: textView, printInfo: configuredPrintInfo())
|
|
|
80
|
+ let operation = NSPrintOperation(view: textView, printInfo: printInfo)
|
|
|
81
|
+ runPrintOperation(operation, title: title)
|
|
|
82
|
+ }
|
|
|
83
|
+
|
|
|
84
|
+ private static func runPrintOperation(_ operation: NSPrintOperation, title: String) {
|
|
|
85
|
+ operation.showsPrintPanel = true
|
|
|
86
|
+ operation.showsProgressPanel = true
|
|
|
87
|
+ operation.jobTitle = title
|
|
|
88
|
+ operation.printPanel.options.insert([
|
|
|
89
|
+ .showsCopies,
|
|
|
90
|
+ .showsPageRange,
|
|
|
91
|
+ .showsPaperSize,
|
|
|
92
|
+ .showsOrientation,
|
|
|
93
|
+ .showsScaling,
|
|
|
94
|
+ ])
|
|
73
|
95
|
operation.run()
|
|
74
|
96
|
}
|
|
75
|
97
|
|
|
76
|
98
|
private static func configuredPrintInfo() -> NSPrintInfo {
|
|
77
|
99
|
let printInfo = NSPrintInfo.shared.copy() as! NSPrintInfo
|
|
|
100
|
+
|
|
78
|
101
|
let printerName = AppSettings.effectiveDefaultPrinter
|
|
79
|
102
|
if let printer = NSPrinter(name: printerName) {
|
|
80
|
103
|
printInfo.printer = printer
|
|
81
|
104
|
}
|
|
|
105
|
+
|
|
|
106
|
+ switch AppSettings.defaultPaperSize {
|
|
|
107
|
+ case .a4: printInfo.paperName = NSPrinter.PaperName("iso-a4")
|
|
|
108
|
+ case .letter: printInfo.paperName = NSPrinter.PaperName("na-letter")
|
|
|
109
|
+ case .legal: printInfo.paperName = NSPrinter.PaperName("na-legal")
|
|
|
110
|
+ }
|
|
|
111
|
+
|
|
|
112
|
+ printInfo.horizontalPagination = .fit
|
|
|
113
|
+ printInfo.verticalPagination = .fit
|
|
|
114
|
+ printInfo.isHorizontallyCentered = true
|
|
|
115
|
+ printInfo.isVerticallyCentered = true
|
|
|
116
|
+ printInfo.topMargin = 36
|
|
|
117
|
+ printInfo.bottomMargin = 36
|
|
|
118
|
+ printInfo.leftMargin = 36
|
|
|
119
|
+ printInfo.rightMargin = 36
|
|
|
120
|
+
|
|
82
|
121
|
return printInfo
|
|
83
|
122
|
}
|
|
84
|
123
|
|
|
|
124
|
+ private static func pdfDocument(from image: NSImage) -> PDFDocument? {
|
|
|
125
|
+ let printInfo = configuredPrintInfo()
|
|
|
126
|
+ let pageSize = printInfo.paperSize
|
|
|
127
|
+ guard pageSize.width > 0, pageSize.height > 0 else { return nil }
|
|
|
128
|
+
|
|
|
129
|
+ let pdfData = NSMutableData()
|
|
|
130
|
+ var mediaBox = CGRect(origin: .zero, size: pageSize)
|
|
|
131
|
+
|
|
|
132
|
+ guard let consumer = CGDataConsumer(data: pdfData as CFMutableData),
|
|
|
133
|
+ let context = CGContext(consumer: consumer, mediaBox: &mediaBox, nil) else {
|
|
|
134
|
+ return nil
|
|
|
135
|
+ }
|
|
|
136
|
+
|
|
|
137
|
+ context.beginPDFPage(nil)
|
|
|
138
|
+
|
|
|
139
|
+ let imageSize = image.size
|
|
|
140
|
+ if imageSize.width > 0, imageSize.height > 0,
|
|
|
141
|
+ let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) {
|
|
|
142
|
+ let scale = min(pageSize.width / imageSize.width, pageSize.height / imageSize.height)
|
|
|
143
|
+ let drawSize = CGSize(width: imageSize.width * scale, height: imageSize.height * scale)
|
|
|
144
|
+ let drawRect = CGRect(
|
|
|
145
|
+ x: (pageSize.width - drawSize.width) / 2,
|
|
|
146
|
+ y: (pageSize.height - drawSize.height) / 2,
|
|
|
147
|
+ width: drawSize.width,
|
|
|
148
|
+ height: drawSize.height
|
|
|
149
|
+ )
|
|
|
150
|
+ context.draw(cgImage, in: drawRect)
|
|
|
151
|
+ }
|
|
|
152
|
+
|
|
|
153
|
+ context.endPDFPage()
|
|
|
154
|
+ context.closePDF()
|
|
|
155
|
+
|
|
|
156
|
+ return PDFDocument(data: pdfData as Data)
|
|
|
157
|
+ }
|
|
|
158
|
+
|
|
85
|
159
|
private static func showUnsupportedAlert(for url: URL) {
|
|
86
|
160
|
let alert = NSAlert()
|
|
87
|
161
|
alert.messageText = "Unsupported File"
|