David PHAM-VAN

Implement asynchronous printing

1 # 1.2.0 1 # 1.2.0
2 * Fix compileSdkVersion to match appcompat 2 * Fix compileSdkVersion to match appcompat
3 * Change license to Apache 2.0 3 * Change license to Apache 2.0
  4 +* Implement asynchronous printing driven by the OS
4 5
5 # 1.1.0 6 # 1.1.0
6 * Rename classes to satisfy Dart conventions 7 * Rename classes to satisfy Dart conventions
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 16
17 package net.nfet.flutter.printing; 17 package net.nfet.flutter.printing;
18 18
  19 +import static java.lang.StrictMath.max;
  20 +
19 import android.app.Activity; 21 import android.app.Activity;
20 import android.content.Context; 22 import android.content.Context;
21 import android.content.Intent; 23 import android.content.Intent;
@@ -36,6 +38,7 @@ import java.io.File; @@ -36,6 +38,7 @@ import java.io.File;
36 import java.io.FileOutputStream; 38 import java.io.FileOutputStream;
37 import java.io.IOException; 39 import java.io.IOException;
38 import java.io.OutputStream; 40 import java.io.OutputStream;
  41 +import java.util.HashMap;
39 42
40 import io.flutter.plugin.common.MethodCall; 43 import io.flutter.plugin.common.MethodCall;
41 import io.flutter.plugin.common.MethodChannel; 44 import io.flutter.plugin.common.MethodChannel;
@@ -46,12 +49,18 @@ import io.flutter.plugin.common.PluginRegistry.Registrar; @@ -46,12 +49,18 @@ import io.flutter.plugin.common.PluginRegistry.Registrar;
46 /** 49 /**
47 * PrintingPlugin 50 * PrintingPlugin
48 */ 51 */
49 -public class PrintingPlugin implements MethodCallHandler { 52 +public class PrintingPlugin extends PrintDocumentAdapter implements MethodCallHandler {
50 private static PrintManager printManager; 53 private static PrintManager printManager;
51 private final Activity activity; 54 private final Activity activity;
  55 + private final MethodChannel channel;
  56 + private PrintedPdfDocument mPdfDocument;
  57 + private byte[] documentData;
  58 + private String jobName;
  59 + private LayoutResultCallback callback;
52 60
53 - private PrintingPlugin(Activity activity) { 61 + private PrintingPlugin(Activity activity, MethodChannel channel) {
54 this.activity = activity; 62 this.activity = activity;
  63 + this.channel = channel;
55 } 64 }
56 65
57 /** 66 /**
@@ -59,82 +68,97 @@ public class PrintingPlugin implements MethodCallHandler { @@ -59,82 +68,97 @@ public class PrintingPlugin implements MethodCallHandler {
59 */ 68 */
60 public static void registerWith(Registrar registrar) { 69 public static void registerWith(Registrar registrar) {
61 final MethodChannel channel = new MethodChannel(registrar.messenger(), "printing"); 70 final MethodChannel channel = new MethodChannel(registrar.messenger(), "printing");
62 - channel.setMethodCallHandler(new PrintingPlugin(registrar.activity())); 71 + channel.setMethodCallHandler(new PrintingPlugin(registrar.activity(), channel));
63 printManager = (PrintManager) registrar.activity().getSystemService(Context.PRINT_SERVICE); 72 printManager = (PrintManager) registrar.activity().getSystemService(Context.PRINT_SERVICE);
64 } 73 }
65 74
66 @Override 75 @Override
  76 + public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor parcelFileDescriptor,
  77 + CancellationSignal cancellationSignal, WriteResultCallback writeResultCallback) {
  78 + OutputStream output = null;
  79 + try {
  80 + output = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
  81 + output.write(documentData, 0, documentData.length);
  82 + writeResultCallback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});
  83 + } catch (IOException e) {
  84 + e.printStackTrace();
  85 + } finally {
  86 + try {
  87 + if (output != null) {
  88 + output.close();
  89 + }
  90 + } catch (IOException e) {
  91 + e.printStackTrace();
  92 + }
  93 + }
  94 + }
  95 +
  96 + @Override
  97 + public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
  98 + CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras) {
  99 + // Create a new PdfDocument with the requested page attributes
  100 + mPdfDocument = new PrintedPdfDocument(activity, newAttributes);
  101 +
  102 + // Respond to cancellation request
  103 + if (cancellationSignal.isCanceled()) {
  104 + callback.onLayoutCancelled();
  105 + return;
  106 + }
  107 +
  108 + this.callback = callback;
  109 +
  110 + HashMap<String, Double> args = new HashMap<>();
  111 +
  112 + PrintAttributes.MediaSize size = newAttributes.getMediaSize();
  113 + args.put("width", size.getWidthMils() * 72.0 / 1000.0);
  114 + args.put("height", size.getHeightMils() * 72.0 / 1000.0);
  115 +
  116 + PrintAttributes.Margins margins = newAttributes.getMinMargins();
  117 + args.put("marginLeft", margins.getLeftMils() * 72.0 / 1000.0);
  118 + args.put("marginTop", margins.getTopMils() * 72.0 / 1000.0);
  119 + args.put("marginRight", margins.getRightMils() * 72.0 / 1000.0);
  120 + args.put("marginBottom", margins.getBottomMils() * 72.0 / 1000.0);
  121 +
  122 + channel.invokeMethod("onLayout", args);
  123 + }
  124 +
  125 + @Override
  126 + public void onFinish() {
  127 + // noinspection ResultOfMethodCallIgnored
  128 + }
  129 +
  130 + @Override
67 public void onMethodCall(MethodCall call, Result result) { 131 public void onMethodCall(MethodCall call, Result result) {
68 switch (call.method) { 132 switch (call.method) {
69 case "printPdf": 133 case "printPdf":
70 - printPdf((byte[]) call.argument("doc")); 134 + jobName =
  135 + call.argument("name") == null ? "Document" : (String) call.argument("name");
  136 + assert jobName != null;
  137 + printManager.print(jobName, this, null);
71 result.success(0); 138 result.success(0);
72 break; 139 break;
73 - case "sharePdf":  
74 - sharePdf((byte[]) call.argument("doc"));  
75 - result.success(0);  
76 - break;  
77 - default:  
78 - result.notImplemented();  
79 - break;  
80 - }  
81 - }  
82 -  
83 - private void printPdf(final byte[] documentData) {  
84 - PrintDocumentAdapter pda = new PrintDocumentAdapter() {  
85 - PrintedPdfDocument mPdfDocument;  
86 -  
87 - @Override  
88 - public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor parcelFileDescriptor,  
89 - CancellationSignal cancellationSignal,  
90 - WriteResultCallback writeResultCallback) {  
91 - OutputStream output = null;  
92 - try {  
93 - output = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());  
94 - output.write(documentData, 0, documentData.length);  
95 - writeResultCallback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});  
96 - } catch (IOException e) {  
97 - e.printStackTrace();  
98 - } finally {  
99 - try {  
100 - if (output != null) {  
101 - output.close();  
102 - }  
103 - } catch (IOException e) {  
104 - e.printStackTrace();  
105 - }  
106 - }  
107 - }  
108 -  
109 - @Override  
110 - public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,  
111 - CancellationSignal cancellationSignal, LayoutResultCallback callback,  
112 - Bundle extras) {  
113 - // Create a new PdfDocument with the requested page attributes  
114 - mPdfDocument = new PrintedPdfDocument(activity, newAttributes);  
115 -  
116 - // Respond to cancellation request  
117 - if (cancellationSignal.isCanceled()) {  
118 - callback.onLayoutCancelled();  
119 - return;  
120 - } 140 + case "writePdf":
  141 + documentData = (byte[]) call.argument("doc");
121 142
122 // Return print information to print framework 143 // Return print information to print framework
123 PrintDocumentInfo info = 144 PrintDocumentInfo info =
124 - new PrintDocumentInfo.Builder("document.pdf") 145 + new PrintDocumentInfo.Builder(jobName + ".pdf")
125 .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) 146 .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
126 .build(); 147 .build();
  148 +
127 // Content layout reflow is complete 149 // Content layout reflow is complete
128 callback.onLayoutFinished(info, true); 150 callback.onLayoutFinished(info, true);
129 - }  
130 151
131 - @Override  
132 - public void onFinish() {  
133 - // noinspection ResultOfMethodCallIgnored  
134 - }  
135 - };  
136 - String jobName = "Document";  
137 - printManager.print(jobName, pda, null); 152 + result.success(0);
  153 + break;
  154 + case "sharePdf":
  155 + sharePdf((byte[]) call.argument("doc"));
  156 + result.success(0);
  157 + break;
  158 + default:
  159 + result.notImplemented();
  160 + break;
  161 + }
138 } 162 }
139 163
140 private void sharePdf(byte[] data) { 164 private void sharePdf(byte[] data) {
@@ -25,8 +25,9 @@ class MyAppState extends State<MyApp> { @@ -25,8 +25,9 @@ class MyAppState extends State<MyApp> {
25 25
26 void _printPdf() async { 26 void _printPdf() async {
27 print("Print ..."); 27 print("Print ...");
28 - final pdf = await generateDocument(PdfPageFormat.a4);  
29 - Printing.printPdf(document: pdf); 28 + await Printing.layoutPdf(
  29 + onLayout: (PdfPageFormat format) async =>
  30 + (await generateDocument(format)).save());
30 } 31 }
31 32
32 void _sharePdf() async { 33 void _sharePdf() async {
@@ -46,45 +47,49 @@ class MyAppState extends State<MyApp> { @@ -46,45 +47,49 @@ class MyAppState extends State<MyApp> {
46 } 47 }
47 48
48 Future<void> _printScreen() async { 49 Future<void> _printScreen() async {
49 - final pdf = PdfDocument(deflate: zlib.encode);  
50 - final page = PdfPage(pdf, pageFormat: PdfPageFormat.a4);  
51 - final g = page.getGraphics();  
52 -  
53 RenderRepaintBoundary boundary = 50 RenderRepaintBoundary boundary =
54 previewContainer.currentContext.findRenderObject(); 51 previewContainer.currentContext.findRenderObject();
55 final im = await boundary.toImage(); 52 final im = await boundary.toImage();
56 final bytes = await im.toByteData(format: ImageByteFormat.rawRgba); 53 final bytes = await im.toByteData(format: ImageByteFormat.rawRgba);
57 print("Print Screen ${im.width}x${im.height} ..."); 54 print("Print Screen ${im.width}x${im.height} ...");
58 55
59 - // Center the image  
60 - final w = page.pageFormat.width -  
61 - page.pageFormat.marginLeft -  
62 - page.pageFormat.marginRight;  
63 - final h = page.pageFormat.height -  
64 - page.pageFormat.marginTop -  
65 - page.pageFormat.marginBottom;  
66 - double iw, ih;  
67 - if (im.width.toDouble() / im.height.toDouble() < 1.0) {  
68 - ih = h;  
69 - iw = im.width.toDouble() * ih / im.height.toDouble();  
70 - } else {  
71 - iw = w;  
72 - ih = im.height.toDouble() * iw / im.width.toDouble();  
73 - }  
74 -  
75 - PdfImage image = PdfImage(pdf,  
76 - image: bytes.buffer.asUint8List(), width: im.width, height: im.height);  
77 - g.drawImage(  
78 - image,  
79 - page.pageFormat.marginLeft + (w - iw) / 2.0,  
80 - page.pageFormat.height -  
81 - page.pageFormat.marginTop -  
82 - ih -  
83 - (h - ih) / 2.0,  
84 - iw,  
85 - ih);  
86 -  
87 - Printing.printPdf(document: pdf); 56 + Printing.layoutPdf(onLayout: (PdfPageFormat format) {
  57 + final pdf = PdfDocument(deflate: zlib.encode);
  58 + final page = PdfPage(pdf, pageFormat: format);
  59 + final g = page.getGraphics();
  60 +
  61 + // Center the image
  62 + final w = page.pageFormat.width -
  63 + page.pageFormat.marginLeft -
  64 + page.pageFormat.marginRight;
  65 + final h = page.pageFormat.height -
  66 + page.pageFormat.marginTop -
  67 + page.pageFormat.marginBottom;
  68 + double iw, ih;
  69 + if (im.width.toDouble() / im.height.toDouble() < 1.0) {
  70 + ih = h;
  71 + iw = im.width.toDouble() * ih / im.height.toDouble();
  72 + } else {
  73 + iw = w;
  74 + ih = im.height.toDouble() * iw / im.width.toDouble();
  75 + }
  76 +
  77 + PdfImage image = PdfImage(pdf,
  78 + image: bytes.buffer.asUint8List(),
  79 + width: im.width,
  80 + height: im.height);
  81 + g.drawImage(
  82 + image,
  83 + page.pageFormat.marginLeft + (w - iw) / 2.0,
  84 + page.pageFormat.height -
  85 + page.pageFormat.marginTop -
  86 + ih -
  87 + (h - ih) / 2.0,
  88 + iw,
  89 + ih);
  90 +
  91 + return pdf.save();
  92 + });
88 } 93 }
89 94
90 @override 95 @override
  1 +/*
  2 + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +#import <Flutter/Flutter.h>
  18 +
  19 +@interface PdfPrintPageRenderer : UIPrintPageRenderer
  20 +
  21 +- (instancetype)init:(FlutterMethodChannel*)channel;
  22 +- (void)drawPageAtIndex:(NSInteger)pageIndex inRect:(CGRect)printableRect;
  23 +
  24 +@property(nonatomic, readonly) NSInteger numberOfPages;
  25 +@property(nonatomic, readonly) NSLock* lock;
  26 +@property(nonatomic) CGPDFDocumentRef pdfDocument;
  27 +@end
  1 +/*
  2 + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +#import "PageRenderer.h"
  18 +
  19 +@implementation PdfPrintPageRenderer {
  20 + FlutterMethodChannel* channel;
  21 +}
  22 +
  23 +@synthesize lock;
  24 +@synthesize pdfDocument;
  25 +
  26 +- (instancetype)init:(FlutterMethodChannel*)channel {
  27 + self = [super init];
  28 + self->channel = channel;
  29 + self->lock = [[NSLock alloc] init];
  30 + self->pdfDocument = nil;
  31 + return self;
  32 +}
  33 +
  34 +- (NSInteger)numberOfPages {
  35 + NSNumber* width = [[NSNumber alloc] initWithDouble:self.paperRect.size.width];
  36 + NSNumber* height =
  37 + [[NSNumber alloc] initWithDouble:self.paperRect.size.height];
  38 + NSNumber* marginLeft =
  39 + [[NSNumber alloc] initWithDouble:self.printableRect.origin.x];
  40 + NSNumber* marginTop =
  41 + [[NSNumber alloc] initWithDouble:self.printableRect.origin.y];
  42 + NSNumber* marginRight =
  43 + [[NSNumber alloc] initWithDouble:self.paperRect.size.width -
  44 + (self.printableRect.origin.x +
  45 + self.printableRect.size.width)];
  46 + NSNumber* marginBottom =
  47 + [[NSNumber alloc] initWithDouble:self.paperRect.size.height -
  48 + (self.printableRect.origin.y +
  49 + self.printableRect.size.height)];
  50 +
  51 + NSDictionary* arg = @{
  52 + @"width" : width,
  53 + @"height" : height,
  54 + @"marginLeft" : marginLeft,
  55 + @"marginTop" : marginTop,
  56 + @"marginRight" : marginRight,
  57 + @"marginBottom" : marginBottom,
  58 + };
  59 +
  60 + [lock lock];
  61 + [channel invokeMethod:@"onLayout" arguments:arg];
  62 + [lock lock];
  63 + [lock unlock];
  64 +
  65 + size_t pages = CGPDFDocumentGetNumberOfPages(pdfDocument);
  66 +
  67 + return pages;
  68 +}
  69 +
  70 +- (void)drawPageAtIndex:(NSInteger)pageIndex inRect:(CGRect)printableRect {
  71 + CGContextRef ctx = UIGraphicsGetCurrentContext();
  72 + CGPDFPageRef page = CGPDFDocumentGetPage(pdfDocument, pageIndex + 1);
  73 + CGContextScaleCTM(ctx, 1.0, -1.0);
  74 + CGContextTranslateCTM(ctx, 0.0, -self.paperRect.size.height);
  75 + CGContextDrawPDFPage(ctx, page);
  76 +}
  77 +
  78 +@end
@@ -18,4 +18,6 @@ @@ -18,4 +18,6 @@
18 18
19 @interface PrintingPlugin 19 @interface PrintingPlugin
20 : NSObject <FlutterPlugin, UIPrintInteractionControllerDelegate> 20 : NSObject <FlutterPlugin, UIPrintInteractionControllerDelegate>
  21 +
  22 +- (instancetype)init:(FlutterMethodChannel*)channel;
21 @end 23 @end
@@ -15,19 +15,34 @@ @@ -15,19 +15,34 @@
15 */ 15 */
16 16
17 #import "PrintingPlugin.h" 17 #import "PrintingPlugin.h"
  18 +#import "PageRenderer.h"
  19 +
  20 +@implementation PrintingPlugin {
  21 + FlutterMethodChannel* channel;
  22 + PdfPrintPageRenderer* renderer;
  23 +}
18 24
19 -@implementation PrintingPlugin  
20 + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar { 25 + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
21 FlutterMethodChannel* channel = 26 FlutterMethodChannel* channel =
22 [FlutterMethodChannel methodChannelWithName:@"printing" 27 [FlutterMethodChannel methodChannelWithName:@"printing"
23 binaryMessenger:[registrar messenger]]; 28 binaryMessenger:[registrar messenger]];
24 - PrintingPlugin* instance = [[PrintingPlugin alloc] init]; 29 + PrintingPlugin* instance = [[PrintingPlugin alloc] init:channel];
25 [registrar addMethodCallDelegate:instance channel:channel]; 30 [registrar addMethodCallDelegate:instance channel:channel];
26 } 31 }
27 32
  33 +- (instancetype)init:(FlutterMethodChannel*)channel {
  34 + self = [super init];
  35 + self->channel = channel;
  36 + self->renderer = [[PdfPrintPageRenderer alloc] init:channel];
  37 + return self;
  38 +}
  39 +
28 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { 40 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
29 if ([@"printPdf" isEqualToString:call.method]) { 41 if ([@"printPdf" isEqualToString:call.method]) {
30 - [self printPdf:[call.arguments objectForKey:@"doc"]]; 42 + [self printPdf:[call.arguments objectForKey:@"name"]];
  43 + result(@1);
  44 + } else if ([@"writePdf" isEqualToString:call.method]) {
  45 + [self writePdf:[call.arguments objectForKey:@"doc"]];
31 result(@1); 46 result(@1);
32 } else if ([@"sharePdf" isEqualToString:call.method]) { 47 } else if ([@"sharePdf" isEqualToString:call.method]) {
33 [self sharePdf:[call.arguments objectForKey:@"doc"] 48 [self sharePdf:[call.arguments objectForKey:@"doc"]
@@ -42,22 +57,22 @@ @@ -42,22 +57,22 @@
42 } 57 }
43 } 58 }
44 59
45 -- (void)printPdf:(nonnull FlutterStandardTypedData*)data { 60 +- (void)printPdf:(nonnull NSString*)name {
46 BOOL printing = [UIPrintInteractionController isPrintingAvailable]; 61 BOOL printing = [UIPrintInteractionController isPrintingAvailable];
47 if (!printing) { 62 if (!printing) {
48 NSLog(@"printing not available"); 63 NSLog(@"printing not available");
49 return; 64 return;
50 } 65 }
51 66
52 - BOOL dataOK = [UIPrintInteractionController canPrintData:[data data]];  
53 - if (!dataOK)  
54 - NSLog(@"data not ok to be printed");  
55 -  
56 UIPrintInteractionController* controller = 67 UIPrintInteractionController* controller =
57 [UIPrintInteractionController sharedPrintController]; 68 [UIPrintInteractionController sharedPrintController];
58 [controller setDelegate:self]; 69 [controller setDelegate:self];
59 - [controller setPrintingItem:[data data]];  
60 70
  71 + UIPrintInfo* printInfo = [UIPrintInfo printInfo];
  72 + printInfo.jobName = name;
  73 + printInfo.outputType = UIPrintInfoOutputGeneral;
  74 + controller.printInfo = printInfo;
  75 + [controller setPrintPageRenderer:renderer];
61 UIPrintInteractionCompletionHandler completionHandler = 76 UIPrintInteractionCompletionHandler completionHandler =
62 ^(UIPrintInteractionController* printController, BOOL completed, 77 ^(UIPrintInteractionController* printController, BOOL completed,
63 NSError* error) { 78 NSError* error) {
@@ -65,11 +80,27 @@ @@ -65,11 +80,27 @@
65 NSLog(@"FAILED! due to error in domain %@ with error code %u", 80 NSLog(@"FAILED! due to error in domain %@ with error code %u",
66 error.domain, (unsigned int)error.code); 81 error.domain, (unsigned int)error.code);
67 } 82 }
  83 + if (self->renderer.pdfDocument != nil) {
  84 + CGPDFDocumentRelease(self->renderer.pdfDocument);
  85 + self->renderer.pdfDocument = nil;
  86 + }
68 }; 87 };
69 88
70 [controller presentAnimated:YES completionHandler:completionHandler]; 89 [controller presentAnimated:YES completionHandler:completionHandler];
71 } 90 }
72 91
  92 +- (void)writePdf:(nonnull FlutterStandardTypedData*)data {
  93 + CGDataProviderRef dataProvider = CGDataProviderCreateWithData(
  94 + NULL, data.data.bytes, data.data.length, NULL);
  95 + if (renderer.pdfDocument != nil) {
  96 + CGPDFDocumentRelease(renderer.pdfDocument);
  97 + renderer.pdfDocument = nil;
  98 + }
  99 + renderer.pdfDocument = CGPDFDocumentCreateWithProvider(dataProvider);
  100 + CGDataProviderRelease(dataProvider);
  101 + [renderer.lock unlock];
  102 +}
  103 +
73 - (void)sharePdf:(nonnull FlutterStandardTypedData*)data 104 - (void)sharePdf:(nonnull FlutterStandardTypedData*)data
74 withSourceRect:(CGRect)rect { 105 withSourceRect:(CGRect)rect {
75 NSURL* tmpDirURL = [NSURL fileURLWithPath:NSTemporaryDirectory() 106 NSURL* tmpDirURL = [NSURL fileURLWithPath:NSTemporaryDirectory()
@@ -16,20 +16,47 @@ @@ -16,20 +16,47 @@
16 16
17 part of printing; 17 part of printing;
18 18
  19 +typedef LayoutCallback = FutureOr<List<int>> Function(PdfPageFormat format);
  20 +
19 class Printing { 21 class Printing {
20 static const MethodChannel _channel = MethodChannel('printing'); 22 static const MethodChannel _channel = MethodChannel('printing');
  23 + static LayoutCallback _onLayout;
  24 +
  25 + static Future<dynamic> _handleMethod(MethodCall call) async {
  26 + switch (call.method) {
  27 + case "onLayout":
  28 + final bytes = await _onLayout(PdfPageFormat(
  29 + call.arguments['width'],
  30 + call.arguments['height'],
  31 + marginLeft: call.arguments['marginLeft'],
  32 + marginTop: call.arguments['marginTop'],
  33 + marginRight: call.arguments['marginRight'],
  34 + marginBottom: call.arguments['marginBottom'],
  35 + ));
  36 + final Map<String, dynamic> params = <String, dynamic>{
  37 + 'doc': Uint8List.fromList(bytes),
  38 + };
  39 + await _channel.invokeMethod('writePdf', params);
  40 + return Future.value("");
  41 + }
  42 + }
  43 +
  44 + static Future<Null> layoutPdf(
  45 + {@required LayoutCallback onLayout, String name = "Document"}) async {
  46 + _onLayout = onLayout;
  47 + _channel.setMethodCallHandler(_handleMethod);
  48 + final Map<String, dynamic> params = <String, dynamic>{'name': name};
  49 + await _channel.invokeMethod('printPdf', params);
  50 + }
21 51
  52 + @deprecated
22 static Future<Null> printPdf({PdfDocument document, List<int> bytes}) async { 53 static Future<Null> printPdf({PdfDocument document, List<int> bytes}) async {
23 assert(document != null || bytes != null); 54 assert(document != null || bytes != null);
24 assert(!(document == null && bytes == null)); 55 assert(!(document == null && bytes == null));
25 56
26 - if (document != null) bytes = document.save();  
27 -  
28 - final Map<String, dynamic> params = <String, dynamic>{  
29 - 'doc': Uint8List.fromList(bytes),  
30 - };  
31 -  
32 - await _channel.invokeMethod('printPdf', params); 57 + layoutPdf(
  58 + onLayout: (PdfPageFormat format) =>
  59 + document != null ? document.save() : bytes);
33 } 60 }
34 61
35 static Future<Null> sharePdf( 62 static Future<Null> sharePdf(