David PHAM-VAN

Add PageTheme

@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 4
5 - Fix Transform.rotateBox 5 - Fix Transform.rotateBox
6 - Add Watermark widget 6 - Add Watermark widget
  7 +- Add PageTheme
7 8
8 ## 1.3.19 9 ## 1.3.19
9 10
@@ -37,6 +37,7 @@ part 'widgets/grid_view.dart'; @@ -37,6 +37,7 @@ part 'widgets/grid_view.dart';
37 part 'widgets/image.dart'; 37 part 'widgets/image.dart';
38 part 'widgets/multi_page.dart'; 38 part 'widgets/multi_page.dart';
39 part 'widgets/page.dart'; 39 part 'widgets/page.dart';
  40 +part 'widgets/page_theme.dart';
40 part 'widgets/placeholders.dart'; 41 part 'widgets/placeholders.dart';
41 part 'widgets/progress.dart'; 42 part 'widgets/progress.dart';
42 part 'widgets/stack.dart'; 43 part 'widgets/stack.dart';
@@ -40,27 +40,31 @@ class _MultiPageInstance { @@ -40,27 +40,31 @@ class _MultiPageInstance {
40 const _MultiPageInstance( 40 const _MultiPageInstance(
41 {@required this.context, 41 {@required this.context,
42 @required this.constraints, 42 @required this.constraints,
  43 + @required this.fullConstraints,
43 @required this.offsetStart}); 44 @required this.offsetStart});
44 45
45 final Context context; 46 final Context context;
46 final BoxConstraints constraints; 47 final BoxConstraints constraints;
  48 + final BoxConstraints fullConstraints;
47 final double offsetStart; 49 final double offsetStart;
48 } 50 }
49 51
50 class MultiPage extends Page { 52 class MultiPage extends Page {
51 MultiPage( 53 MultiPage(
52 - {PdfPageFormat pageFormat = PdfPageFormat.a4, 54 + {PageTheme pageTheme,
  55 + PdfPageFormat pageFormat,
53 BuildListCallback build, 56 BuildListCallback build,
54 this.crossAxisAlignment = CrossAxisAlignment.start, 57 this.crossAxisAlignment = CrossAxisAlignment.start,
55 this.header, 58 this.header,
56 this.footer, 59 this.footer,
57 Theme theme, 60 Theme theme,
58 this.maxPages = 20, 61 this.maxPages = 20,
59 - PageOrientation orientation = PageOrientation.natural, 62 + PageOrientation orientation,
60 EdgeInsets margin}) 63 EdgeInsets margin})
61 : _buildList = build, 64 : _buildList = build,
62 assert(maxPages != null && maxPages > 0), 65 assert(maxPages != null && maxPages > 0),
63 super( 66 super(
  67 + pageTheme: pageTheme,
64 pageFormat: pageFormat, 68 pageFormat: pageFormat,
65 margin: margin, 69 margin: margin,
66 theme: theme, 70 theme: theme,
@@ -88,6 +92,7 @@ class MultiPage extends Page { @@ -88,6 +92,7 @@ class MultiPage extends Page {
88 ..rotateZ(-math.pi / 2) 92 ..rotateZ(-math.pi / 2)
89 ..translate(x - pageHeight + _margin.top - _margin.left, 93 ..translate(x - pageHeight + _margin.top - _margin.left,
90 y + _margin.left - _margin.bottom)); 94 y + _margin.left - _margin.bottom));
  95 +
91 child.paint(context); 96 child.paint(context);
92 context.canvas.restoreContext(); 97 context.canvas.restoreContext();
93 } else { 98 } else {
@@ -112,6 +117,13 @@ class MultiPage extends Page { @@ -112,6 +117,13 @@ class MultiPage extends Page {
112 maxWidth: _mustRotate 117 maxWidth: _mustRotate
113 ? (pageFormat.height - _margin.vertical) 118 ? (pageFormat.height - _margin.vertical)
114 : (pageFormat.width - _margin.horizontal)); 119 : (pageFormat.width - _margin.horizontal));
  120 + final BoxConstraints fullConstraints = mustRotate
  121 + ? BoxConstraints(
  122 + maxWidth: pageFormat.height - _margin.vertical,
  123 + maxHeight: pageFormat.width - _margin.horizontal)
  124 + : BoxConstraints(
  125 + maxWidth: pageFormat.width - _margin.horizontal,
  126 + maxHeight: pageFormat.height - _margin.vertical);
115 final Theme calculatedTheme = theme ?? document.theme ?? Theme.base(); 127 final Theme calculatedTheme = theme ?? document.theme ?? Theme.base();
116 Context context; 128 Context context;
117 double offsetEnd; 129 double offsetEnd;
@@ -149,6 +161,16 @@ class MultiPage extends Page { @@ -149,6 +161,16 @@ class MultiPage extends Page {
149 return true; 161 return true;
150 }()); 162 }());
151 163
  164 + if (pageTheme.buildBackground != null) {
  165 + final Widget child = pageTheme.buildBackground(context);
  166 + if (child != null) {
  167 + child.layout(context, fullConstraints, parentUsesSize: false);
  168 + assert(child.box != null);
  169 + _paintChild(context, child, _margin.left, _margin.bottom,
  170 + pageFormat.height);
  171 + }
  172 + }
  173 +
152 offsetStart = pageHeight - 174 offsetStart = pageHeight -
153 (_mustRotate ? pageHeightMargin - margin.bottom : _margin.top); 175 (_mustRotate ? pageHeightMargin - margin.bottom : _margin.top);
154 offsetEnd = 176 offsetEnd =
@@ -157,6 +179,7 @@ class MultiPage extends Page { @@ -157,6 +179,7 @@ class MultiPage extends Page {
157 _pages.add(_MultiPageInstance( 179 _pages.add(_MultiPageInstance(
158 context: context, 180 context: context,
159 constraints: constraints, 181 constraints: constraints,
  182 + fullConstraints: fullConstraints,
160 offsetStart: offsetStart, 183 offsetStart: offsetStart,
161 )); 184 ));
162 185
@@ -262,6 +285,17 @@ class MultiPage extends Page { @@ -262,6 +285,17 @@ class MultiPage extends Page {
262 pageFormat.height); 285 pageFormat.height);
263 } 286 }
264 } 287 }
  288 +
  289 + if (pageTheme.buildForeground != null) {
  290 + final Widget child = pageTheme.buildForeground(page.context);
  291 + if (child != null) {
  292 + child.layout(page.context, page.fullConstraints,
  293 + parentUsesSize: false);
  294 + assert(child.box != null);
  295 + _paintChild(page.context, child, _margin.left, _margin.bottom,
  296 + pageFormat.height);
  297 + }
  298 + }
265 } 299 }
266 } 300 }
267 } 301 }
@@ -23,51 +23,43 @@ enum PageOrientation { natural, landscape, portrait } @@ -23,51 +23,43 @@ enum PageOrientation { natural, landscape, portrait }
23 23
24 class Page { 24 class Page {
25 Page( 25 Page(
26 - {this.pageFormat = PdfPageFormat.standard, 26 + {PageTheme pageTheme,
  27 + PdfPageFormat pageFormat,
27 BuildCallback build, 28 BuildCallback build,
28 - this.theme,  
29 - this.orientation = PageOrientation.natural, 29 + Theme theme,
  30 + PageOrientation orientation,
30 EdgeInsets margin}) 31 EdgeInsets margin})
31 - : assert(pageFormat != null),  
32 - _margin = margin, 32 + : assert(
  33 + pageTheme == null ||
  34 + (pageFormat == null &&
  35 + theme == null &&
  36 + orientation == null &&
  37 + margin == null),
  38 + 'Don\'t set both pageTheme and other settings'),
  39 + pageTheme = pageTheme ??
  40 + PageTheme(
  41 + pageFormat: pageFormat,
  42 + orientation: orientation,
  43 + margin: margin,
  44 + theme: theme,
  45 + ),
33 _build = build; 46 _build = build;
34 47
35 - final PdfPageFormat pageFormat; 48 + final PageTheme pageTheme;
36 49
37 - final PageOrientation orientation; 50 + PdfPageFormat get pageFormat => pageTheme.pageFormat;
38 51
39 - final EdgeInsets _margin; 52 + PageOrientation get orientation => pageTheme.orientation;
40 53
41 final BuildCallback _build; 54 final BuildCallback _build;
42 55
43 - final Theme theme; 56 + Theme get theme => pageTheme.theme;
44 57
45 - bool get mustRotate =>  
46 - (orientation == PageOrientation.landscape &&  
47 - pageFormat.height > pageFormat.width) ||  
48 - (orientation == PageOrientation.portrait &&  
49 - pageFormat.width > pageFormat.height); 58 + bool get mustRotate => pageTheme.mustRotate;
50 59
51 PdfPage _pdfPage; 60 PdfPage _pdfPage;
52 61
53 - EdgeInsets get margin {  
54 - if (_margin != null) {  
55 - if (mustRotate) {  
56 - return EdgeInsets.fromLTRB(  
57 - _margin.bottom, _margin.left, _margin.top, _margin.right);  
58 - } else {  
59 - return _margin;  
60 - }  
61 - }  
62 -  
63 - if (mustRotate) {  
64 - return EdgeInsets.fromLTRB(pageFormat.marginBottom, pageFormat.marginLeft,  
65 - pageFormat.marginTop, pageFormat.marginRight);  
66 - } else {  
67 - return EdgeInsets.fromLTRB(pageFormat.marginLeft, pageFormat.marginTop,  
68 - pageFormat.marginRight, pageFormat.marginBottom);  
69 - }  
70 - } 62 + EdgeInsets get margin => pageTheme.margin;
71 63
72 @protected 64 @protected
73 void debugPaint(Context context) { 65 void debugPaint(Context context) {
@@ -109,12 +101,28 @@ class Page { @@ -109,12 +101,28 @@ class Page {
109 page: _pdfPage, 101 page: _pdfPage,
110 canvas: canvas, 102 canvas: canvas,
111 ).inheritFrom(calculatedTheme); 103 ).inheritFrom(calculatedTheme);
  104 + if (pageTheme.buildBackground != null) {
  105 + final Widget child = pageTheme.buildBackground(context);
  106 + if (child != null) {
  107 + layout(child, context, constraints);
  108 + paint(child, context);
  109 + }
  110 + }
112 if (_build != null) { 111 if (_build != null) {
113 final Widget child = _build(context); 112 final Widget child = _build(context);
  113 + if (child != null) {
  114 + layout(child, context, constraints);
  115 + paint(child, context);
  116 + }
  117 + }
  118 + if (pageTheme.buildForeground != null) {
  119 + final Widget child = pageTheme.buildForeground(context);
  120 + if (child != null) {
114 layout(child, context, constraints); 121 layout(child, context, constraints);
115 paint(child, context); 122 paint(child, context);
116 } 123 }
117 } 124 }
  125 + }
118 126
119 @protected 127 @protected
120 void layout(Widget child, Context context, BoxConstraints constraints, 128 void layout(Widget child, Context context, BoxConstraints constraints,
  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 +part of widget;
  18 +
  19 +@immutable
  20 +class PageTheme {
  21 + const PageTheme(
  22 + {PdfPageFormat pageFormat,
  23 + this.buildBackground,
  24 + this.buildForeground,
  25 + this.theme,
  26 + PageOrientation orientation,
  27 + EdgeInsets margin})
  28 + : pageFormat = pageFormat ?? PdfPageFormat.standard,
  29 + orientation = orientation ?? PageOrientation.natural,
  30 + _margin = margin;
  31 +
  32 + final PdfPageFormat pageFormat;
  33 +
  34 + final PageOrientation orientation;
  35 +
  36 + final EdgeInsets _margin;
  37 +
  38 + final BuildCallback buildBackground;
  39 +
  40 + final BuildCallback buildForeground;
  41 +
  42 + final Theme theme;
  43 +
  44 + bool get mustRotate =>
  45 + (orientation == PageOrientation.landscape &&
  46 + pageFormat.height > pageFormat.width) ||
  47 + (orientation == PageOrientation.portrait &&
  48 + pageFormat.width > pageFormat.height);
  49 +
  50 + EdgeInsets get margin {
  51 + if (_margin != null) {
  52 + if (mustRotate) {
  53 + return EdgeInsets.fromLTRB(
  54 + _margin.bottom, _margin.left, _margin.top, _margin.right);
  55 + } else {
  56 + return _margin;
  57 + }
  58 + }
  59 +
  60 + if (mustRotate) {
  61 + return EdgeInsets.fromLTRB(pageFormat.marginBottom, pageFormat.marginLeft,
  62 + pageFormat.marginTop, pageFormat.marginRight);
  63 + } else {
  64 + return EdgeInsets.fromLTRB(pageFormat.marginLeft, pageFormat.marginTop,
  65 + pageFormat.marginRight, pageFormat.marginBottom);
  66 + }
  67 + }
  68 +}
@@ -34,6 +34,7 @@ import 'widget_table_test.dart' as widget_table; @@ -34,6 +34,7 @@ import 'widget_table_test.dart' as widget_table;
34 import 'widget_test.dart' as widget; 34 import 'widget_test.dart' as widget;
35 import 'widget_text_test.dart' as widget_text; 35 import 'widget_text_test.dart' as widget_text;
36 import 'widget_theme_test.dart' as widget_theme; 36 import 'widget_theme_test.dart' as widget_theme;
  37 +import 'widget_watermark_test.dart' as widget_watermark;
37 import 'widget_wrap_test.dart' as widget_wrap; 38 import 'widget_wrap_test.dart' as widget_wrap;
38 39
39 void main() { 40 void main() {
@@ -54,6 +55,7 @@ void main() { @@ -54,6 +55,7 @@ void main() {
54 widget_table.main(); 55 widget_table.main();
55 widget_text.main(); 56 widget_text.main();
56 widget_theme.main(); 57 widget_theme.main();
  58 + widget_watermark.main();
57 widget_wrap.main(); 59 widget_wrap.main();
58 widget.main(); 60 widget.main();
59 } 61 }
  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 'dart:io';
  18 +
  19 +import 'package:pdf/widgets.dart';
  20 +import 'package:test/test.dart';
  21 +
  22 +void main() {
  23 + test('Pdf Widgets Watermark', () {
  24 + Document.debug = true;
  25 + final Document pdf = Document();
  26 +
  27 + final PageTheme pageTheme = PageTheme(
  28 + buildBackground: (Context context) => Watermark.text('DRAFT'),
  29 + buildForeground: (Context context) => Align(
  30 + alignment: Alignment.bottomLeft,
  31 + child: SizedBox(
  32 + width: 100,
  33 + height: 100,
  34 + child: PdfLogo(),
  35 + ),
  36 + ),
  37 + );
  38 +
  39 + pdf.addPage(
  40 + Page(
  41 + pageTheme: pageTheme,
  42 + build: (Context context) => Center(
  43 + child: Text(
  44 + 'Hello World',
  45 + ),
  46 + ),
  47 + ),
  48 + );
  49 +
  50 + pdf.addPage(
  51 + MultiPage(
  52 + pageTheme: pageTheme,
  53 + build: (Context context) => List<Widget>.filled(
  54 + 100,
  55 + Text(
  56 + 'Hello World',
  57 + ),
  58 + ),
  59 + ),
  60 + );
  61 +
  62 + final File file = File('widgets-watermark.pdf');
  63 + file.writeAsBytesSync(pdf.save());
  64 + });
  65 +}