David PHAM-VAN

Add dynamicLayout setting to prevent iOS to freeze

... ... @@ -27,8 +27,10 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
private var pdfDocument: CGPDFDocument?
private var urlObservation: NSKeyValueObservation?
private var jobName: String?
private var printerName: String?
private var orientation: UIPrintInfo.Orientation?
private let semaphore = DispatchSemaphore(value: 0)
private var dynamic = false
public init(printing: PrintingPlugin, index: Int) {
self.printing = printing
... ... @@ -47,9 +49,13 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
}
}
func cancelJob(_: String?) {
func cancelJob(_ error: String?) {
pdfDocument = nil
if dynamic {
semaphore.signal()
} else {
printing.onCompleted(printJob: self, completed: false, error: error as NSString?)
}
}
func setDocument(_ data: Data?) {
... ... @@ -58,11 +64,44 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
let dataProvider = CGDataProvider(dataInfo: nil, data: bytesPointer, size: data?.count ?? 0, releaseData: dataProviderReleaseDataCallback)
pdfDocument = CGPDFDocument(dataProvider!)
if dynamic {
// Unblock the main thread
semaphore.signal()
return
}
let controller = UIPrintInteractionController.shared
controller.delegate = self
let printInfo = UIPrintInfo.printInfo()
printInfo.jobName = jobName!
printInfo.outputType = .general
if orientation != nil {
printInfo.orientation = orientation!
orientation = nil
}
controller.printInfo = printInfo
controller.printPageRenderer = self
DispatchQueue.main.async {
if self.printerName != nil {
let printerURL = URL(string: self.printerName!)
if printerURL == nil {
self.printing.onCompleted(printJob: self, completed: false, error: "Unable to find printer URL")
return
}
let printer = UIPrinter(url: printerURL!)
controller.print(to: printer, completionHandler: self.completionHandler)
} else {
controller.present(animated: true, completionHandler: self.completionHandler)
}
}
}
override public var numberOfPages: Int {
if dynamic {
printing.onLayout(
printJob: self,
width: paperRect.size.width,
... ... @@ -75,6 +114,7 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
// Block the main thread, waiting for a document
semaphore.wait()
}
return pdfDocument?.numberOfPages ?? 0
}
... ... @@ -87,7 +127,8 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
printing.onCompleted(printJob: self, completed: completed, error: error?.localizedDescription as NSString?)
}
func printPdf(name: String, withPageSize size: CGSize, andMargin _: CGRect, withPrinter printerID: String?) {
func printPdf(name: String, withPageSize size: CGSize, andMargin margin: CGRect, withPrinter printerID: String?, dynamically dyn: Bool) {
dynamic = dyn
let printing = UIPrintInteractionController.isPrintingAvailable
if !printing {
self.printing.onCompleted(printJob: self, completed: false, error: "Printing not available")
... ... @@ -99,6 +140,7 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
}
jobName = name
printerName = printerID
let controller = UIPrintInteractionController.shared
controller.delegate = self
... ... @@ -128,7 +170,20 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
return
}
if dynamic {
controller.present(animated: true, completionHandler: completionHandler)
return
}
self.printing.onLayout(
printJob: self,
width: size.width,
height: size.height,
marginLeft: margin.minX,
marginTop: margin.minY,
marginRight: size.width - margin.maxX,
marginBottom: size.height - margin.maxY
)
}
static func sharePdf(data: Data, withSourceRect rect: CGRect, andName name: String) {
... ...
... ... @@ -59,6 +59,7 @@ public class PrintingPlugin: NSObject, FlutterPlugin {
let marginRight = CGFloat((args["marginRight"] as! NSNumber).floatValue)
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
printJob.printPdf(name: name,
withPageSize: CGSize(
... ... @@ -70,7 +71,8 @@ public class PrintingPlugin: NSObject, FlutterPlugin {
y: marginTop,
width: width - marginRight - marginLeft,
height: height - marginBottom - marginTop
), withPrinter: printer)
), withPrinter: printer,
dynamically: dynamic)
result(NSNumber(value: 1))
} else if call.method == "sharePdf" {
let object = args["doc"] as! FlutterStandardTypedData
... ...
... ... @@ -64,6 +64,7 @@ abstract class PrintingPlatform extends PlatformInterface {
LayoutCallback onLayout,
String name,
PdfPageFormat format,
bool dynamicLayout,
);
/// Enumerate the available printers on the system.
... ...
... ... @@ -142,6 +142,7 @@ class MethodChannelPrinting extends PrintingPlatform {
LayoutCallback onLayout,
String name,
PdfPageFormat format,
bool dynamicLayout,
) async {
final job = _printJobs.add(
onCompleted: Completer<bool>(),
... ... @@ -158,6 +159,7 @@ class MethodChannelPrinting extends PrintingPlatform {
'marginTop': format.marginTop,
'marginRight': format.marginRight,
'marginBottom': format.marginBottom,
'dynamic': dynamicLayout,
};
await _channel.invokeMethod<int>('printPdf', params);
... ...
... ... @@ -34,6 +34,7 @@ class PdfPreview extends StatefulWidget {
this.pdfFileName,
this.useActions = true,
this.pages,
this.dynamicLayout = true,
}) : super(key: key);
/// Called when a pdf document is needed
... ... @@ -90,6 +91,11 @@ class PdfPreview extends StatefulWidget {
/// Pages to display. Default will display all the pages.
final List<int>? pages;
/// Request page re-layout to match the printer paper and margins.
/// Mitigate an issue with iOS and macOS print dialog that prevent any
/// channel message while opened.
final bool dynamicLayout;
@override
_PdfPreviewState createState() => _PdfPreviewState();
}
... ... @@ -512,6 +518,7 @@ class _PdfPreviewState extends State<PdfPreview> {
onLayout: widget.build,
name: widget.pdfFileName ?? 'Document',
format: format,
dynamicLayout: widget.dynamicLayout,
);
if (result && widget.onPrinted != null) {
... ...
... ... @@ -40,12 +40,14 @@ mixin Printing {
required LayoutCallback onLayout,
String name = 'Document',
PdfPageFormat format = PdfPageFormat.standard,
bool dynamicLayout = true,
}) {
return PrintingPlatform.instance.layoutPdf(
null,
onLayout,
name,
format,
dynamicLayout,
);
}
... ... @@ -122,12 +124,14 @@ mixin Printing {
required LayoutCallback onLayout,
String name = 'Document',
PdfPageFormat format = PdfPageFormat.standard,
bool dynamicLayout = true,
}) {
return PrintingPlatform.instance.layoutPdf(
printer,
onLayout,
name,
format,
dynamicLayout,
);
}
... ...
... ... @@ -30,6 +30,7 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate {
private var pdfDocument: CGPDFDocument?
private var page: CGPDFPage?
private let semaphore = DispatchSemaphore(value: 0)
private var dynamic = false
public init(printing: PrintingPlugin, index: Int) {
self.printing = printing
... ... @@ -49,6 +50,7 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate {
setFrameSize(size)
setBoundsSize(size)
if dynamic {
printing.onLayout(
printJob: self,
width: printOperation!.printInfo.paperSize.width,
... ... @@ -61,6 +63,7 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate {
// Block the main thread, waiting for a document
semaphore.wait()
}
if pdfDocument != nil {
range.pointee.length = pdfDocument!.numberOfPages
... ... @@ -90,8 +93,16 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate {
let dataProvider = CGDataProvider(dataInfo: nil, data: bytesPointer, size: data?.count ?? 0, releaseData: dataProviderReleaseDataCallback)
pdfDocument = CGPDFDocument(dataProvider!)
if dynamic {
// Unblock the main thread
semaphore.signal()
return
}
DispatchQueue.main.async {
let window = NSApplication.shared.mainWindow!
self.printOperation!.runModal(for: window, delegate: self, didRun: #selector(self.printOperationDidRun(printOperation:success:contextInfo:)), contextInfo: nil)
}
}
override public func draw(_: NSRect) {
... ... @@ -119,7 +130,8 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate {
return printers
}
public func printPdf(name: String, withPageSize size: CGSize, andMargin _: CGRect, withPrinter printer: String?) {
public func printPdf(name: String, withPageSize size: CGSize, andMargin _: CGRect, withPrinter printer: String?, dynamically dyn: Bool) {
dynamic = dyn
let sharedInfo = NSPrintInfo.shared
let sharedDict = sharedInfo.dictionary()
let printInfoDict = NSMutableDictionary(dictionary: sharedDict)
... ... @@ -133,20 +145,38 @@ public class PrintJob: NSView, NSSharingServicePickerDelegate {
// Print the custom view
printOperation = NSPrintOperation(view: self, printInfo: printInfo)
printOperation!.jobTitle = name
printOperation!.printPanel.options = [.showsPreview, .showsPaperSize, .showsOrientation]
printOperation!.printPanel.options = [.showsPreview]
if printer != nil {
printInfo.printer = NSPrinter(name: printer!)!
printOperation!.showsPrintPanel = false
printOperation!.showsProgressPanel = false
}
if dynamic {
let window = NSApplication.shared.mainWindow!
printOperation!.printPanel.options = [.showsPreview, .showsPaperSize, .showsOrientation]
printOperation!.runModal(for: window, delegate: self, didRun: #selector(printOperationDidRun(printOperation:success:contextInfo:)), contextInfo: nil)
return
}
printing.onLayout(
printJob: self,
width: printOperation!.printInfo.paperSize.width,
height: printOperation!.printInfo.paperSize.height,
marginLeft: printOperation!.printInfo.leftMargin,
marginTop: printOperation!.printInfo.topMargin,
marginRight: printOperation!.printInfo.rightMargin,
marginBottom: printOperation!.printInfo.bottomMargin
)
}
func cancelJob(_: String?) {
func cancelJob(_ error: String?) {
pdfDocument = nil
if dynamic {
semaphore.signal()
} else {
printing.onCompleted(printJob: self, completed: false, error: error as NSString?)
}
}
public static func sharePdf(data: Data, withSourceRect rect: CGRect, andName name: String) {
... ...
... ... @@ -59,6 +59,7 @@ public class PrintingPlugin: NSObject, FlutterPlugin {
let marginRight = CGFloat((args["marginRight"] as! NSNumber).floatValue)
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
printJob.printPdf(name: name,
withPageSize: CGSize(
... ... @@ -70,7 +71,8 @@ public class PrintingPlugin: NSObject, FlutterPlugin {
y: marginTop,
width: width - marginRight - marginLeft,
height: height - marginBottom - marginTop
), withPrinter: printer)
), withPrinter: printer,
dynamically: dynamic)
result(NSNumber(value: 1))
} else if call.method == "listPrinters" {
let printJob = PrintJob(printing: self, index: -1)
... ...
... ... @@ -113,6 +113,7 @@ class MockPrinting extends Mock
LayoutCallback onLayout,
String name,
PdfPageFormat format,
bool dynamicLayout,
) async =>
true;
... ...