Showing
8 changed files
with
307 additions
and
112 deletions
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 |
printing/ios/Classes/PageRenderer.h
0 → 100644
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 |
printing/ios/Classes/PageRenderer.m
0 → 100644
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( |
-
Please register or login to post a comment