Showing
25 changed files
with
321 additions
and
210 deletions
Makefile
0 → 100644
1 | + # Copyright (C) 2018, David PHAM-VAN <dev.nfet.net@gmail.com> | ||
2 | + # | ||
3 | + # This library is free software; you can redistribute it and/or | ||
4 | + # modify it under the terms of the GNU Lesser General | ||
5 | + # License as published by the Free Software Foundation; either | ||
6 | + # version 2.1 of the License, or (at your option) any later version. | ||
7 | + # | ||
8 | + # This library is distributed in the hope that it will be useful, | ||
9 | + # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
11 | + # Lesser General License for more details. | ||
12 | + # | ||
13 | + # You should have received a copy of the GNU Lesser General | ||
14 | + # License along with this library; if not, write to the Free Software | ||
15 | + # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
16 | + | ||
17 | + DART_SRC=$(shell find . -name '*.dart') | ||
18 | + CLNG_SRC=$(shell find . -name '*.java' -o -name '*.m' -o -name '*.h') | ||
19 | + | ||
20 | +all: pdf/open-sans.ttf format | ||
21 | + | ||
22 | +pdf/open-sans.ttf: | ||
23 | + curl -L "https://github.com/google/fonts/raw/master/apache/opensans/OpenSans-Regular.ttf" > $@ | ||
24 | + | ||
25 | +format: format-dart format-clang | ||
26 | + | ||
27 | +format-dart: $(DART_SRC) | ||
28 | + dartfmt -w $^ | ||
29 | + | ||
30 | +format-clang: $(CLNG_SRC) | ||
31 | + clang-format -style=Chromium -i $^ | ||
32 | + | ||
33 | +test: pdf/open-sans.ttf | ||
34 | + cd pdf; for TEST in $(shell cd pdf; find test -name '*.dart'); do dart $$TEST; done | ||
35 | + cd printing; flutter test | ||
36 | + | ||
37 | +clean: | ||
38 | + git clean -fdx | ||
39 | + | ||
40 | +.PHONY: test format format-dart format-clang clean |
@@ -10,13 +10,13 @@ void main() { | @@ -10,13 +10,13 @@ void main() { | ||
10 | final top = page.pageFormat.height; | 10 | final top = page.pageFormat.height; |
11 | 11 | ||
12 | g.setColor(new PDFColor(0.0, 1.0, 1.0)); | 12 | g.setColor(new PDFColor(0.0, 1.0, 1.0)); |
13 | - g.drawRect(50.0 * PDFPageFormat.MM, top - 80.0 * PDFPageFormat.MM, 100.0 * PDFPageFormat.MM, | ||
14 | - 50.0 * PDFPageFormat.MM); | 13 | + g.drawRect(50.0 * PDFPageFormat.MM, top - 80.0 * PDFPageFormat.MM, |
14 | + 100.0 * PDFPageFormat.MM, 50.0 * PDFPageFormat.MM); | ||
15 | g.fillPath(); | 15 | g.fillPath(); |
16 | 16 | ||
17 | g.setColor(new PDFColor(0.3, 0.3, 0.3)); | 17 | g.setColor(new PDFColor(0.3, 0.3, 0.3)); |
18 | - g.drawString( | ||
19 | - font, 12.0, "Hello World!", 10.0 * PDFPageFormat.MM, top - 10.0 * PDFPageFormat.MM); | 18 | + g.drawString(font, 12.0, "Hello World!", 10.0 * PDFPageFormat.MM, |
19 | + top - 10.0 * PDFPageFormat.MM); | ||
20 | 20 | ||
21 | var file = new File('example.pdf'); | 21 | var file = new File('example.pdf'); |
22 | file.writeAsBytesSync(pdf.save()); | 22 | file.writeAsBytesSync(pdf.save()); |
@@ -91,7 +91,8 @@ class PDFAnnot extends PDFObject { | @@ -91,7 +91,8 @@ class PDFAnnot extends PDFObject { | ||
91 | /// @param r Right coordinate | 91 | /// @param r Right coordinate |
92 | /// @param t Top coordinate | 92 | /// @param t Top coordinate |
93 | /// @param s Text for this annotation | 93 | /// @param s Text for this annotation |
94 | - factory PDFAnnot.text(PDFPage pdfPage, double l, double b, double r, double t, String s) => | 94 | + factory PDFAnnot.text( |
95 | + PDFPage pdfPage, double l, double b, double r, double t, String s) => | ||
95 | new PDFAnnot(pdfPage, type: "/Text", l: l, b: b, r: r, t: t, s: s); | 96 | new PDFAnnot(pdfPage, type: "/Text", l: l, b: b, r: r, t: t, s: s); |
96 | 97 | ||
97 | /// Creates a link annotation | 98 | /// Creates a link annotation |
@@ -106,14 +107,23 @@ class PDFAnnot extends PDFObject { | @@ -106,14 +107,23 @@ class PDFAnnot extends PDFObject { | ||
106 | /// @param ft Top coordinate | 107 | /// @param ft Top coordinate |
107 | /// <br><br>Rectangle describing what part of the page to be displayed | 108 | /// <br><br>Rectangle describing what part of the page to be displayed |
108 | /// (must be in User Coordinates) | 109 | /// (must be in User Coordinates) |
109 | - factory PDFAnnot.link( | ||
110 | - PDFPage pdfPage, double l, double b, double r, double t, PDFObject dest, | 110 | + factory PDFAnnot.link(PDFPage pdfPage, double l, double b, double r, double t, |
111 | + PDFObject dest, | ||
111 | [double fl = FULL_PAGE, | 112 | [double fl = FULL_PAGE, |
112 | double fb = FULL_PAGE, | 113 | double fb = FULL_PAGE, |
113 | double fr = FULL_PAGE, | 114 | double fr = FULL_PAGE, |
114 | double ft = FULL_PAGE]) => | 115 | double ft = FULL_PAGE]) => |
115 | new PDFAnnot(pdfPage, | 116 | new PDFAnnot(pdfPage, |
116 | - type: "/Link", l: l, b: b, r: r, t: t, dest: dest, fl: fl, fb: fb, fr: fr, ft: ft); | 117 | + type: "/Link", |
118 | + l: l, | ||
119 | + b: b, | ||
120 | + r: r, | ||
121 | + t: t, | ||
122 | + dest: dest, | ||
123 | + fl: fl, | ||
124 | + fb: fb, | ||
125 | + fr: fr, | ||
126 | + ft: ft); | ||
117 | 127 | ||
118 | /// Sets the border for the annotation. By default, no border is defined. | 128 | /// Sets the border for the annotation. By default, no border is defined. |
119 | /// | 129 | /// |
@@ -50,6 +50,7 @@ class PDFCatalog extends PDFObject { | @@ -50,6 +50,7 @@ class PDFCatalog extends PDFObject { | ||
50 | } | 50 | } |
51 | 51 | ||
52 | // the /PageMode setting | 52 | // the /PageMode setting |
53 | - params["/PageMode"] = PDFStream.string(PDFDocument._PDF_PAGE_MODES[pageMode.index]); | 53 | + params["/PageMode"] = |
54 | + PDFStream.string(PDFDocument._PDF_PAGE_MODES[pageMode.index]); | ||
54 | } | 55 | } |
55 | } | 56 | } |
@@ -29,8 +29,11 @@ class PDFColor { | @@ -29,8 +29,11 @@ class PDFColor { | ||
29 | PDFColor(this.r, this.g, this.b, [this.a = 1.0]); | 29 | PDFColor(this.r, this.g, this.b, [this.a = 1.0]); |
30 | 30 | ||
31 | factory PDFColor.fromInt(int color) { | 31 | factory PDFColor.fromInt(int color) { |
32 | - return new PDFColor((color >> 16 & 0xff) / 255.0, (color >> 8 & 0xff) / 255.0, | ||
33 | - (color & 0xff) / 255.0, (color >> 24 & 0xff) / 255.0); | 32 | + return new PDFColor( |
33 | + (color >> 16 & 0xff) / 255.0, | ||
34 | + (color >> 8 & 0xff) / 255.0, | ||
35 | + (color & 0xff) / 255.0, | ||
36 | + (color >> 24 & 0xff) / 255.0); | ||
34 | } | 37 | } |
35 | 38 | ||
36 | factory PDFColor.fromHex(String color) { | 39 | factory PDFColor.fromHex(String color) { |
@@ -32,7 +32,8 @@ class PDFFont extends PDFObject { | @@ -32,7 +32,8 @@ class PDFFont extends PDFObject { | ||
32 | /// @param type The pdf type, ie /Type1 | 32 | /// @param type The pdf type, ie /Type1 |
33 | /// @param font The font name, ie Helvetica | 33 | /// @param font The font name, ie Helvetica |
34 | /// @param style The java.awt.Font style, ie: Font.PLAIN | 34 | /// @param style The java.awt.Font style, ie: Font.PLAIN |
35 | - PDFFont(PDFDocument pdfDocument, {this.subtype = "/Type1", this.baseFont = "/Helvetica"}) | 35 | + PDFFont(PDFDocument pdfDocument, |
36 | + {this.subtype = "/Type1", this.baseFont = "/Helvetica"}) | ||
36 | : super(pdfDocument, "/Font") { | 37 | : super(pdfDocument, "/Font") { |
37 | pdfDocument.fonts.add(this); | 38 | pdfDocument.fonts.add(this); |
38 | } | 39 | } |
@@ -22,7 +22,8 @@ class PDFFontDescriptor extends PDFObject { | @@ -22,7 +22,8 @@ class PDFFontDescriptor extends PDFObject { | ||
22 | final PDFObjectStream file; | 22 | final PDFObjectStream file; |
23 | final PDFTTFFont ttfFont; | 23 | final PDFTTFFont ttfFont; |
24 | 24 | ||
25 | - PDFFontDescriptor(this.ttfFont, this.file) : super(ttfFont.pdfDocument, "/FontDescriptor"); | 25 | + PDFFontDescriptor(this.ttfFont, this.file) |
26 | + : super(ttfFont.pdfDocument, "/FontDescriptor"); | ||
26 | 27 | ||
27 | @override | 28 | @override |
28 | void prepare() { | 29 | void prepare() { |
@@ -32,8 +33,12 @@ class PDFFontDescriptor extends PDFObject { | @@ -32,8 +33,12 @@ class PDFFontDescriptor extends PDFObject { | ||
32 | params["/FontFile2"] = file.ref(); | 33 | params["/FontFile2"] = file.ref(); |
33 | params["/Flags"] = PDFStream.intNum(32); | 34 | params["/Flags"] = PDFStream.intNum(32); |
34 | params["/FontBBox"] = new PDFStream() | 35 | params["/FontBBox"] = new PDFStream() |
35 | - ..putStringArray( | ||
36 | - [ttfFont.font.xMin, ttfFont.font.yMin, ttfFont.font.xMax, ttfFont.font.yMax]); | 36 | + ..putStringArray([ |
37 | + ttfFont.font.xMin, | ||
38 | + ttfFont.font.yMin, | ||
39 | + ttfFont.font.xMax, | ||
40 | + ttfFont.font.yMax | ||
41 | + ]); | ||
37 | params["/Ascent"] = PDFStream.intNum(ttfFont.font.ascent); | 42 | params["/Ascent"] = PDFStream.intNum(ttfFont.font.ascent); |
38 | params["/Descent"] = PDFStream.intNum(ttfFont.font.descent); | 43 | params["/Descent"] = PDFStream.intNum(ttfFont.font.descent); |
39 | params["/ItalicAngle"] = PDFStream.intNum(0); | 44 | params["/ItalicAngle"] = PDFStream.intNum(0); |
@@ -133,10 +133,14 @@ class PDFGraphics { | @@ -133,10 +133,14 @@ class PDFGraphics { | ||
133 | // Starting point | 133 | // Starting point |
134 | moveTo(x, y - r2); | 134 | moveTo(x, y - r2); |
135 | 135 | ||
136 | - buf.putString("${x + m4 * r1} ${y - r2} ${x + r1} ${y - m4 * r2} ${x + r1} $y c\n"); | ||
137 | - buf.putString("${x + r1} ${y + m4 * r2} ${x + m4 * r1} ${y + r2} $x ${y + r2} c\n"); | ||
138 | - buf.putString("${x - m4 * r1} ${y + r2} ${x - r1} ${y + m4 * r2} ${x - r1} $y c\n"); | ||
139 | - buf.putString("${x - r1} ${y - m4 * r2} ${x - m4 * r1} ${y - r2} $x ${y - r2} c\n"); | 136 | + buf.putString( |
137 | + "${x + m4 * r1} ${y - r2} ${x + r1} ${y - m4 * r2} ${x + r1} $y c\n"); | ||
138 | + buf.putString( | ||
139 | + "${x + r1} ${y + m4 * r2} ${x + m4 * r1} ${y + r2} $x ${y + r2} c\n"); | ||
140 | + buf.putString( | ||
141 | + "${x - m4 * r1} ${y + r2} ${x - r1} ${y + m4 * r2} ${x - r1} $y c\n"); | ||
142 | + buf.putString( | ||
143 | + "${x - r1} ${y - m4 * r2} ${x - m4 * r1} ${y - r2} $x ${y - r2} c\n"); | ||
140 | } | 144 | } |
141 | 145 | ||
142 | /// We override Graphics.drawRect as it doesn't join the 4 lines. | 146 | /// We override Graphics.drawRect as it doesn't join the 4 lines. |
@@ -174,7 +178,8 @@ class PDFGraphics { | @@ -174,7 +178,8 @@ class PDFGraphics { | ||
174 | /// | 178 | /// |
175 | /// @param c Color to use | 179 | /// @param c Color to use |
176 | void setColor(PDFColor color) { | 180 | void setColor(PDFColor color) { |
177 | - buf.putString("${color.r} ${color.g} ${color.b} rg ${color.r} ${color.g} ${color.b} RG\n"); | 181 | + buf.putString( |
182 | + "${color.r} ${color.g} ${color.b} rg ${color.r} ${color.g} ${color.b} RG\n"); | ||
178 | } | 183 | } |
179 | 184 | ||
180 | /// Set the transformation Matrix | 185 | /// Set the transformation Matrix |
@@ -62,7 +62,11 @@ class PDFImage extends PDFXObject { | @@ -62,7 +62,11 @@ class PDFImage extends PDFXObject { | ||
62 | 62 | ||
63 | if (alphaChannel == false && alpha) { | 63 | if (alphaChannel == false && alpha) { |
64 | var _sMask = new PDFImage(pdfDocument, | 64 | var _sMask = new PDFImage(pdfDocument, |
65 | - image: image, width: width, height: height, alpha: alpha, alphaChannel: true); | 65 | + image: image, |
66 | + width: width, | ||
67 | + height: height, | ||
68 | + alpha: alpha, | ||
69 | + alphaChannel: true); | ||
66 | params["/SMask"] = PDFStream.string("${_sMask.objser} 0 R"); | 70 | params["/SMask"] = PDFStream.string("${_sMask.objser} 0 R"); |
67 | } | 71 | } |
68 | 72 |
@@ -34,7 +34,8 @@ class PDFObject { | @@ -34,7 +34,8 @@ class PDFObject { | ||
34 | /// This is usually called by extensors to this class, and sets the | 34 | /// This is usually called by extensors to this class, and sets the |
35 | /// PDF Object Type | 35 | /// PDF Object Type |
36 | /// @param type the PDF Object Type | 36 | /// @param type the PDF Object Type |
37 | - PDFObject(this.pdfDocument, [String type]) : objser = pdfDocument._genSerial() { | 37 | + PDFObject(this.pdfDocument, [String type]) |
38 | + : objser = pdfDocument._genSerial() { | ||
38 | if (type != null) { | 39 | if (type != null) { |
39 | params["/Type"] = PDFStream.string(type); | 40 | params["/Type"] = PDFStream.string(type); |
40 | } | 41 | } |
@@ -52,7 +52,8 @@ class PDFOutline extends PDFObject { | @@ -52,7 +52,8 @@ class PDFOutline extends PDFObject { | ||
52 | /// @param b bottom coordinate | 52 | /// @param b bottom coordinate |
53 | /// @param r right coordinate | 53 | /// @param r right coordinate |
54 | /// @param t top coordinate | 54 | /// @param t top coordinate |
55 | - PDFOutline(PDFDocument pdfDocument, {this.title, this.dest, this.l, this.b, this.r, this.t}) | 55 | + PDFOutline(PDFDocument pdfDocument, |
56 | + {this.title, this.dest, this.l, this.b, this.r, this.t}) | ||
56 | : super(pdfDocument, "/Outlines"); | 57 | : super(pdfDocument, "/Outlines"); |
57 | 58 | ||
58 | /// This method creates an outline, and attaches it to this one. | 59 | /// This method creates an outline, and attaches it to this one. |
@@ -48,7 +48,8 @@ class PDFPage extends PDFObject { | @@ -48,7 +48,8 @@ class PDFPage extends PDFObject { | ||
48 | /// @see PageFormat#LANDSCAPE | 48 | /// @see PageFormat#LANDSCAPE |
49 | /// @see PageFormat#REVERSE_LANDSCAPE | 49 | /// @see PageFormat#REVERSE_LANDSCAPE |
50 | /// @param pageFormat PageFormat describing the page size | 50 | /// @param pageFormat PageFormat describing the page size |
51 | - PDFPage(PDFDocument pdfDocument, {this.pageFormat}) : super(pdfDocument, "/Page") { | 51 | + PDFPage(PDFDocument pdfDocument, {this.pageFormat}) |
52 | + : super(pdfDocument, "/Page") { | ||
52 | pdfDocument.pdfPageList.pages.add(this); | 53 | pdfDocument.pdfPageList.pages.add(this); |
53 | if (pageFormat == null) pageFormat = PDFPageFormat.A4; | 54 | if (pageFormat == null) pageFormat = PDFPageFormat.A4; |
54 | } | 55 | } |
@@ -125,8 +126,8 @@ class PDFPage extends PDFObject { | @@ -125,8 +126,8 @@ class PDFPage extends PDFObject { | ||
125 | var xy2 = cxy(x + w, y); | 126 | var xy2 = cxy(x + w, y); |
126 | var xy3 = cxy(vx, vy + vh); | 127 | var xy3 = cxy(vx, vy + vh); |
127 | var xy4 = cxy(vx + vw, vy); | 128 | var xy4 = cxy(vx + vw, vy); |
128 | - PDFAnnot ob = | ||
129 | - new PDFAnnot.link(this, xy1.w, xy1.h, xy2.w, xy2.h, dest, xy3.w, xy3.h, xy4.w, xy4.h); | 129 | + PDFAnnot ob = new PDFAnnot.link( |
130 | + this, xy1.w, xy1.h, xy2.w, xy2.h, dest, xy3.w, xy3.h, xy4.w, xy4.h); | ||
130 | return ob; | 131 | return ob; |
131 | } | 132 | } |
132 | 133 | ||
@@ -138,7 +139,8 @@ class PDFPage extends PDFObject { | @@ -138,7 +139,8 @@ class PDFPage extends PDFObject { | ||
138 | /// @param w Width of region | 139 | /// @param w Width of region |
139 | /// @param h Height coordinate of region | 140 | /// @param h Height coordinate of region |
140 | /// @return PDFOutline object created, for addSubOutline if required. | 141 | /// @return PDFOutline object created, for addSubOutline if required. |
141 | - PDFOutline addOutline(String title, {double x, double y, double w, double h}) { | 142 | + PDFOutline addOutline(String title, |
143 | + {double x, double y, double w, double h}) { | ||
142 | PDFPoint xy1 = cxy(x, y + h); | 144 | PDFPoint xy1 = cxy(x, y + h); |
143 | PDFPoint xy2 = cxy(x + w, y); | 145 | PDFPoint xy2 = cxy(x + w, y); |
144 | PDFOutline outline = new PDFOutline(pdfDocument, | 146 | PDFOutline outline = new PDFOutline(pdfDocument, |
@@ -107,7 +107,8 @@ class PDFStream { | @@ -107,7 +107,8 @@ class PDFStream { | ||
107 | putString("[" + values.join(" ") + "]"); | 107 | putString("[" + values.join(" ") + "]"); |
108 | } | 108 | } |
109 | 109 | ||
110 | - static PDFStream array(List<PDFStream> values) => new PDFStream()..putArray(values); | 110 | + static PDFStream array(List<PDFStream> values) => |
111 | + new PDFStream()..putArray(values); | ||
111 | 112 | ||
112 | void putDictionary(Map<String, PDFStream> values) { | 113 | void putDictionary(Map<String, PDFStream> values) { |
113 | putString("<< "); | 114 | putString("<< "); |
@@ -86,8 +86,8 @@ class TTFParser { | @@ -86,8 +86,8 @@ class TTFParser { | ||
86 | int offset = bytes.getUint16(pos + 10); | 86 | int offset = bytes.getUint16(pos + 10); |
87 | pos += 12; | 87 | pos += 12; |
88 | if (platformID == 1 && nameID == 6) { | 88 | if (platformID == 1 && nameID == 6) { |
89 | - _fontName = utf8 | ||
90 | - .decode(bytes.buffer.asUint8List(basePosition + stringOffset + offset, length)); | 89 | + _fontName = utf8.decode(bytes.buffer |
90 | + .asUint8List(basePosition + stringOffset + offset, length)); | ||
91 | } | 91 | } |
92 | } | 92 | } |
93 | } | 93 | } |
@@ -164,7 +164,8 @@ class TTFParser { | @@ -164,7 +164,8 @@ class TTFParser { | ||
164 | if (idRangeOffset == 0) { | 164 | if (idRangeOffset == 0) { |
165 | glyphIndex = (idDelta + c) % 65536; | 165 | glyphIndex = (idDelta + c) % 65536; |
166 | } else { | 166 | } else { |
167 | - final glyphIndexAddress = idRangeOffset + 2 * (c - startCode) + idRangeOffsetAddress; | 167 | + final glyphIndexAddress = |
168 | + idRangeOffset + 2 * (c - startCode) + idRangeOffsetAddress; | ||
168 | glyphIndex = bytes.getUint16(glyphIndexAddress); | 169 | glyphIndex = bytes.getUint16(glyphIndexAddress); |
169 | } | 170 | } |
170 | charToGlyphIndexMap[c] = glyphIndex; | 171 | charToGlyphIndexMap[c] = glyphIndex; |
@@ -36,8 +36,9 @@ class PDFXref { | @@ -36,8 +36,9 @@ class PDFXref { | ||
36 | 36 | ||
37 | /// @return The xref in the format of the xref section in the PDF file | 37 | /// @return The xref in the format of the xref section in the PDF file |
38 | String ref() { | 38 | String ref() { |
39 | - String rs = | ||
40 | - offset.toString().padLeft(10, '0') + " " + generation.toString().padLeft(5, '0'); | 39 | + String rs = offset.toString().padLeft(10, '0') + |
40 | + " " + | ||
41 | + generation.toString().padLeft(5, '0'); | ||
41 | 42 | ||
42 | if (generation == 65535) return rs + " f "; | 43 | if (generation == 65535) return rs + " f "; |
43 | return rs + " n "; | 44 | return rs + " n "; |
@@ -20,5 +20,4 @@ package net.nfet.flutter.printing; | @@ -20,5 +20,4 @@ package net.nfet.flutter.printing; | ||
20 | 20 | ||
21 | import android.support.v4.content.FileProvider; | 21 | import android.support.v4.content.FileProvider; |
22 | 22 | ||
23 | -public class PrintFileProvider extends FileProvider { | ||
24 | -} | 23 | +public class PrintFileProvider extends FileProvider {} |
@@ -49,118 +49,121 @@ import io.flutter.plugin.common.PluginRegistry.Registrar; | @@ -49,118 +49,121 @@ import io.flutter.plugin.common.PluginRegistry.Registrar; | ||
49 | * PrintingPlugin | 49 | * PrintingPlugin |
50 | */ | 50 | */ |
51 | public class PrintingPlugin implements MethodCallHandler { | 51 | public class PrintingPlugin implements MethodCallHandler { |
52 | - private static PrintManager printManager; | ||
53 | - private final Activity activity; | ||
54 | - | ||
55 | - private PrintingPlugin(Activity activity) { | ||
56 | - this.activity = activity; | ||
57 | - } | ||
58 | - | ||
59 | - /** | ||
60 | - * Plugin registration. | ||
61 | - */ | ||
62 | - public static void registerWith(Registrar registrar) { | ||
63 | - final MethodChannel channel = new MethodChannel(registrar.messenger(), "printing"); | ||
64 | - channel.setMethodCallHandler(new PrintingPlugin(registrar.activity())); | ||
65 | - printManager = (PrintManager) registrar.activity().getSystemService(Context.PRINT_SERVICE); | ||
66 | - } | ||
67 | - | ||
68 | - @Override | ||
69 | - public void onMethodCall(MethodCall call, Result result) { | ||
70 | - switch (call.method) { | ||
71 | - case "printPdf": | ||
72 | - printPdf((byte[]) call.argument("doc")); | ||
73 | - result.success(0); | ||
74 | - break; | ||
75 | - case "sharePdf": | ||
76 | - sharePdf((byte[]) call.argument("doc")); | ||
77 | - result.success(0); | ||
78 | - break; | ||
79 | - default: | ||
80 | - result.notImplemented(); | ||
81 | - break; | ||
82 | - } | ||
83 | - } | ||
84 | - | ||
85 | - | ||
86 | - private void printPdf(final byte[] badgeData) { | ||
87 | - PrintDocumentAdapter pda = new PrintDocumentAdapter() { | ||
88 | - PrintedPdfDocument mPdfDocument; | ||
89 | - | ||
90 | - @Override | ||
91 | - public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor parcelFileDescriptor, CancellationSignal cancellationSignal, WriteResultCallback writeResultCallback) { | ||
92 | - | ||
93 | - OutputStream output = null; | ||
94 | - try { | ||
95 | - output = new FileOutputStream(parcelFileDescriptor.getFileDescriptor()); | ||
96 | - output.write(badgeData, 0, badgeData.length); | ||
97 | - writeResultCallback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES}); | ||
98 | - } catch (IOException e) { | ||
99 | - e.printStackTrace(); | ||
100 | - } finally { | ||
101 | - try { | ||
102 | - if (output != null) { | ||
103 | - output.close(); | ||
104 | - } | ||
105 | - } catch (IOException e) { | ||
106 | - e.printStackTrace(); | ||
107 | - } | ||
108 | - } | ||
109 | - } | ||
110 | - | ||
111 | - @Override | ||
112 | - public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, 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 | - } | ||
121 | - | ||
122 | - // Return print information to print framework | ||
123 | - PrintDocumentInfo info = new PrintDocumentInfo | ||
124 | - .Builder("badge.pdf") | ||
125 | - .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) | ||
126 | - .setPageCount(1).build(); | ||
127 | - // Content layout reflow is complete | ||
128 | - callback.onLayoutFinished(info, true); | ||
129 | - } | ||
130 | - | ||
131 | - @Override | ||
132 | - public void onFinish() { | ||
133 | - //noinspection ResultOfMethodCallIgnored | ||
134 | - } | ||
135 | - }; | ||
136 | - String jobName = "Badge"; | ||
137 | - printManager.print(jobName, pda, null); | ||
138 | - } | ||
139 | - | ||
140 | - private void sharePdf(byte[] data) { | ||
141 | - try { | ||
142 | - | ||
143 | - final File externalFilesDirectory = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES); | ||
144 | - File shareFile = File.createTempFile("badge", ".pdf", externalFilesDirectory); | ||
145 | - | ||
146 | - FileOutputStream stream = new FileOutputStream(shareFile); | ||
147 | - stream.write(data); | ||
148 | - stream.close(); | ||
149 | - | ||
150 | - Uri apkURI = FileProvider.getUriForFile(activity, | ||
151 | - activity.getApplicationContext().getPackageName() + ".flutter.printing", shareFile); | ||
152 | - | ||
153 | - Intent shareIntent = new Intent(); | ||
154 | - shareIntent.setAction(Intent.ACTION_SEND); | ||
155 | - shareIntent.setType("application/pdf"); | ||
156 | - shareIntent.putExtra(Intent.EXTRA_STREAM, apkURI); | ||
157 | - shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); | ||
158 | - Intent chooserIntent = Intent.createChooser(shareIntent, null); | ||
159 | - activity.startActivity(chooserIntent); | ||
160 | - shareFile.deleteOnExit(); | ||
161 | - } catch (IOException e) { | ||
162 | - e.printStackTrace(); | ||
163 | - } | ||
164 | - } | ||
165 | - | 52 | + private static PrintManager printManager; |
53 | + private final Activity activity; | ||
54 | + | ||
55 | + private PrintingPlugin(Activity activity) { | ||
56 | + this.activity = activity; | ||
57 | + } | ||
58 | + | ||
59 | + /** | ||
60 | + * Plugin registration. | ||
61 | + */ | ||
62 | + public static void registerWith(Registrar registrar) { | ||
63 | + final MethodChannel channel = new MethodChannel(registrar.messenger(), "printing"); | ||
64 | + channel.setMethodCallHandler(new PrintingPlugin(registrar.activity())); | ||
65 | + printManager = (PrintManager) registrar.activity().getSystemService(Context.PRINT_SERVICE); | ||
66 | + } | ||
67 | + | ||
68 | + @Override | ||
69 | + public void onMethodCall(MethodCall call, Result result) { | ||
70 | + switch (call.method) { | ||
71 | + case "printPdf": | ||
72 | + printPdf((byte[]) call.argument("doc")); | ||
73 | + result.success(0); | ||
74 | + break; | ||
75 | + case "sharePdf": | ||
76 | + sharePdf((byte[]) call.argument("doc")); | ||
77 | + result.success(0); | ||
78 | + break; | ||
79 | + default: | ||
80 | + result.notImplemented(); | ||
81 | + break; | ||
82 | + } | ||
83 | + } | ||
84 | + | ||
85 | + private void printPdf(final byte[] badgeData) { | ||
86 | + PrintDocumentAdapter pda = new PrintDocumentAdapter() { | ||
87 | + PrintedPdfDocument mPdfDocument; | ||
88 | + | ||
89 | + @Override | ||
90 | + public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor parcelFileDescriptor, | ||
91 | + CancellationSignal cancellationSignal, | ||
92 | + WriteResultCallback writeResultCallback) { | ||
93 | + OutputStream output = null; | ||
94 | + try { | ||
95 | + output = new FileOutputStream(parcelFileDescriptor.getFileDescriptor()); | ||
96 | + output.write(badgeData, 0, badgeData.length); | ||
97 | + writeResultCallback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES}); | ||
98 | + } catch (IOException e) { | ||
99 | + e.printStackTrace(); | ||
100 | + } finally { | ||
101 | + try { | ||
102 | + if (output != null) { | ||
103 | + output.close(); | ||
104 | + } | ||
105 | + } catch (IOException e) { | ||
106 | + e.printStackTrace(); | ||
107 | + } | ||
108 | + } | ||
109 | + } | ||
110 | + | ||
111 | + @Override | ||
112 | + public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, | ||
113 | + CancellationSignal cancellationSignal, LayoutResultCallback callback, | ||
114 | + Bundle extras) { | ||
115 | + // Create a new PdfDocument with the requested page attributes | ||
116 | + mPdfDocument = new PrintedPdfDocument(activity, newAttributes); | ||
117 | + | ||
118 | + // Respond to cancellation request | ||
119 | + if (cancellationSignal.isCanceled()) { | ||
120 | + callback.onLayoutCancelled(); | ||
121 | + return; | ||
122 | + } | ||
123 | + | ||
124 | + // Return print information to print framework | ||
125 | + PrintDocumentInfo info = | ||
126 | + new PrintDocumentInfo.Builder("badge.pdf") | ||
127 | + .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) | ||
128 | + .setPageCount(1) | ||
129 | + .build(); | ||
130 | + // Content layout reflow is complete | ||
131 | + callback.onLayoutFinished(info, true); | ||
132 | + } | ||
133 | + | ||
134 | + @Override | ||
135 | + public void onFinish() { | ||
136 | + // noinspection ResultOfMethodCallIgnored | ||
137 | + } | ||
138 | + }; | ||
139 | + String jobName = "Badge"; | ||
140 | + printManager.print(jobName, pda, null); | ||
141 | + } | ||
142 | + | ||
143 | + private void sharePdf(byte[] data) { | ||
144 | + try { | ||
145 | + final File externalFilesDirectory = | ||
146 | + activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES); | ||
147 | + File shareFile = File.createTempFile("badge", ".pdf", externalFilesDirectory); | ||
148 | + | ||
149 | + FileOutputStream stream = new FileOutputStream(shareFile); | ||
150 | + stream.write(data); | ||
151 | + stream.close(); | ||
152 | + | ||
153 | + Uri apkURI = FileProvider.getUriForFile(activity, | ||
154 | + activity.getApplicationContext().getPackageName() + ".flutter.printing", | ||
155 | + shareFile); | ||
156 | + | ||
157 | + Intent shareIntent = new Intent(); | ||
158 | + shareIntent.setAction(Intent.ACTION_SEND); | ||
159 | + shareIntent.setType("application/pdf"); | ||
160 | + shareIntent.putExtra(Intent.EXTRA_STREAM, apkURI); | ||
161 | + shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); | ||
162 | + Intent chooserIntent = Intent.createChooser(shareIntent, null); | ||
163 | + activity.startActivity(chooserIntent); | ||
164 | + shareFile.deleteOnExit(); | ||
165 | + } catch (IOException e) { | ||
166 | + e.printStackTrace(); | ||
167 | + } | ||
168 | + } | ||
166 | } | 169 | } |
@@ -22,8 +22,8 @@ class MyApp extends StatelessWidget { | @@ -22,8 +22,8 @@ class MyApp extends StatelessWidget { | ||
22 | g.fillPath(); | 22 | g.fillPath(); |
23 | 23 | ||
24 | g.setColor(new PDFColor(0.3, 0.3, 0.3)); | 24 | g.setColor(new PDFColor(0.3, 0.3, 0.3)); |
25 | - g.drawString( | ||
26 | - font, 12.0, "Hello World!", 10.0 * PDFPageFormat.MM, top - 10.0 * PDFPageFormat.MM); | 25 | + g.drawString(font, 12.0, "Hello World!", 10.0 * PDFPageFormat.MM, |
26 | + top - 10.0 * PDFPageFormat.MM); | ||
27 | 27 | ||
28 | return pdf; | 28 | return pdf; |
29 | } | 29 | } |
@@ -39,9 +39,12 @@ class MyApp extends StatelessWidget { | @@ -39,9 +39,12 @@ class MyApp extends StatelessWidget { | ||
39 | final pdf = _generateDocument(); | 39 | final pdf = _generateDocument(); |
40 | 40 | ||
41 | // Calculate the widget center for iPad sharing popup position | 41 | // Calculate the widget center for iPad sharing popup position |
42 | - final RenderBox referenceBox = shareWidget.currentContext.findRenderObject(); | ||
43 | - final topLeft = referenceBox.localToGlobal(referenceBox.paintBounds.topLeft); | ||
44 | - final bottomRight = referenceBox.localToGlobal(referenceBox.paintBounds.bottomRight); | 42 | + final RenderBox referenceBox = |
43 | + shareWidget.currentContext.findRenderObject(); | ||
44 | + final topLeft = | ||
45 | + referenceBox.localToGlobal(referenceBox.paintBounds.topLeft); | ||
46 | + final bottomRight = | ||
47 | + referenceBox.localToGlobal(referenceBox.paintBounds.bottomRight); | ||
45 | final bounds = new Rect.fromPoints(topLeft, bottomRight); | 48 | final bounds = new Rect.fromPoints(topLeft, bottomRight); |
46 | 49 | ||
47 | Printing.sharePdf(document: pdf, bounds: bounds); | 50 | Printing.sharePdf(document: pdf, bounds: bounds); |
@@ -57,9 +60,12 @@ class MyApp extends StatelessWidget { | @@ -57,9 +60,12 @@ class MyApp extends StatelessWidget { | ||
57 | child: new Column( | 60 | child: new Column( |
58 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, | 61 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
59 | children: <Widget>[ | 62 | children: <Widget>[ |
60 | - new RaisedButton(child: new Text('Print Document'), onPressed: _printPdf), | ||
61 | new RaisedButton( | 63 | new RaisedButton( |
62 | - key: shareWidget, child: new Text('Share Document'), onPressed: _sharePdf), | 64 | + child: new Text('Print Document'), onPressed: _printPdf), |
65 | + new RaisedButton( | ||
66 | + key: shareWidget, | ||
67 | + child: new Text('Share Document'), | ||
68 | + onPressed: _sharePdf), | ||
63 | ], | 69 | ], |
64 | ), | 70 | ), |
65 | ), | 71 | ), |
@@ -18,5 +18,6 @@ | @@ -18,5 +18,6 @@ | ||
18 | 18 | ||
19 | #import <Flutter/Flutter.h> | 19 | #import <Flutter/Flutter.h> |
20 | 20 | ||
21 | -@interface PrintingPlugin : NSObject<FlutterPlugin, UIPrintInteractionControllerDelegate> | 21 | +@interface PrintingPlugin |
22 | + : NSObject <FlutterPlugin, UIPrintInteractionControllerDelegate> | ||
22 | @end | 23 | @end |
@@ -20,9 +20,9 @@ | @@ -20,9 +20,9 @@ | ||
20 | 20 | ||
21 | @implementation PrintingPlugin | 21 | @implementation PrintingPlugin |
22 | + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar { | 22 | + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar { |
23 | - FlutterMethodChannel* channel = [FlutterMethodChannel | ||
24 | - methodChannelWithName:@"printing" | ||
25 | - binaryMessenger:[registrar messenger]]; | 23 | + FlutterMethodChannel* channel = |
24 | + [FlutterMethodChannel methodChannelWithName:@"printing" | ||
25 | + binaryMessenger:[registrar messenger]]; | ||
26 | PrintingPlugin* instance = [[PrintingPlugin alloc] init]; | 26 | PrintingPlugin* instance = [[PrintingPlugin alloc] init]; |
27 | [registrar addMethodCallDelegate:instance channel:channel]; | 27 | [registrar addMethodCallDelegate:instance channel:channel]; |
28 | } | 28 | } |
@@ -32,72 +32,89 @@ | @@ -32,72 +32,89 @@ | ||
32 | [self printPdf:[call.arguments objectForKey:@"doc"]]; | 32 | [self printPdf:[call.arguments objectForKey:@"doc"]]; |
33 | result(@1); | 33 | result(@1); |
34 | } else if ([@"sharePdf" isEqualToString:call.method]) { | 34 | } else if ([@"sharePdf" isEqualToString:call.method]) { |
35 | - [self | ||
36 | - sharePdf:[call.arguments objectForKey:@"doc"] | ||
37 | - withSourceRect:CGRectMake( | ||
38 | - [[call.arguments objectForKey:@"x"] floatValue], | ||
39 | - [[call.arguments objectForKey:@"y"] floatValue], | ||
40 | - [[call.arguments objectForKey:@"w"] floatValue], | ||
41 | - [[call.arguments objectForKey:@"h"] floatValue])]; | 35 | + [self sharePdf:[call.arguments objectForKey:@"doc"] |
36 | + withSourceRect:CGRectMake( | ||
37 | + [[call.arguments objectForKey:@"x"] floatValue], | ||
38 | + [[call.arguments objectForKey:@"y"] floatValue], | ||
39 | + [[call.arguments objectForKey:@"w"] floatValue], | ||
40 | + [[call.arguments objectForKey:@"h"] floatValue])]; | ||
42 | result(@1); | 41 | result(@1); |
43 | } else { | 42 | } else { |
44 | result(FlutterMethodNotImplemented); | 43 | result(FlutterMethodNotImplemented); |
45 | } | 44 | } |
46 | } | 45 | } |
47 | 46 | ||
48 | -- (void)printPdf:(nonnull FlutterStandardTypedData *)data { | ||
49 | - BOOL printing = [UIPrintInteractionController isPrintingAvailable]; | ||
50 | - if (!printing) { | ||
51 | - NSLog(@"printing not available"); | ||
52 | - return; | ||
53 | - } | ||
54 | - | ||
55 | - BOOL dataOK = [UIPrintInteractionController canPrintData:[data data]]; | ||
56 | - if (!dataOK) NSLog(@"data not ok to be printed"); | ||
57 | - | ||
58 | - UIPrintInteractionController *controller = [UIPrintInteractionController sharedPrintController]; | ||
59 | - [controller setDelegate:self]; | ||
60 | - [controller setPrintingItem:[data data]]; | 47 | +- (void)printPdf:(nonnull FlutterStandardTypedData*)data { |
48 | + BOOL printing = [UIPrintInteractionController isPrintingAvailable]; | ||
49 | + if (!printing) { | ||
50 | + NSLog(@"printing not available"); | ||
51 | + return; | ||
52 | + } | ||
61 | 53 | ||
62 | - UIPrintInteractionCompletionHandler completionHandler = ^(UIPrintInteractionController *printController, BOOL completed, NSError *error) { | ||
63 | - if(!completed && error){ | ||
64 | - NSLog(@"FAILED! due to error in domain %@ with error code %u", error.domain, (unsigned int)error.code); | 54 | + BOOL dataOK = [UIPrintInteractionController canPrintData:[data data]]; |
55 | + if (!dataOK) | ||
56 | + NSLog(@"data not ok to be printed"); | ||
57 | + | ||
58 | + UIPrintInteractionController* controller = | ||
59 | + [UIPrintInteractionController sharedPrintController]; | ||
60 | + [controller setDelegate:self]; | ||
61 | + [controller setPrintingItem:[data data]]; | ||
62 | + | ||
63 | + UIPrintInteractionCompletionHandler completionHandler = | ||
64 | + ^(UIPrintInteractionController* printController, BOOL completed, | ||
65 | + NSError* error) { | ||
66 | + if (!completed && error) { | ||
67 | + NSLog(@"FAILED! due to error in domain %@ with error code %u", | ||
68 | + error.domain, (unsigned int)error.code); | ||
65 | } | 69 | } |
66 | - }; | 70 | + }; |
67 | 71 | ||
68 | - [controller presentAnimated:YES completionHandler:completionHandler]; | 72 | + [controller presentAnimated:YES completionHandler:completionHandler]; |
69 | } | 73 | } |
70 | 74 | ||
71 | -- (void)sharePdf:(nonnull FlutterStandardTypedData *)data withSourceRect:(CGRect)rect { | ||
72 | - NSURL *tmpDirURL = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES]; | 75 | +- (void)sharePdf:(nonnull FlutterStandardTypedData*)data |
76 | + withSourceRect:(CGRect)rect { | ||
77 | + NSURL* tmpDirURL = | ||
78 | + [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES]; | ||
73 | 79 | ||
74 | - CFUUIDRef uuid = CFUUIDCreate(NULL); | ||
75 | - assert(uuid != NULL); | 80 | + CFUUIDRef uuid = CFUUIDCreate(NULL); |
81 | + assert(uuid != NULL); | ||
76 | 82 | ||
77 | - CFStringRef uuidStr = CFUUIDCreateString(NULL, uuid); | ||
78 | - assert(uuidStr != NULL); | 83 | + CFStringRef uuidStr = CFUUIDCreateString(NULL, uuid); |
84 | + assert(uuidStr != NULL); | ||
79 | 85 | ||
80 | - NSURL *fileURL = [[tmpDirURL URLByAppendingPathComponent:[NSString stringWithFormat:@"pdf-%@", uuidStr]] URLByAppendingPathExtension:@"pdf"]; | ||
81 | - assert(fileURL != NULL); | 86 | + NSURL* fileURL = [[tmpDirURL |
87 | + URLByAppendingPathComponent:[NSString | ||
88 | + stringWithFormat:@"pdf-%@", uuidStr]] | ||
89 | + URLByAppendingPathExtension:@"pdf"]; | ||
90 | + assert(fileURL != NULL); | ||
82 | 91 | ||
83 | - CFRelease(uuidStr); | ||
84 | - CFRelease(uuid); | 92 | + CFRelease(uuidStr); |
93 | + CFRelease(uuid); | ||
85 | 94 | ||
86 | - NSString *path = [fileURL path]; | 95 | + NSString* path = [fileURL path]; |
87 | 96 | ||
88 | - NSError *error; | ||
89 | - if (![[data data] writeToFile:path options:NSDataWritingAtomic error:&error]) { | ||
90 | - NSLog(@"sharePdf error: %@", [error localizedDescription]); | ||
91 | - return; | ||
92 | - } | 97 | + NSError* error; |
98 | + if (! | ||
99 | + [[data data] writeToFile:path options:NSDataWritingAtomic error:&error]) { | ||
100 | + NSLog(@"sharePdf error: %@", [error localizedDescription]); | ||
101 | + return; | ||
102 | + } | ||
93 | 103 | ||
94 | - UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[fileURL] applicationActivities:nil]; | ||
95 | - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { | ||
96 | - UIViewController *controller = [UIApplication sharedApplication].keyWindow.rootViewController; | ||
97 | - activityViewController.popoverPresentationController.sourceView = controller.view; | ||
98 | - activityViewController.popoverPresentationController.sourceRect = rect; | ||
99 | - } | ||
100 | - [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:activityViewController animated:YES completion:nil]; | 104 | + UIActivityViewController* activityViewController = |
105 | + [[UIActivityViewController alloc] initWithActivityItems:@[ fileURL ] | ||
106 | + applicationActivities:nil]; | ||
107 | + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { | ||
108 | + UIViewController* controller = | ||
109 | + [UIApplication sharedApplication].keyWindow.rootViewController; | ||
110 | + activityViewController.popoverPresentationController.sourceView = | ||
111 | + controller.view; | ||
112 | + activityViewController.popoverPresentationController.sourceRect = rect; | ||
113 | + } | ||
114 | + [[UIApplication sharedApplication].keyWindow.rootViewController | ||
115 | + presentViewController:activityViewController | ||
116 | + animated:YES | ||
117 | + completion:nil]; | ||
101 | } | 118 | } |
102 | 119 | ||
103 | @end | 120 | @end |
@@ -38,7 +38,8 @@ class Printing { | @@ -38,7 +38,8 @@ class Printing { | ||
38 | await _channel.invokeMethod('printPdf', params); | 38 | await _channel.invokeMethod('printPdf', params); |
39 | } | 39 | } |
40 | 40 | ||
41 | - static Future<Null> sharePdf({PDFDocument document, List<int> bytes, Rect bounds}) async { | 41 | + static Future<Null> sharePdf( |
42 | + {PDFDocument document, List<int> bytes, Rect bounds}) async { | ||
42 | assert(document != null || bytes != null); | 43 | assert(document != null || bytes != null); |
43 | assert(!(document == null && bytes == null)); | 44 | assert(!(document == null && bytes == null)); |
44 | 45 |
@@ -2,7 +2,7 @@ name: printing | @@ -2,7 +2,7 @@ name: printing | ||
2 | author: David PHAM-VAN <dev.nfet.net@gmail.com> | 2 | author: David PHAM-VAN <dev.nfet.net@gmail.com> |
3 | description: Plugin that allows Flutter apps to generate and print documents to android or ios compatible printers | 3 | description: Plugin that allows Flutter apps to generate and print documents to android or ios compatible printers |
4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing | 4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing |
5 | -version: 1.0.2 | 5 | +version: 1.0.3 |
6 | 6 | ||
7 | environment: | 7 | environment: |
8 | sdk: ">=1.19.0 <3.0.0" | 8 | sdk: ">=1.19.0 <3.0.0" |
-
Please register or login to post a comment