|
@@ -5,12 +5,12 @@ import UniformTypeIdentifiers
|
|
|
|
|
|
|
|
enum PrintService {
|
|
enum PrintService {
|
|
|
@MainActor
|
|
@MainActor
|
|
|
- private static func requirePremiumPrint(from window: NSWindow?) -> Bool {
|
|
|
|
|
- PremiumAccess.require(feature: .printText, from: window ?? NSApp.keyWindow)
|
|
|
|
|
|
|
+ private static func requirePremium(_ feature: AppFeature, from window: NSWindow?) -> Bool {
|
|
|
|
|
+ PremiumAccess.require(feature: feature, from: window ?? NSApp.keyWindow)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static func printContacts(_ contacts: [PrintableContact], from window: NSWindow? = nil) {
|
|
static func printContacts(_ contacts: [PrintableContact], from window: NSWindow? = nil) {
|
|
|
- guard requirePremiumPrint(from: window) else { return }
|
|
|
|
|
|
|
+ guard requirePremium(.printContacts, from: window) else { return }
|
|
|
guard !contacts.isEmpty else {
|
|
guard !contacts.isEmpty else {
|
|
|
showNoContactsAlert()
|
|
showNoContactsAlert()
|
|
|
return
|
|
return
|
|
@@ -24,7 +24,7 @@ enum PrintService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static func printCanvas(_ image: NSImage, from window: NSWindow? = nil) {
|
|
static func printCanvas(_ image: NSImage, from window: NSWindow? = nil) {
|
|
|
- guard requirePremiumPrint(from: window) else { return }
|
|
|
|
|
|
|
+ guard requirePremium(.drawPrint, from: window) else { return }
|
|
|
guard image.size.width > 0, image.size.height > 0 else {
|
|
guard image.size.width > 0, image.size.height > 0 else {
|
|
|
showEmptyCanvasAlert()
|
|
showEmptyCanvasAlert()
|
|
|
return
|
|
return
|
|
@@ -33,7 +33,7 @@ enum PrintService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static func printPhoto(_ image: NSImage, title: String = "Photo", from window: NSWindow? = nil) {
|
|
static func printPhoto(_ image: NSImage, title: String = "Photo", from window: NSWindow? = nil) {
|
|
|
- guard requirePremiumPrint(from: window) else { return }
|
|
|
|
|
|
|
+ guard requirePremium(.photoPreview, from: window) else { return }
|
|
|
guard image.size.width > 0, image.size.height > 0 else {
|
|
guard image.size.width > 0, image.size.height > 0 else {
|
|
|
showPrintFailedAlert()
|
|
showPrintFailedAlert()
|
|
|
return
|
|
return
|
|
@@ -41,7 +41,8 @@ enum PrintService {
|
|
|
printImage(image, title: title, from: window)
|
|
printImage(image, title: title, from: window)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- static func saveCanvasPDF(_ image: NSImage) {
|
|
|
|
|
|
|
+ static func saveCanvasPDF(_ image: NSImage, from window: NSWindow? = nil) {
|
|
|
|
|
+ guard requirePremium(.drawPrint, from: window) else { return }
|
|
|
guard image.size.width > 0, image.size.height > 0 else {
|
|
guard image.size.width > 0, image.size.height > 0 else {
|
|
|
showEmptyCanvasAlert()
|
|
showEmptyCanvasAlert()
|
|
|
return
|
|
return
|
|
@@ -67,7 +68,7 @@ enum PrintService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static func printText(_ text: String, title: String = "Print Text", from window: NSWindow? = nil) {
|
|
static func printText(_ text: String, title: String = "Print Text", from window: NSWindow? = nil) {
|
|
|
- guard requirePremiumPrint(from: window) else { return }
|
|
|
|
|
|
|
+ guard requirePremium(.printText, from: window) else { return }
|
|
|
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
guard !trimmed.isEmpty else {
|
|
guard !trimmed.isEmpty else {
|
|
|
showEmptyTextAlert()
|
|
showEmptyTextAlert()
|
|
@@ -85,6 +86,7 @@ enum PrintService {
|
|
|
defaultName: String = "Scan",
|
|
defaultName: String = "Scan",
|
|
|
from window: NSWindow? = nil
|
|
from window: NSWindow? = nil
|
|
|
) {
|
|
) {
|
|
|
|
|
+ guard requirePremium(.scannerScan, from: window) else { return }
|
|
|
guard hasPrintableContent(attributed) else {
|
|
guard hasPrintableContent(attributed) else {
|
|
|
showEmptyTextAlert()
|
|
showEmptyTextAlert()
|
|
|
return
|
|
return
|
|
@@ -114,7 +116,7 @@ enum PrintService {
|
|
|
title: String = "Scan",
|
|
title: String = "Scan",
|
|
|
from window: NSWindow? = nil
|
|
from window: NSWindow? = nil
|
|
|
) {
|
|
) {
|
|
|
- guard requirePremiumPrint(from: window) else { return }
|
|
|
|
|
|
|
+ guard requirePremium(.scannerScan, from: window) else { return }
|
|
|
guard hasPrintableContent(attributed) else {
|
|
guard hasPrintableContent(attributed) else {
|
|
|
showEmptyTextAlert()
|
|
showEmptyTextAlert()
|
|
|
return
|
|
return
|
|
@@ -131,6 +133,7 @@ enum PrintService {
|
|
|
defaultName: String = "Scan",
|
|
defaultName: String = "Scan",
|
|
|
from window: NSWindow? = nil
|
|
from window: NSWindow? = nil
|
|
|
) {
|
|
) {
|
|
|
|
|
+ guard requirePremium(.scannerScan, from: window) else { return }
|
|
|
guard !pages.isEmpty else {
|
|
guard !pages.isEmpty else {
|
|
|
showEmptyTextAlert()
|
|
showEmptyTextAlert()
|
|
|
return
|
|
return
|
|
@@ -160,7 +163,7 @@ enum PrintService {
|
|
|
title: String = "Scan",
|
|
title: String = "Scan",
|
|
|
from window: NSWindow? = nil
|
|
from window: NSWindow? = nil
|
|
|
) {
|
|
) {
|
|
|
- guard requirePremiumPrint(from: window) else { return }
|
|
|
|
|
|
|
+ guard requirePremium(.scannerScan, from: window) else { return }
|
|
|
guard !pages.isEmpty else {
|
|
guard !pages.isEmpty else {
|
|
|
showEmptyTextAlert()
|
|
showEmptyTextAlert()
|
|
|
return
|
|
return
|
|
@@ -177,6 +180,7 @@ enum PrintService {
|
|
|
defaultName: String = "Scan",
|
|
defaultName: String = "Scan",
|
|
|
from window: NSWindow? = nil
|
|
from window: NSWindow? = nil
|
|
|
) {
|
|
) {
|
|
|
|
|
+ guard requirePremium(.printText, from: window) else { return }
|
|
|
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
guard !trimmed.isEmpty else {
|
|
guard !trimmed.isEmpty else {
|
|
|
showEmptyTextAlert()
|
|
showEmptyTextAlert()
|
|
@@ -203,7 +207,7 @@ enum PrintService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static func print(urls: [URL], from window: NSWindow? = nil) {
|
|
static func print(urls: [URL], from window: NSWindow? = nil) {
|
|
|
- guard requirePremiumPrint(from: window) else { return }
|
|
|
|
|
|
|
+ guard requirePremium(.filePreview, from: window) else { return }
|
|
|
guard !urls.isEmpty else { return }
|
|
guard !urls.isEmpty else { return }
|
|
|
|
|
|
|
|
let hostWindow = window ?? NSApp.keyWindow
|
|
let hostWindow = window ?? NSApp.keyWindow
|
|
@@ -216,14 +220,15 @@ enum PrintService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- static func printFile(at url: URL, from window: NSWindow? = nil) {
|
|
|
|
|
- guard requirePremiumPrint(from: window) else { return }
|
|
|
|
|
|
|
+ @discardableResult
|
|
|
|
|
+ static func printFile(at url: URL, from window: NSWindow? = nil) -> Bool {
|
|
|
|
|
+ guard requirePremium(.filePreview, from: window) else { return false }
|
|
|
let title = url.lastPathComponent
|
|
let title = url.lastPathComponent
|
|
|
guard let document = printablePDFDocument(from: url) else {
|
|
guard let document = printablePDFDocument(from: url) else {
|
|
|
showUnsupportedAlert(for: url)
|
|
showUnsupportedAlert(for: url)
|
|
|
- return
|
|
|
|
|
|
|
+ return false
|
|
|
}
|
|
}
|
|
|
- printPDF(document, title: title, from: window ?? NSApp.keyWindow)
|
|
|
|
|
|
|
+ return printPDF(document, title: title, from: window ?? NSApp.keyWindow)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private static func contentType(for url: URL) -> UTType? {
|
|
private static func contentType(for url: URL) -> UTType? {
|
|
@@ -280,10 +285,11 @@ enum PrintService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static func printPDF(_ document: PDFDocument, title: String, from window: NSWindow?) {
|
|
|
|
|
|
|
+ @discardableResult
|
|
|
|
|
+ private static func printPDF(_ document: PDFDocument, title: String, from window: NSWindow?) -> Bool {
|
|
|
guard document.pageCount > 0 else {
|
|
guard document.pageCount > 0 else {
|
|
|
showPrintFailedAlert()
|
|
showPrintFailedAlert()
|
|
|
- return
|
|
|
|
|
|
|
+ return false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let context = PrintOperationContext(document: document)
|
|
let context = PrintOperationContext(document: document)
|
|
@@ -292,18 +298,20 @@ enum PrintService {
|
|
|
|
|
|
|
|
let printInfo = configuredPrintInfo()
|
|
let printInfo = configuredPrintInfo()
|
|
|
let operation = NSPrintOperation(view: context.pdfView, printInfo: printInfo)
|
|
let operation = NSPrintOperation(view: context.pdfView, printInfo: printInfo)
|
|
|
- runPrintOperation(operation, title: title, from: window)
|
|
|
|
|
|
|
+ return runPrintOperation(operation, title: title, from: window)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static func printImage(_ image: NSImage, title: String, from window: NSWindow?) {
|
|
|
|
|
|
|
+ @discardableResult
|
|
|
|
|
+ private static func printImage(_ image: NSImage, title: String, from window: NSWindow?) -> Bool {
|
|
|
guard let document = pdfDocument(from: image) else {
|
|
guard let document = pdfDocument(from: image) else {
|
|
|
showPrintFailedAlert()
|
|
showPrintFailedAlert()
|
|
|
- return
|
|
|
|
|
|
|
+ return false
|
|
|
}
|
|
}
|
|
|
- printPDF(document, title: title, from: window)
|
|
|
|
|
|
|
+ return printPDF(document, title: title, from: window)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static func runPrintOperation(_ operation: NSPrintOperation, title: String, from window: NSWindow?) {
|
|
|
|
|
|
|
+ @discardableResult
|
|
|
|
|
+ private static func runPrintOperation(_ operation: NSPrintOperation, title: String, from window: NSWindow?) -> Bool {
|
|
|
operation.showsPrintPanel = true
|
|
operation.showsPrintPanel = true
|
|
|
operation.showsProgressPanel = true
|
|
operation.showsProgressPanel = true
|
|
|
operation.jobTitle = title
|
|
operation.jobTitle = title
|
|
@@ -320,16 +328,19 @@ enum PrintService {
|
|
|
didRun: #selector(PrintRatingCallback.printOperationDidRun(_:success:contextInfo:)),
|
|
didRun: #selector(PrintRatingCallback.printOperationDidRun(_:success:contextInfo:)),
|
|
|
contextInfo: nil
|
|
contextInfo: nil
|
|
|
)
|
|
)
|
|
|
|
|
+ let success = PrintRatingCallback.shared.lastSuccess
|
|
|
PrintRatingCallback.shared.reset()
|
|
PrintRatingCallback.shared.reset()
|
|
|
- } else {
|
|
|
|
|
- operation.run()
|
|
|
|
|
|
|
+ return success
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ return operation.run()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private final class PrintRatingCallback: NSObject {
|
|
private final class PrintRatingCallback: NSObject {
|
|
|
static let shared = PrintRatingCallback()
|
|
static let shared = PrintRatingCallback()
|
|
|
|
|
|
|
|
private var window: NSWindow?
|
|
private var window: NSWindow?
|
|
|
|
|
+ private(set) var lastSuccess = false
|
|
|
|
|
|
|
|
private override init() {
|
|
private override init() {
|
|
|
super.init()
|
|
super.init()
|
|
@@ -337,10 +348,12 @@ enum PrintService {
|
|
|
|
|
|
|
|
func prepare(window: NSWindow?) {
|
|
func prepare(window: NSWindow?) {
|
|
|
self.window = window
|
|
self.window = window
|
|
|
|
|
+ lastSuccess = false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func reset() {
|
|
func reset() {
|
|
|
window = nil
|
|
window = nil
|
|
|
|
|
+ lastSuccess = false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@objc func printOperationDidRun(
|
|
@objc func printOperationDidRun(
|
|
@@ -348,8 +361,9 @@ enum PrintService {
|
|
|
success: Bool,
|
|
success: Bool,
|
|
|
contextInfo: UnsafeMutableRawPointer?
|
|
contextInfo: UnsafeMutableRawPointer?
|
|
|
) {
|
|
) {
|
|
|
|
|
+ lastSuccess = success
|
|
|
let sourceWindow = window
|
|
let sourceWindow = window
|
|
|
- reset()
|
|
|
|
|
|
|
+ window = nil
|
|
|
guard success else { return }
|
|
guard success else { return }
|
|
|
AppRatingManager.shared.recordSuccessfulPrint(from: sourceWindow)
|
|
AppRatingManager.shared.recordSuccessfulPrint(from: sourceWindow)
|
|
|
}
|
|
}
|
|
@@ -374,9 +388,35 @@ enum PrintService {
|
|
|
printInfo.leftMargin = 36
|
|
printInfo.leftMargin = 36
|
|
|
printInfo.rightMargin = 36
|
|
printInfo.rightMargin = 36
|
|
|
|
|
|
|
|
|
|
+ applyPrintSettings(to: printInfo)
|
|
|
|
|
+
|
|
|
return printInfo
|
|
return printInfo
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private static func applyPrintSettings(to printInfo: NSPrintInfo) {
|
|
|
|
|
+ let settings = printInfo.dictionary()
|
|
|
|
|
+
|
|
|
|
|
+ let qualityValue: Int
|
|
|
|
|
+ switch AppSettings.printQuality {
|
|
|
|
|
+ case .draft: qualityValue = 0
|
|
|
|
|
+ case .normal: qualityValue = 1
|
|
|
|
|
+ case .best: qualityValue = 2
|
|
|
|
|
+ }
|
|
|
|
|
+ settings[NSPrintInfo.AttributeKey("NSPrintQuality")] = NSNumber(value: qualityValue)
|
|
|
|
|
+
|
|
|
|
|
+ if AppSettings.colorMode == .grayscale {
|
|
|
|
|
+ settings[NSPrintInfo.AttributeKey("NSPrintMode")] = "Gray"
|
|
|
|
|
+ } else {
|
|
|
|
|
+ settings.removeObject(forKey: NSPrintInfo.AttributeKey("NSPrintMode"))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if AppSettings.duplexEnabled {
|
|
|
|
|
+ settings[NSPrintInfo.AttributeKey("NSPrintDuplex")] = NSNumber(value: 1)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ settings.removeObject(forKey: NSPrintInfo.AttributeKey("NSPrintDuplex"))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private static func applyPrinter(to printInfo: NSPrintInfo) {
|
|
private static func applyPrinter(to printInfo: NSPrintInfo) {
|
|
|
let available = NSPrinter.printerNames
|
|
let available = NSPrinter.printerNames
|
|
|
guard !available.isEmpty else { return }
|
|
guard !available.isEmpty else { return }
|