Matteo Ricupero
Committed by David PHAM-VAN

Workaround for iOS bug + force paper size:

implemented fix when UIPrinter isn't contactable even if accessible due to an iOS bug.

added a flag to force using custom paper size to use when the combination of airprint+printer driver choose a wrong paper format.
... ... @@ -3,6 +3,7 @@
## 5.13.2
- Added new printing output type value on iOS [Matteo Ricupero]
- Workaround for iOS bug and force paper size [Matteo Ricupero]
## 5.13.1
... ...
public class CustomPrintPaper: UIPrintPaper {
private let size: CGSize
override public var paperSize: CGSize { return size }
override public var printableRect: CGRect { return CGRect(origin: CGPoint.zero, size: size) }
init(size: CGSize) {
self.size = size
}
}
... ...
... ... @@ -25,6 +25,9 @@ func dataProviderReleaseDataCallback(info _: UnsafeMutableRawPointer?, data: Uns
// Each printer will be identified by its URL string
var selectedPrinters = [String: UIPrinter]()
// Holds the printer after it was picked
var pickedPrinter: UIPrinter?
public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate {
private var printing: PrintingPlugin
public var index: Int
... ... @@ -36,6 +39,7 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
private let semaphore = DispatchSemaphore(value: 0)
private var dynamic = false
private var currentSize: CGSize?
private var forceCustomPrintPaper = false
public init(printing: PrintingPlugin, index: Int) {
self.printing = printing
... ... @@ -149,6 +153,10 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
return paperList[0]
}
if forceCustomPrintPaper {
return CustomPrintPaper(size: currentSize!)
}
for paper in paperList {
if (paper.paperSize.width == currentSize!.width && paper.paperSize.height == currentSize!.height) ||
(paper.paperSize.width == currentSize!.height && paper.paperSize.height == currentSize!.width)
... ... @@ -162,9 +170,11 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
return bestPaper
}
func printPdf(name: String, withPageSize size: CGSize, andMargin margin: CGRect, withPrinter printerID: String?, dynamically dyn: Bool, outputType type: UIPrintInfo.OutputType) {
func printPdf(name: String, withPageSize size: CGSize, andMargin margin: CGRect, withPrinter printerID: String?, dynamically dyn: Bool, outputType type: UIPrintInfo.OutputType, forceCustomPrintPaper: Bool = false) {
currentSize = size
dynamic = dyn
self.forceCustomPrintPaper = forceCustomPrintPaper
let printing = UIPrintInteractionController.isPrintingAvailable
if !printing {
self.printing.onCompleted(printJob: self, completed: false, error: "Printing not available")
... ... @@ -207,6 +217,14 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
selectedPrinters[printerURLString] = UIPrinter(url: printerURL!)
}
// Sometimes using UIPrinter(url:) gives a non-contactable printer.
// https://stackoverflow.com/questions/34602302/creating-a-working-uiprinter-object-from-url-for-dialogue-free-printing
// This lets use a printer saved during picking and fall back using a printer created with UIPrinter(url:)
if pickedPrinter != nil && selectedPrinters[printerURLString]!.url == pickedPrinter?.url {
controller.print(to: pickedPrinter!, completionHandler: completionHandler)
return
}
selectedPrinters[printerURLString]!.contactPrinter { available in
if !available {
self.printing.onCompleted(printJob: self, completed: false, error: "Printer not available")
... ... @@ -328,6 +346,9 @@ public class PrintJob: UIPrintPageRenderer, UIPrintInteractionControllerDelegate
"model": printer.makeAndModel as Any,
"location": printer.displayLocation as Any,
]
pickedPrinter = printer
result(data)
}
... ...
... ... @@ -60,6 +60,7 @@ 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
let forceCustomPrintPaper = args["forceCustomPrintPaper"] as! Bool
let outputType: UIPrintInfo.OutputType
switch args["outputType"] as! Int {
... ... @@ -89,7 +90,8 @@ public class PrintingPlugin: NSObject, FlutterPlugin {
),
withPrinter: printer,
dynamically: dynamic,
outputType: outputType)
outputType: outputType,
forceCustomPrintPaper: forceCustomPrintPaper)
result(NSNumber(value: 1))
} else if call.method == "sharePdf" {
let object = args["doc"] as! FlutterStandardTypedData
... ...
... ... @@ -158,6 +158,7 @@ class PrintingPlugin extends PrintingPlatform {
bool dynamicLayout,
bool usePrinterSettings,
OutputType outputType,
bool forceCustomPrintPaper,
) async {
late Uint8List result;
try {
... ...
... ... @@ -68,6 +68,7 @@ abstract class PrintingPlatform extends PlatformInterface {
bool dynamicLayout,
bool usePrinterSettings,
OutputType outputType,
bool forceCustomPrintPaper,
);
/// Enumerate the available printers on the system.
... ...
... ... @@ -182,6 +182,7 @@ class MethodChannelPrinting extends PrintingPlatform {
bool dynamicLayout,
bool usePrinterSettings,
OutputType outputType,
bool forceCustomPrintPaper,
) async {
final job = _printJobs.add(
onCompleted: Completer<bool>(),
... ... @@ -201,6 +202,7 @@ class MethodChannelPrinting extends PrintingPlatform {
'dynamic': dynamicLayout,
'usePrinterSettings': usePrinterSettings,
'outputType': outputType.index,
'forceCustomPrintPaper': forceCustomPrintPaper,
};
await _channel.invokeMethod<int>('printPdf', params);
... ...
... ... @@ -40,9 +40,15 @@ mixin Printing {
/// Set [usePrinterSettings] to true to use the configuration defined by
/// the printer. May not work for all the printers and can depend on the
/// drivers. (Supported platforms: Windows)
///
/// Set [outputType] to [OutputType.generic] to use the default printing
/// system, or [OutputType.photos] to use the photo printing system.
/// (Supported platforms: iOS)
///
/// Use [customPrintPaper] to force the printer to use a custom paper size.
/// Use value `true` to use [format] as custom paper size, when the printer
/// driver will not allows the user to use papers which are actually supported by the printer.
/// (Supported platforms: iOS)
static Future<bool> layoutPdf({
required LayoutCallback onLayout,
String name = 'Document',
... ... @@ -50,6 +56,7 @@ mixin Printing {
bool dynamicLayout = true,
bool usePrinterSettings = false,
OutputType outputType = OutputType.generic,
bool forceCustomPrintPaper = false,
}) {
return PrintingPlatform.instance.layoutPdf(
null,
... ... @@ -59,6 +66,7 @@ mixin Printing {
dynamicLayout,
usePrinterSettings,
outputType,
forceCustomPrintPaper,
);
}
... ... @@ -138,6 +146,15 @@ mixin Printing {
/// Set [usePrinterSettings] to true to use the configuration defined by
/// the printer. May not work for all the printers and can depend on the
/// drivers. (Supported platforms: Windows)
///
/// Set [outputType] to [OutputType.generic] to use the default printing
/// system, or [OutputType.photos] to use the photo printing system.
/// (Supported platforms: iOS)
///
/// Use [customPrintPaper] to force the printer to use a custom paper size.
/// Use value `true` to use [format] as custom paper size, when the printer
/// driver will not allows the user to use papers which are actually supported by the printer.
/// (Supported platforms: iOS)
static FutureOr<bool> directPrintPdf({
required Printer printer,
required LayoutCallback onLayout,
... ... @@ -146,6 +163,7 @@ mixin Printing {
bool dynamicLayout = true,
bool usePrinterSettings = false,
OutputType outputType = OutputType.generic,
bool forceCustomPrintPaper = false,
}) {
return PrintingPlatform.instance.layoutPdf(
printer,
... ... @@ -155,6 +173,7 @@ mixin Printing {
dynamicLayout,
usePrinterSettings,
outputType,
forceCustomPrintPaper,
);
}
... ...
... ... @@ -109,6 +109,7 @@ class MockPrinting extends Mock
bool dynamicLayout,
bool usePrinterSettings,
OutputType outputType,
bool forceCustomPrintPaper,
) async =>
true;
... ...