David PHAM-VAN

Add iOS Direct Print

# Changelog
## 2.1.7
- Add iOS Direct Print
## 2.1.6
- Add qrcode to example
... ...
... ... @@ -26,8 +26,11 @@ class MyApp extends StatefulWidget {
class MyAppState extends State<MyApp> {
final GlobalKey<State<StatefulWidget>> shareWidget = GlobalKey();
final GlobalKey<State<StatefulWidget>> pickWidget = GlobalKey();
final GlobalKey<State<StatefulWidget>> previewContainer = GlobalKey();
Printer selectedPrinter;
Future<void> _printPdf() async {
print('Print ...');
final bool result = await Printing.layoutPdf(
... ... @@ -50,6 +53,36 @@ class MyAppState extends State<MyApp> {
);
}
Future<void> _pickPrinter() async {
print('Pick printer ...');
// Calculate the widget center for iPad sharing popup position
final RenderBox referenceBox = pickWidget.currentContext.findRenderObject();
final Offset topLeft =
referenceBox.localToGlobal(referenceBox.paintBounds.topLeft);
final Offset bottomRight =
referenceBox.localToGlobal(referenceBox.paintBounds.bottomRight);
final Rect bounds = Rect.fromPoints(topLeft, bottomRight);
final Printer printer = await Printing.pickPrinter(bounds: bounds);
setState(() {
selectedPrinter = printer;
});
print('Selected printer: $selectedPrinter');
}
Future<void> _directPrintPdf() async {
print('Direct print ...');
final bool result = await Printing.directPrintPdf(
printer: selectedPrinter,
onLayout: (PdfPageFormat format) async =>
(await generateDocument(PdfPageFormat.letter)).save());
print('Document printed: $result');
}
Future<void> _sharePdf() async {
print('Share ...');
final pdf.Document document = await generateDocument(PdfPageFormat.a4);
... ... @@ -124,6 +157,22 @@ class MyAppState extends State<MyApp> {
children: <Widget>[
RaisedButton(
child: const Text('Print Document'), onPressed: _printPdf),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
key: pickWidget,
child: const Text('Pick Printer'),
onPressed: _pickPrinter),
const SizedBox(width: 10),
RaisedButton(
child: Text(selectedPrinter == null
? 'Direct Print'
: 'Print to $selectedPrinter'),
onPressed:
selectedPrinter != null ? _directPrintPdf : null),
],
),
RaisedButton(
key: shareWidget,
child: const Text('Share Document'),
... ...
... ... @@ -37,7 +37,9 @@ class PdfPrintPageRenderer: UIPrintPageRenderer {
let page = pdfDocument?.page(at: pageIndex + 1)
ctx?.scaleBy(x: 1.0, y: -1.0)
ctx?.translateBy(x: 0.0, y: -paperRect.size.height)
ctx?.drawPDFPage(page!)
if page != nil {
ctx?.drawPDFPage(page!)
}
}
func cancelJob() {
... ... @@ -79,4 +81,22 @@ class PdfPrintPageRenderer: UIPrintPageRenderer {
return pages
}
var pageArgs: [String: NSNumber] {
let width = NSNumber(value: Double(paperRect.size.width))
let height = NSNumber(value: Double(paperRect.size.height))
let marginLeft = NSNumber(value: Double(printableRect.origin.x))
let marginTop = NSNumber(value: Double(printableRect.origin.y))
let marginRight = NSNumber(value: Double(paperRect.size.width - (printableRect.origin.x + printableRect.size.width)))
let marginBottom = NSNumber(value: Double(paperRect.size.height - (printableRect.origin.y + printableRect.size.height)))
return [
"width": width,
"height": height,
"marginLeft": marginLeft,
"marginTop": marginTop,
"marginRight": marginRight,
"marginBottom": marginBottom,
]
}
}
... ...
... ... @@ -35,10 +35,17 @@ public class SwiftPrintingPlugin: NSObject, FlutterPlugin, UIPrintInteractionCon
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: FlutterResult) {
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
let args = call.arguments! as! [String: Any]
if call.method == "printPdf" {
printPdf(args["name"] as? String ?? "")
let name = args["name"] as? String ?? ""
printPdf(name)
result(NSNumber(value: 1))
} else if call.method == "directPrintPdf" {
let name = args["name"] as? String ?? ""
let printer = args["printer"] as? String
let object = args["doc"] as? FlutterStandardTypedData
directPrintPdf(name: name, data: object!.data, withPrinter: printer!)
result(NSNumber(value: 1))
} else if call.method == "writePdf" {
if let object = args["doc"] as? FlutterStandardTypedData {
... ... @@ -83,6 +90,13 @@ public class SwiftPrintingPlugin: NSObject, FlutterPlugin, UIPrintInteractionCon
andBaseUrl: args["baseUrl"] as? String == nil ? nil : URL(string: (args["baseUrl"] as? String)!)
)
result(NSNumber(value: 1))
} else if call.method == "pickPrinter" {
pickPrinter(result, withSourceRect: CGRect(
x: CGFloat((args["x"] as? NSNumber)?.floatValue ?? 0.0),
y: CGFloat((args["y"] as? NSNumber)?.floatValue ?? 0.0),
width: CGFloat((args["w"] as? NSNumber)?.floatValue ?? 0.0),
height: CGFloat((args["h"] as? NSNumber)?.floatValue ?? 0.0)
))
} else {
result(FlutterMethodNotImplemented)
}
... ... @@ -102,6 +116,40 @@ public class SwiftPrintingPlugin: NSObject, FlutterPlugin, UIPrintInteractionCon
renderer = nil
}
func directPrintPdf(name: String, data: Data, withPrinter printerID: String) {
let printing = UIPrintInteractionController.isPrintingAvailable
if !printing {
let data: NSDictionary = [
"completed": false,
"error": "Printing not available",
]
channel?.invokeMethod("onCompleted", arguments: data)
return
}
let controller = UIPrintInteractionController.shared
controller.delegate = self
let printInfo = UIPrintInfo.printInfo()
printInfo.jobName = name
printInfo.outputType = .general
controller.printInfo = printInfo
controller.printingItem = data
let printerURL = URL(string: printerID)
if printerURL == nil {
let data: NSDictionary = [
"completed": false,
"error": "Unable to fine printer URL",
]
channel?.invokeMethod("onCompleted", arguments: data)
return
}
let printer = UIPrinter(url: printerURL!)
controller.print(to: printer, completionHandler: completionHandler)
}
func printPdf(_ name: String) {
let printing = UIPrintInteractionController.isPrintingAvailable
if !printing {
... ... @@ -212,4 +260,41 @@ public class SwiftPrintingPlugin: NSObject, FlutterPlugin, UIPrintInteractionCon
}
})
}
func pickPrinter(_ result: @escaping FlutterResult, withSourceRect rect: CGRect) {
let controller = UIPrinterPickerController(initiallySelectedPrinter: nil)
let pickPrinterCompletionHandler: UIPrinterPickerController.CompletionHandler = {
(printerPickerController: UIPrinterPickerController, completed: Bool, error: Error?) in
if !completed, error != nil {
print("Unable to pick printer: \(error?.localizedDescription ?? "unknown error")")
result(nil)
return
}
if printerPickerController.selectedPrinter == nil {
result(nil)
return
}
let printer = printerPickerController.selectedPrinter!
let data: NSDictionary = [
"url": printer.url.absoluteString as Any,
"name": printer.displayName as Any,
"model": printer.makeAndModel as Any,
"location": printer.displayLocation as Any,
]
result(data)
}
if UI_USER_INTERFACE_IDIOM() == .pad {
let viewController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
if viewController != nil {
controller.present(from: rect, in: viewController!.view, animated: true, completionHandler: pickPrinterCompletionHandler)
return
}
}
controller.present(animated: true, completionHandler: pickPrinterCompletionHandler)
}
}
... ...
... ... @@ -17,6 +17,7 @@
library printing;
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;
... ...
... ... @@ -18,6 +18,24 @@ part of printing;
typedef LayoutCallback = FutureOr<List<int>> Function(PdfPageFormat format);
@immutable
class Printer {
const Printer({
@required this.url,
this.name,
this.model,
this.location,
}) : assert(url != null);
final String url;
final String name;
final String model;
final String location;
@override
String toString() => name ?? url;
}
mixin Printing {
static const MethodChannel _channel = MethodChannel('printing');
static LayoutCallback _onLayout;
... ... @@ -87,6 +105,61 @@ mixin Printing {
return _onCompleted.future;
}
/// Opens the native printer picker interface, and returns the URL of the selected printer.
static Future<Printer> pickPrinter({Rect bounds}) async {
if (!Platform.isIOS) {
return null;
}
_channel.setMethodCallHandler(_handleMethod);
bounds ??= Rect.fromCircle(center: Offset.zero, radius: 10);
final Map<String, dynamic> params = <String, dynamic>{
'x': bounds.left,
'y': bounds.top,
'w': bounds.width,
'h': bounds.height,
};
final Map<dynamic, dynamic> printer = await _channel
.invokeMethod<Map<dynamic, dynamic>>('pickPrinter', params);
print(printer);
if (printer == null) {
return null;
}
return Printer(
url: printer['url'],
name: printer['name'],
model: printer['model'],
location: printer['location'],
);
}
/// Prints a Pdf document to a specific local printer with no UI
///
/// returns a future with a `bool` set to true if the document is printed
/// and false if it is canceled.
/// throws an exception in case of error
static Future<bool> directPrintPdf({
@required Printer printer,
@required LayoutCallback onLayout,
String name = 'Document',
}) async {
if (!Platform.isIOS || printer == null) {
return false;
}
_onCompleted = Completer<bool>();
_channel.setMethodCallHandler(_handleMethod);
final List<int> bytes = await onLayout(PdfPageFormat.standard);
if (bytes == null) {
return false;
}
final Map<String, dynamic> params = <String, dynamic>{
'name': name,
'printer': printer.url,
'doc': Uint8List.fromList(bytes),
};
await _channel.invokeMethod<int>('directPrintPdf', params);
return _onCompleted.future;
}
/// Prints a [PdfDocument] or a pdf stream to a local printer using the platform UI
@Deprecated('use Printing.layoutPdf(onLayout: (_) => document.save());')
static Future<void> printPdf({
... ...
... ... @@ -4,7 +4,7 @@ description: Plugin that allows Flutter apps to generate and print documents to
homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing
repository: https://github.com/DavBfr/dart_pdf
issue_tracker: https://github.com/DavBfr/dart_pdf/issues
version: 2.1.6
version: 2.1.7
environment:
sdk: ">=2.1.0 <3.0.0"
... ...