David PHAM-VAN

Improve Example document

@@ -75,7 +75,7 @@ test-pdf: $(FONTS) get-pdf .coverage @@ -75,7 +75,7 @@ test-pdf: $(FONTS) get-pdf .coverage
75 cd pdf; for EXAMPLE in $(shell cd pdf; find example -name '*.dart'); do dart $$EXAMPLE; done 75 cd pdf; for EXAMPLE in $(shell cd pdf; find example -name '*.dart'); do dart $$EXAMPLE; done
76 76
77 test-printing: $(FONTS) get-printing .coverage 77 test-printing: $(FONTS) get-printing .coverage
78 - cd printing; flutter test --coverage --coverage-path lcov.info 78 + cd printing/example; flutter test --coverage --coverage-path ../lcov.info
79 79
80 test-readme: $(FONTS) get-readme 80 test-readme: $(FONTS) get-readme
81 cd test; dart extract_readme.dart 81 cd test; dart extract_readme.dart
@@ -15,6 +15,7 @@ dependencies: @@ -15,6 +15,7 @@ dependencies:
15 utf: "^0.9.0" 15 utf: "^0.9.0"
16 crypto: "^2.0.6" 16 crypto: "^2.0.6"
17 archive: "^2.0.10" 17 archive: "^2.0.10"
  18 +
18 19
19 dev_dependencies: 20 dev_dependencies:
20 test: any 21 test: any
1 # Changelog 1 # Changelog
2 2
  3 +## 2.1.6
  4 +
  5 +- Add qrcode to example
  6 +
3 ## 2.1.5 7 ## 2.1.5
4 8
5 - Add printing completion 9 - Add printing completion
1 import 'dart:async'; 1 import 'dart:async';
2 2
3 import 'package:flutter/widgets.dart' as fw; 3 import 'package:flutter/widgets.dart' as fw;
4 -  
5 import 'package:pdf/pdf.dart'; 4 import 'package:pdf/pdf.dart';
6 import 'package:pdf/widgets.dart'; 5 import 'package:pdf/widgets.dart';
7 import 'package:printing/printing.dart'; 6 import 'package:printing/printing.dart';
8 7
9 -const PdfColor green = PdfColor.fromInt(0xff9ce5d0);  
10 -const PdfColor lightGreen = PdfColor.fromInt(0xffcdf1e7);  
11 -  
12 -class MyPage extends Page {  
13 - MyPage(  
14 - {PdfPageFormat pageFormat = PdfPageFormat.a4,  
15 - BuildCallback build,  
16 - EdgeInsets margin})  
17 - : super(pageFormat: pageFormat, margin: margin, build: build);  
18 -  
19 - @override  
20 - void paint(Widget child, Context context) {  
21 - context.canvas  
22 - ..setColor(lightGreen)  
23 - ..moveTo(0, pageFormat.height)  
24 - ..lineTo(0, pageFormat.height - 230)  
25 - ..lineTo(60, pageFormat.height)  
26 - ..fillPath()  
27 - ..setColor(green)  
28 - ..moveTo(0, pageFormat.height)  
29 - ..lineTo(0, pageFormat.height - 100)  
30 - ..lineTo(100, pageFormat.height)  
31 - ..fillPath()  
32 - ..setColor(lightGreen)  
33 - ..moveTo(30, pageFormat.height)  
34 - ..lineTo(110, pageFormat.height - 50)  
35 - ..lineTo(150, pageFormat.height)  
36 - ..fillPath()  
37 - ..moveTo(pageFormat.width, 0)  
38 - ..lineTo(pageFormat.width, 230)  
39 - ..lineTo(pageFormat.width - 60, 0)  
40 - ..fillPath()  
41 - ..setColor(green)  
42 - ..moveTo(pageFormat.width, 0)  
43 - ..lineTo(pageFormat.width, 100)  
44 - ..lineTo(pageFormat.width - 100, 0)  
45 - ..fillPath()  
46 - ..setColor(lightGreen)  
47 - ..moveTo(pageFormat.width - 30, 0)  
48 - ..lineTo(pageFormat.width - 110, 50)  
49 - ..lineTo(pageFormat.width - 150, 0)  
50 - ..fillPath();  
51 -  
52 - super.paint(child, context);  
53 - }  
54 -}  
55 -  
56 -class Block extends StatelessWidget {  
57 - Block({this.title});  
58 -  
59 - final String title;  
60 -  
61 - @override  
62 - Widget build(Context context) {  
63 - return Column(  
64 - crossAxisAlignment: CrossAxisAlignment.start,  
65 - children: <Widget>[  
66 - Row(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[  
67 - Container(  
68 - width: 6,  
69 - height: 6,  
70 - margin: const EdgeInsets.only(top: 2.5, left: 2, right: 5),  
71 - decoration:  
72 - const BoxDecoration(color: green, shape: BoxShape.circle),  
73 - ),  
74 - Text(title,  
75 - style: Theme.of(context)  
76 - .defaultTextStyle  
77 - .copyWith(fontWeight: FontWeight.bold)),  
78 - ]),  
79 - Container(  
80 - decoration: const BoxDecoration(  
81 - border: BoxBorder(left: true, color: green, width: 2)),  
82 - padding: const EdgeInsets.only(left: 10, top: 5, bottom: 5),  
83 - margin: const EdgeInsets.only(left: 5),  
84 - child: Column(  
85 - crossAxisAlignment: CrossAxisAlignment.start,  
86 - children: <Widget>[  
87 - Lorem(length: 20),  
88 - ]),  
89 - ),  
90 - ]);  
91 - }  
92 -}  
93 -  
94 -class Category extends StatelessWidget {  
95 - Category({this.title});  
96 -  
97 - final String title;  
98 -  
99 - @override  
100 - Widget build(Context context) {  
101 - return Container(  
102 - decoration: const BoxDecoration(color: lightGreen, borderRadius: 6),  
103 - margin: const EdgeInsets.only(bottom: 10, top: 20),  
104 - padding: const EdgeInsets.fromLTRB(10, 7, 10, 4),  
105 - child: Text(title, textScaleFactor: 1.5));  
106 - }  
107 -} 8 +import 'example_widgets.dart';
108 9
109 Future<Document> generateDocument(PdfPageFormat format) async { 10 Future<Document> generateDocument(PdfPageFormat format) async {
110 final Document pdf = Document(title: 'My Résumé', author: 'David PHAM-VAN'); 11 final Document pdf = Document(title: 'My Résumé', author: 'David PHAM-VAN');
@@ -159,8 +60,10 @@ Future<Document> generateDocument(PdfPageFormat format) async { @@ -159,8 +60,10 @@ Future<Document> generateDocument(PdfPageFormat format) async {
159 crossAxisAlignment: CrossAxisAlignment.start, 60 crossAxisAlignment: CrossAxisAlignment.start,
160 children: <Widget>[ 61 children: <Widget>[
161 Text('+1 403-721-6898'), 62 Text('+1 403-721-6898'),
162 - Text('p.charlesbois@yahoo.com'),  
163 - Text('wholeprices.ca') 63 + UrlText('p.charlesbois@yahoo.com',
  64 + 'mailto:p.charlesbois@yahoo.com'),
  65 + UrlText('wholeprices.ca',
  66 + 'https://wholeprices.ca'),
164 ]), 67 ]),
165 Padding(padding: EdgeInsets.zero) 68 Padding(padding: EdgeInsets.zero)
166 ]), 69 ]),
@@ -175,19 +78,29 @@ Future<Document> generateDocument(PdfPageFormat format) async { @@ -175,19 +78,29 @@ Future<Document> generateDocument(PdfPageFormat format) async {
175 ])), 78 ])),
176 Container( 79 Container(
177 height: double.infinity, 80 height: double.infinity,
178 - width: 10,  
179 - decoration: const BoxDecoration(  
180 - border: BoxBorder(left: true, color: green, width: 2)), 81 + width: 2,
  82 + margin: const EdgeInsets.symmetric(horizontal: 5),
  83 + color: green,
181 ), 84 ),
182 - Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[  
183 - ClipOval(  
184 - child: Container(  
185 - width: 100,  
186 - height: 100,  
187 - color: lightGreen,  
188 - child:  
189 - profileImage == null ? Container() : Image(profileImage)))  
190 - ]) 85 + Column(
  86 + crossAxisAlignment: CrossAxisAlignment.center,
  87 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  88 + children: <Widget>[
  89 + ClipOval(
  90 + child: Container(
  91 + width: 100,
  92 + height: 100,
  93 + color: lightGreen,
  94 + child: profileImage == null
  95 + ? Container()
  96 + : Image(profileImage))),
  97 + Column(children: <Widget>[
  98 + Percent(size: 60, value: .7, title: Text('Word')),
  99 + Percent(size: 60, value: .4, title: Text('Excel')),
  100 + ]),
  101 + QrCodeWidget(data: 'Parnella Charlesbois', size: 60),
  102 + ],
  103 + )
191 ]), 104 ]),
192 )); 105 ));
193 return pdf; 106 return pdf;
  1 +import 'package:meta/meta.dart';
  2 +import 'package:pdf/pdf.dart';
  3 +import 'package:pdf/widgets.dart';
  4 +import 'package:qr/qr.dart';
  5 +
  6 +const PdfColor green = PdfColor.fromInt(0xff9ce5d0);
  7 +const PdfColor lightGreen = PdfColor.fromInt(0xffcdf1e7);
  8 +
  9 +class MyPage extends Page {
  10 + MyPage(
  11 + {PdfPageFormat pageFormat = PdfPageFormat.a4,
  12 + BuildCallback build,
  13 + EdgeInsets margin})
  14 + : super(pageFormat: pageFormat, margin: margin, build: build);
  15 +
  16 + @override
  17 + void paint(Widget child, Context context) {
  18 + context.canvas
  19 + ..setColor(lightGreen)
  20 + ..moveTo(0, pageFormat.height)
  21 + ..lineTo(0, pageFormat.height - 230)
  22 + ..lineTo(60, pageFormat.height)
  23 + ..fillPath()
  24 + ..setColor(green)
  25 + ..moveTo(0, pageFormat.height)
  26 + ..lineTo(0, pageFormat.height - 100)
  27 + ..lineTo(100, pageFormat.height)
  28 + ..fillPath()
  29 + ..setColor(lightGreen)
  30 + ..moveTo(30, pageFormat.height)
  31 + ..lineTo(110, pageFormat.height - 50)
  32 + ..lineTo(150, pageFormat.height)
  33 + ..fillPath()
  34 + ..moveTo(pageFormat.width, 0)
  35 + ..lineTo(pageFormat.width, 230)
  36 + ..lineTo(pageFormat.width - 60, 0)
  37 + ..fillPath()
  38 + ..setColor(green)
  39 + ..moveTo(pageFormat.width, 0)
  40 + ..lineTo(pageFormat.width, 100)
  41 + ..lineTo(pageFormat.width - 100, 0)
  42 + ..fillPath()
  43 + ..setColor(lightGreen)
  44 + ..moveTo(pageFormat.width - 30, 0)
  45 + ..lineTo(pageFormat.width - 110, 50)
  46 + ..lineTo(pageFormat.width - 150, 0)
  47 + ..fillPath();
  48 +
  49 + super.paint(child, context);
  50 + }
  51 +}
  52 +
  53 +class Block extends StatelessWidget {
  54 + Block({this.title});
  55 +
  56 + final String title;
  57 +
  58 + @override
  59 + Widget build(Context context) {
  60 + return Column(
  61 + crossAxisAlignment: CrossAxisAlignment.start,
  62 + children: <Widget>[
  63 + Row(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
  64 + Container(
  65 + width: 6,
  66 + height: 6,
  67 + margin: const EdgeInsets.only(top: 2.5, left: 2, right: 5),
  68 + decoration:
  69 + const BoxDecoration(color: green, shape: BoxShape.circle),
  70 + ),
  71 + Text(title,
  72 + style: Theme.of(context)
  73 + .defaultTextStyle
  74 + .copyWith(fontWeight: FontWeight.bold)),
  75 + ]),
  76 + Container(
  77 + decoration: const BoxDecoration(
  78 + border: BoxBorder(left: true, color: green, width: 2)),
  79 + padding: const EdgeInsets.only(left: 10, top: 5, bottom: 5),
  80 + margin: const EdgeInsets.only(left: 5),
  81 + child: Column(
  82 + crossAxisAlignment: CrossAxisAlignment.start,
  83 + children: <Widget>[
  84 + Lorem(length: 20),
  85 + ]),
  86 + ),
  87 + ]);
  88 + }
  89 +}
  90 +
  91 +class Category extends StatelessWidget {
  92 + Category({this.title});
  93 +
  94 + final String title;
  95 +
  96 + @override
  97 + Widget build(Context context) {
  98 + return Container(
  99 + decoration: const BoxDecoration(color: lightGreen, borderRadius: 6),
  100 + margin: const EdgeInsets.only(bottom: 10, top: 20),
  101 + padding: const EdgeInsets.fromLTRB(10, 7, 10, 4),
  102 + child: Text(title, textScaleFactor: 1.5));
  103 + }
  104 +}
  105 +
  106 +typedef QrError = void Function(dynamic error);
  107 +
  108 +class _QrCodeWidget extends Widget {
  109 + _QrCodeWidget({
  110 + @required String data,
  111 + this.version,
  112 + this.errorCorrectionLevel,
  113 + this.color,
  114 + this.onError,
  115 + this.gapless = false,
  116 + }) : assert(data != null),
  117 + _qr = version == null
  118 + ? QrCode.fromData(
  119 + data: data,
  120 + errorCorrectLevel: errorCorrectionLevel,
  121 + )
  122 + : QrCode(
  123 + version,
  124 + errorCorrectionLevel,
  125 + ) {
  126 + // configure and make the QR code data
  127 + try {
  128 + if (version != null) {
  129 + _qr.addData(data);
  130 + }
  131 + _qr.make();
  132 + } catch (ex) {
  133 + if (onError != null) {
  134 + _hasError = true;
  135 + this.onError(ex);
  136 + }
  137 + }
  138 + }
  139 +
  140 + @override
  141 + void layout(Context context, BoxConstraints constraints,
  142 + {bool parentUsesSize = false}) {
  143 + box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
  144 + }
  145 +
  146 + /// the qr code version
  147 + final int version;
  148 +
  149 + /// the qr code error correction level
  150 + final int errorCorrectionLevel;
  151 +
  152 + /// the color of the dark squares
  153 + final PdfColor color;
  154 +
  155 + final QrError onError;
  156 +
  157 + final bool gapless;
  158 +
  159 + // our qr code data
  160 + final QrCode _qr;
  161 +
  162 + bool _hasError = false;
  163 +
  164 + @override
  165 + void paint(Context context) {
  166 + super.paint(context);
  167 +
  168 + if (_hasError) {
  169 + return;
  170 + }
  171 +
  172 + final double shortestSide = box.width < box.height ? box.width : box.height;
  173 + assert(shortestSide > 0);
  174 +
  175 + context.canvas.setFillColor(color);
  176 + final double squareSize = shortestSide / _qr.moduleCount.toDouble();
  177 + final int pxAdjustValue = gapless ? 1 : 0;
  178 + for (int x = 0; x < _qr.moduleCount; x++) {
  179 + for (int y = 0; y < _qr.moduleCount; y++) {
  180 + if (_qr.isDark(y, x)) {
  181 + context.canvas.drawRect(
  182 + box.left + x * squareSize,
  183 + box.top - (y + 1) * squareSize,
  184 + squareSize + pxAdjustValue,
  185 + squareSize + pxAdjustValue,
  186 + );
  187 + }
  188 + }
  189 + }
  190 +
  191 + context.canvas.fillPath();
  192 + }
  193 +}
  194 +
  195 +class QrCodeWidget extends StatelessWidget {
  196 + QrCodeWidget({
  197 + @required this.data,
  198 + this.version,
  199 + this.errorCorrectionLevel = QrErrorCorrectLevel.L,
  200 + this.color = PdfColors.black,
  201 + this.onError,
  202 + this.gapless = false,
  203 + this.size,
  204 + this.padding,
  205 + });
  206 +
  207 + /// the qr code data
  208 + final String data;
  209 +
  210 + /// the qr code version
  211 + final int version;
  212 +
  213 + /// the qr code error correction level
  214 + final int errorCorrectionLevel;
  215 +
  216 + /// the color of the dark squares
  217 + final PdfColor color;
  218 +
  219 + final QrError onError;
  220 +
  221 + final bool gapless;
  222 +
  223 + final double size;
  224 +
  225 + final EdgeInsets padding;
  226 +
  227 + @override
  228 + Widget build(Context context) {
  229 + Widget qrcode = AspectRatio(
  230 + aspectRatio: 1.0,
  231 + child: _QrCodeWidget(
  232 + data: data,
  233 + version: version,
  234 + errorCorrectionLevel: errorCorrectionLevel,
  235 + color: color,
  236 + onError: onError,
  237 + gapless: gapless,
  238 + ));
  239 +
  240 + if (padding != null) {
  241 + qrcode = Padding(padding: padding, child: qrcode);
  242 + }
  243 +
  244 + if (size != null) {
  245 + qrcode = SizedBox(width: size, height: size, child: qrcode);
  246 + }
  247 +
  248 + return qrcode;
  249 + }
  250 +}
  251 +
  252 +class Percent extends StatelessWidget {
  253 + Percent({
  254 + @required this.size,
  255 + @required this.value,
  256 + this.title,
  257 + this.fontSize = 1.2,
  258 + this.color = green,
  259 + this.backgroundColor = PdfColors.grey300,
  260 + this.strokeWidth = 5,
  261 + }) : assert(size != null);
  262 +
  263 + final double size;
  264 +
  265 + final double value;
  266 +
  267 + final Widget title;
  268 +
  269 + final double fontSize;
  270 +
  271 + final PdfColor color;
  272 +
  273 + final PdfColor backgroundColor;
  274 +
  275 + final double strokeWidth;
  276 +
  277 + @override
  278 + Widget build(Context context) {
  279 + final List<Widget> widgets = <Widget>[
  280 + Container(
  281 + width: size,
  282 + height: size,
  283 + child: Stack(
  284 + alignment: Alignment.center,
  285 + fit: StackFit.expand,
  286 + children: <Widget>[
  287 + Center(
  288 + child: Text(
  289 + '${(value * 100).round().toInt()}%',
  290 + textScaleFactor: fontSize,
  291 + ),
  292 + ),
  293 + CircularProgressIndicator(
  294 + value: value,
  295 + backgroundColor: backgroundColor,
  296 + color: color,
  297 + strokeWidth: strokeWidth,
  298 + ),
  299 + ],
  300 + ),
  301 + )
  302 + ];
  303 +
  304 + if (title != null) {
  305 + widgets.add(title);
  306 + }
  307 +
  308 + return Column(children: widgets);
  309 + }
  310 +}
  311 +
  312 +class UrlText extends StatelessWidget {
  313 + UrlText(this.text, this.url);
  314 +
  315 + final String text;
  316 + final String url;
  317 +
  318 + @override
  319 + Widget build(Context context) {
  320 + return UrlLink(
  321 + destination: url,
  322 + child: Text(text,
  323 + style: TextStyle(
  324 + decoration: TextDecoration.underline,
  325 + color: PdfColors.blue,
  326 + )),
  327 + );
  328 + }
  329 +}
@@ -107,6 +107,11 @@ class MyAppState extends State<MyApp> { @@ -107,6 +107,11 @@ class MyAppState extends State<MyApp> {
107 107
108 @override 108 @override
109 Widget build(BuildContext context) { 109 Widget build(BuildContext context) {
  110 + bool canDebug = false;
  111 + assert(() {
  112 + canDebug = true;
  113 + return true;
  114 + }());
110 return RepaintBoundary( 115 return RepaintBoundary(
111 key: previewContainer, 116 key: previewContainer,
112 child: Scaffold( 117 child: Scaffold(
@@ -130,6 +135,22 @@ class MyAppState extends State<MyApp> { @@ -130,6 +135,22 @@ class MyAppState extends State<MyApp> {
130 child: const Text('Save to file'), onPressed: _saveAsFile), 135 child: const Text('Save to file'), onPressed: _saveAsFile),
131 RaisedButton( 136 RaisedButton(
132 child: const Text('Print Html'), onPressed: _printHtml), 137 child: const Text('Print Html'), onPressed: _printHtml),
  138 + canDebug
  139 + ? Row(
  140 + mainAxisSize: MainAxisSize.min,
  141 + children: <Widget>[
  142 + const Text('Debug'),
  143 + Switch.adaptive(
  144 + onChanged: (bool value) {
  145 + setState(() {
  146 + pdf.Document.debug = value;
  147 + });
  148 + },
  149 + value: pdf.Document.debug,
  150 + ),
  151 + ],
  152 + )
  153 + : const SizedBox(),
133 ], 154 ],
134 ), 155 ),
135 ), 156 ),
@@ -13,6 +13,7 @@ dependencies: @@ -13,6 +13,7 @@ dependencies:
13 path_provider: 13 path_provider:
14 flutter_full_pdf_viewer: 14 flutter_full_pdf_viewer:
15 cupertino_icons: 15 cupertino_icons:
  16 + qr:
16 17
17 dev_dependencies: 18 dev_dependencies:
18 flutter_test: 19 flutter_test:
1 import 'dart:io'; 1 import 'dart:io';
2 2
3 import 'package:flutter_test/flutter_test.dart'; 3 import 'package:flutter_test/flutter_test.dart';
4 -  
5 import 'package:pdf/pdf.dart'; 4 import 'package:pdf/pdf.dart';
6 -import 'package:pdf/widgets.dart' as pdf;  
7 -  
8 -import 'document.dart'; 5 +import 'package:pdf/widgets.dart';
  6 +import 'package:printing_example/document.dart';
9 7
10 void main() { 8 void main() {
11 testWidgets('Pdf Generate the document', (WidgetTester tester) async { 9 testWidgets('Pdf Generate the document', (WidgetTester tester) async {
12 - final pdf.Document document = await generateDocument(PdfPageFormat.a4); 10 + final Document document = await generateDocument(PdfPageFormat.a4);
  11 +
13 final File file = File('document.pdf'); 12 final File file = File('document.pdf');
14 file.writeAsBytesSync(document.save()); 13 file.writeAsBytesSync(document.save());
15 }); 14 });
@@ -4,7 +4,7 @@ description: Plugin that allows Flutter apps to generate and print documents to @@ -4,7 +4,7 @@ description: Plugin that allows Flutter apps to generate and print documents to
4 homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing 4 homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing
5 repository: https://github.com/DavBfr/dart_pdf 5 repository: https://github.com/DavBfr/dart_pdf
6 issue_tracker: https://github.com/DavBfr/dart_pdf/issues 6 issue_tracker: https://github.com/DavBfr/dart_pdf/issues
7 -version: 2.1.5 7 +version: 2.1.6
8 8
9 environment: 9 environment:
10 sdk: ">=2.1.0 <3.0.0" 10 sdk: ">=2.1.0 <3.0.0"
1 -../example/lib/document.dart