diff --git a/printing/ios/printing/Sources/printing/PrintJob.swift b/printing/ios/printing/Sources/printing/PrintJob.swift index af87b263..b7c9a224 100644 --- a/printing/ios/printing/Sources/printing/PrintJob.swift +++ b/printing/ios/printing/Sources/printing/PrintJob.swift @@ -84,7 +84,8 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate controller.delegate = self let printInfo = UIPrintInfo.printInfo() - printInfo.jobName = jobName! + let strippedJobName = jobName!.hasSuffix(".pdf") ? String(jobName!.dropLast(4)) : jobName! + printInfo.jobName = strippedJobName printInfo.outputType = .general if orientation != nil { printInfo.orientation = orientation! @@ -185,7 +186,8 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate orientation = UIPrintInfo.Orientation.landscape } - jobName = name + // Strip .pdf extension as UIPrintInteractionController appends it automatically + jobName = name.hasSuffix(".pdf") ? String(name.dropLast(4)) : name printerName = printerID let controller = UIPrintInteractionController.shared diff --git a/printing/ios/printing/Sources/printing/PrintingPlugin.swift b/printing/ios/printing/Sources/printing/PrintingPlugin.swift index 57641ece..1a993cbf 100644 --- a/printing/ios/printing/Sources/printing/PrintingPlugin.swift +++ b/printing/ios/printing/Sources/printing/PrintingPlugin.swift @@ -185,7 +185,9 @@ public class PrintingPlugin: NSObject, FlutterPlugin { "job": printJob.index, ] as [String: Any] - channel.invokeMethod("onLayout", arguments: arg) + DispatchQueue.main.async { + self.channel.invokeMethod("onLayout", arguments: arg) + } } /// send completion status to flutter @@ -195,8 +197,10 @@ public class PrintingPlugin: NSObject, FlutterPlugin { "error": error as Any, "job": printJob.index, ] - channel.invokeMethod("onCompleted", arguments: data) jobs.removeValue(forKey: UInt32(printJob.index)) + DispatchQueue.main.async { + self.channel.invokeMethod("onCompleted", arguments: data) + } } /// send html to pdf data result to flutter @@ -205,7 +209,9 @@ public class PrintingPlugin: NSObject, FlutterPlugin { "doc": FlutterStandardTypedData(bytes: pdfData), "job": printJob.index, ] - channel.invokeMethod("onHtmlRendered", arguments: data) + DispatchQueue.main.async { + self.channel.invokeMethod("onHtmlRendered", arguments: data) + } } /// send html to pdf conversion error to flutter @@ -214,7 +220,9 @@ public class PrintingPlugin: NSObject, FlutterPlugin { "error": error, "job": printJob.index, ] - channel.invokeMethod("onHtmlError", arguments: data) + DispatchQueue.main.async { + self.channel.invokeMethod("onHtmlError", arguments: data) + } } /// send pdf to raster data result to flutter @@ -225,7 +233,9 @@ public class PrintingPlugin: NSObject, FlutterPlugin { "height": height, "job": printJob.index, ] - channel.invokeMethod("onPageRasterized", arguments: data) + DispatchQueue.main.async { + self.channel.invokeMethod("onPageRasterized", arguments: data) + } } public func onPageRasterEnd(printJob: PrintJob, error: String?) { @@ -233,6 +243,8 @@ public class PrintingPlugin: NSObject, FlutterPlugin { "job": printJob.index, "error": error as Any, ] - channel.invokeMethod("onPageRasterEnd", arguments: data) + DispatchQueue.main.async { + self.channel.invokeMethod("onPageRasterEnd", arguments: data) + } } } diff --git a/printing/lib/src/print_job.dart b/printing/lib/src/print_job.dart index 7209d547..26ee2e56 100644 --- a/printing/lib/src/print_job.dart +++ b/printing/lib/src/print_job.dart @@ -15,6 +15,7 @@ */ import 'dart:async'; +import 'dart:math'; import 'dart:typed_data'; import 'callback.dart'; @@ -57,7 +58,7 @@ class PrintJobs { /// Create a list print jobs PrintJobs(); - static var _currentIndex = 0; + static int _currentIndex = Random().nextInt(0x7FFFFFFF); final _printJobs = {}; diff --git a/printing/macos/printing/Sources/printing/PrintJob.swift b/printing/macos/printing/Sources/printing/PrintJob.swift index ebca1799..8a9f41c5 100644 --- a/printing/macos/printing/Sources/printing/PrintJob.swift +++ b/printing/macos/printing/Sources/printing/PrintJob.swift @@ -29,9 +29,11 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate { private var printOperation: NSPrintOperation? private var pdfDocument: CGPDFDocument? private var page: CGPDFPage? - private let semaphore = DispatchSemaphore(value: 0) + private var isWaitingForDocument = false + private var documentReceived = false private var dynamic = false private var _window: NSWindow? + private var lastLayoutParams: (width: CGFloat, height: CGFloat, marginLeft: CGFloat, marginTop: CGFloat, marginRight: CGFloat, marginBottom: CGFloat)? public init(printing: PrintingPlugin, index: Int) { self.printing = printing @@ -44,6 +46,13 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate { fatalError("init(coder:) has not been implemented") } + private func layoutParamsChanged(_ new: (width: CGFloat, height: CGFloat, marginLeft: CGFloat, marginTop: CGFloat, marginRight: CGFloat, marginBottom: CGFloat)) -> Bool { + guard let last = lastLayoutParams else { return true } + return last.width != new.width || last.height != new.height || + last.marginLeft != new.marginLeft || last.marginTop != new.marginTop || + last.marginRight != new.marginRight || last.marginBottom != new.marginBottom + } + // Return the number of pages available for printing override public func knowsPageRange(_ range: NSRangePointer) -> Bool { let size = printOperation!.showsPrintPanel ? printOperation!.printPanel.printInfo.paperSize : printOperation!.printInfo.paperSize @@ -52,8 +61,7 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate { setBoundsSize(size) if dynamic { - printing.onLayout( - printJob: self, + let currentParams = ( width: printOperation!.printInfo.paperSize.width, height: printOperation!.printInfo.paperSize.height, marginLeft: printOperation!.printInfo.leftMargin, @@ -61,9 +69,30 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate { marginRight: printOperation!.printInfo.rightMargin, marginBottom: printOperation!.printInfo.bottomMargin ) - - // Block the main thread, waiting for a document - semaphore.wait() + + if layoutParamsChanged(currentParams) { + lastLayoutParams = currentParams + + printing.onLayout( + printJob: self, + width: currentParams.width, + height: currentParams.height, + marginLeft: currentParams.marginLeft, + marginTop: currentParams.marginTop, + marginRight: currentParams.marginRight, + marginBottom: currentParams.marginBottom + ) + + // Wait for document using RunLoop to keep main thread responsive + isWaitingForDocument = true + documentReceived = false + let runLoop = RunLoop.current + let timeout = Date(timeIntervalSinceNow: 30.0) + while !documentReceived && Date() < timeout { + runLoop.run(mode: .default, before: Date(timeIntervalSinceNow: 0.1)) + } + isWaitingForDocument = false + } } if pdfDocument != nil { @@ -95,8 +124,8 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate { pdfDocument = CGPDFDocument(dataProvider!) if dynamic { - // Unblock the main thread - semaphore.signal() + // Signal that document is ready + documentReceived = true return } @@ -136,6 +165,7 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate { public func printPdf(name: String, withPageSize size: CGSize, andMargin _: CGRect, withPrinter printer: String?, dynamically dyn: Bool, andWindow window: NSWindow) { dynamic = dyn _window = window + lastLayoutParams = nil let sharedInfo = NSPrintInfo.shared let sharedDict = sharedInfo.dictionary() let printInfoDict = NSMutableDictionary(dictionary: sharedDict) @@ -184,8 +214,9 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate { func cancelJob(_ error: String?) { pdfDocument = nil + lastLayoutParams = nil if dynamic { - semaphore.signal() + documentReceived = true } else { printing.onCompleted(printJob: self, completed: false, error: error as NSString?) } diff --git a/printing/macos/printing/Sources/printing/PrintingPlugin.swift b/printing/macos/printing/Sources/printing/PrintingPlugin.swift index 0d9cfed2..6d384b22 100644 --- a/printing/macos/printing/Sources/printing/PrintingPlugin.swift +++ b/printing/macos/printing/Sources/printing/PrintingPlugin.swift @@ -37,7 +37,9 @@ public func net_nfet_printing_set_error(job: UInt32, message: UnsafePointer, size: UInt64) { - instance!.jobs[job]?.setDocument(Data(bytes: doc, count: Int(size))) + jobsLock.lock() + let printJob = sharedJobs[job] + jobsLock.unlock() + printJob?.setDocument(Data(bytes: doc, count: Int(size))) } @objc public static func setError(job: UInt32, message: UnsafePointer) { - instance!.jobs[job]?.cancelJob(String(cString: message)) + jobsLock.lock() + let printJob = sharedJobs[job] + jobsLock.unlock() + printJob?.cancelJob(String(cString: message)) } /// Entry point @@ -78,7 +86,10 @@ public class PrintingPlugin: NSObject, FlutterPlugin { let marginBottom = CGFloat((args["marginBottom"] as! NSNumber).floatValue) let printJob = PrintJob(printing: self, index: args["job"] as! Int) let dynamic = args["dynamic"] as! Bool - jobs[args["job"] as! UInt32] = printJob + + PrintingPlugin.jobsLock.lock() + PrintingPlugin.sharedJobs[args["job"] as! UInt32] = printJob + PrintingPlugin.jobsLock.unlock() guard let window = registrar.view?.window else { result(NSNumber(value: 0)) @@ -191,7 +202,10 @@ public class PrintingPlugin: NSObject, FlutterPlugin { "job": printJob.index, ] channel.invokeMethod("onCompleted", arguments: data) - jobs.removeValue(forKey: UInt32(printJob.index)) + + PrintingPlugin.jobsLock.lock() + PrintingPlugin.sharedJobs.removeValue(forKey: UInt32(printJob.index)) + PrintingPlugin.jobsLock.unlock() } /// send html to pdf data result to flutter