David PHAM-VAN

Reorder MultiPage paint operations

@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 4
5 - Fix Align debug painting 5 - Fix Align debug painting
6 - Fix GridView when empty 6 - Fix GridView when empty
  7 +- Reorder MultiPage paint operations
7 8
8 ## 1.4.1 9 ## 1.4.1
9 10
@@ -94,7 +95,7 @@ @@ -94,7 +95,7 @@
94 95
95 ## 1.3.16 96 ## 1.3.16
96 97
97 -- Add better debugPaint on Align Widget 98 +- Add better debug painting on Align Widget
98 - Fix Transform placement when Alignment and Origin are Null 99 - Fix Transform placement when Alignment and Origin are Null
99 - Add Transform.rotateBox constructor 100 - Add Transform.rotateBox constructor
100 - Add Wrap Widget 101 - Add Wrap Widget
@@ -126,7 +127,7 @@ @@ -126,7 +127,7 @@
126 ## 1.3.10 127 ## 1.3.10
127 128
128 - Deprecate the document argument in Printing.sharePdf() 129 - Deprecate the document argument in Printing.sharePdf()
129 -- Add default value to alpha in PdfColor variants 130 +- Add a default value to alpha in PdfColor variants
130 - Fix Table Widget 131 - Fix Table Widget
131 - Add Flexible and Spacer Widgets 132 - Add Flexible and Spacer Widgets
132 133
@@ -186,7 +187,7 @@ @@ -186,7 +187,7 @@
186 - Fix dart lint warnings 187 - Fix dart lint warnings
187 - Improve font bounds calculation 188 - Improve font bounds calculation
188 - Add RichText Widget 189 - Add RichText Widget
189 -- Fix MultiPage max height 190 +- Fix MultiPage max-height
190 - Add Stack Widget 191 - Add Stack Widget
191 - Update Readme 192 - Update Readme
192 193
@@ -196,13 +197,13 @@ @@ -196,13 +197,13 @@
196 197
197 ## 1.3.0 198 ## 1.3.0
198 199
199 -- Add a Flutter like widget system 200 +- Add a Flutter-like Widget system
200 201
201 ## 1.2.0 202 ## 1.2.0
202 203
203 - Change license to Apache 2.0 204 - Change license to Apache 2.0
204 - Improve PdfRect 205 - Improve PdfRect
205 -- Add support for CMYK, HSL anf HSV colors 206 +- Add support for CMYK, HSL and HSV colors
206 - Implement rounded rect 207 - Implement rounded rect
207 208
208 ## 1.1.1 209 ## 1.1.1
@@ -225,7 +226,7 @@ @@ -225,7 +226,7 @@
225 - Implement drawShape 226 - Implement drawShape
226 - Add support for Jpeg images 227 - Add support for Jpeg images
227 - Fix numeric conversions in graphic operations 228 - Fix numeric conversions in graphic operations
228 -- Add unicode support for annotations and info block 229 +- Add Unicode support for annotations and info block
229 - Add Flutter example 230 - Add Flutter example
230 231
231 ## 1.0.8 232 ## 1.0.8
@@ -258,7 +259,7 @@ @@ -258,7 +259,7 @@
258 259
259 ## 1.0.2 260 ## 1.0.2
260 261
261 -- Update sdk support for 2.0.0 262 +- Update SDK support for 2.0.0
262 263
263 ## 1.0.1 264 ## 1.0.1
264 265
@@ -59,14 +59,19 @@ class Document { @@ -59,14 +59,19 @@ class Document {
59 59
60 final List<Page> _pages = <Page>[]; 60 final List<Page> _pages = <Page>[];
61 61
  62 + bool _paint = false;
  63 +
62 void addPage(Page page) { 64 void addPage(Page page) {
63 page.generate(this); 65 page.generate(this);
64 _pages.add(page); 66 _pages.add(page);
65 } 67 }
66 68
67 List<int> save() { 69 List<int> save() {
68 - for (Page page in _pages) {  
69 - page.postProcess(this); 70 + if (!_paint) {
  71 + for (Page page in _pages) {
  72 + page.postProcess(this);
  73 + }
  74 + _paint = true;
70 } 75 }
71 return document.save(); 76 return document.save();
72 } 77 }
@@ -26,8 +26,27 @@ class _GridViewContext extends WidgetContext { @@ -26,8 +26,27 @@ class _GridViewContext extends WidgetContext {
26 double childMainAxis; 26 double childMainAxis;
27 27
28 @override 28 @override
  29 + void apply(WidgetContext other) {
  30 + if (other is _GridViewContext) {
  31 + firstChild = other.firstChild;
  32 + lastChild = other.lastChild;
  33 + childCrossAxis = other.childCrossAxis;
  34 + childMainAxis = other.childMainAxis;
  35 + }
  36 + }
  37 +
  38 + @override
  39 + WidgetContext clone() {
  40 + return _GridViewContext()
  41 + ..firstChild = firstChild
  42 + ..lastChild = lastChild
  43 + ..childCrossAxis = childCrossAxis
  44 + ..childMainAxis = childMainAxis;
  45 + }
  46 +
  47 + @override
29 String toString() => 48 String toString() =>
30 - 'GridViewContext first:$firstChild last:$lastChild size:${childCrossAxis}x$childMainAxis'; 49 + '$runtimeType first:$firstChild last:$lastChild size:${childCrossAxis}x$childMainAxis';
31 } 50 }
32 51
33 class GridView extends MultiChildWidget implements SpanningWidget { 52 class GridView extends MultiChildWidget implements SpanningWidget {
@@ -18,14 +18,20 @@ @@ -18,14 +18,20 @@
18 18
19 part of widget; 19 part of widget;
20 20
21 -class WidgetContext {} 21 +abstract class WidgetContext {
  22 + WidgetContext clone();
  23 +
  24 + void apply(WidgetContext other);
  25 +}
22 26
23 abstract class SpanningWidget extends Widget { 27 abstract class SpanningWidget extends Widget {
24 bool get canSpan => false; 28 bool get canSpan => false;
25 29
  30 + /// Get unmodified mutable context object
26 @protected 31 @protected
27 WidgetContext saveContext(); 32 WidgetContext saveContext();
28 33
  34 + /// Aplpy the context for next layout
29 @protected 35 @protected
30 void restoreContext(WidgetContext context); 36 void restoreContext(WidgetContext context);
31 } 37 }
@@ -38,17 +44,37 @@ class NewPage extends Widget { @@ -38,17 +44,37 @@ class NewPage extends Widget {
38 } 44 }
39 } 45 }
40 46
  47 +@immutable
  48 +class _MultiPageWidget {
  49 + const _MultiPageWidget({
  50 + @required this.child,
  51 + @required this.x,
  52 + @required this.y,
  53 + @required this.constraints,
  54 + @required this.widgetContext,
  55 + });
  56 +
  57 + final Widget child;
  58 + final double x;
  59 + final double y;
  60 + final BoxConstraints constraints;
  61 + final WidgetContext widgetContext;
  62 +}
  63 +
  64 +@immutable
41 class _MultiPageInstance { 65 class _MultiPageInstance {
42 - const _MultiPageInstance(  
43 - {@required this.context,  
44 - @required this.constraints,  
45 - @required this.fullConstraints,  
46 - @required this.offsetStart}); 66 + _MultiPageInstance({
  67 + @required this.context,
  68 + @required this.constraints,
  69 + @required this.fullConstraints,
  70 + @required this.offsetStart,
  71 + });
47 72
48 final Context context; 73 final Context context;
49 final BoxConstraints constraints; 74 final BoxConstraints constraints;
50 final BoxConstraints fullConstraints; 75 final BoxConstraints fullConstraints;
51 final double offsetStart; 76 final double offsetStart;
  77 + final List<_MultiPageWidget> widgets = <_MultiPageWidget>[];
52 } 78 }
53 79
54 class MultiPage extends Page { 80 class MultiPage extends Page {
@@ -168,16 +194,6 @@ class MultiPage extends Page { @@ -168,16 +194,6 @@ class MultiPage extends Page {
168 return true; 194 return true;
169 }()); 195 }());
170 196
171 - if (pageTheme.buildBackground != null) {  
172 - final Widget child = pageTheme.buildBackground(context);  
173 - if (child != null) {  
174 - child.layout(context, fullConstraints, parentUsesSize: false);  
175 - assert(child.box != null);  
176 - _paintChild(context, child, _margin.left, _margin.bottom,  
177 - pageFormat.height);  
178 - }  
179 - }  
180 -  
181 offsetStart = pageHeight - 197 offsetStart = pageHeight -
182 (_mustRotate ? pageHeightMargin - margin.bottom : _margin.top); 198 (_mustRotate ? pageHeightMargin - margin.bottom : _margin.top);
183 offsetEnd = 199 offsetEnd =
@@ -238,17 +254,23 @@ class MultiPage extends Page { @@ -238,17 +254,23 @@ class MultiPage extends Page {
238 254
239 final SpanningWidget span = child; 255 final SpanningWidget span = child;
240 256
241 - child.layout(  
242 - context, constraints.copyWith(maxHeight: offsetStart - offsetEnd),  
243 - parentUsesSize: false); 257 + final BoxConstraints localConstraints =
  258 + constraints.copyWith(maxHeight: offsetStart - offsetEnd);
  259 + child.layout(context, localConstraints, parentUsesSize: false);
244 assert(child.box != null); 260 assert(child.box != null);
245 - _paintChild(context, child, _margin.left,  
246 - offsetStart - child.box.height, pageFormat.height); 261 + widgetContext = span.saveContext();
  262 + _pages.last.widgets.add(
  263 + _MultiPageWidget(
  264 + child: child,
  265 + x: _margin.left,
  266 + y: offsetStart - child.box.height,
  267 + constraints: localConstraints,
  268 + widgetContext: widgetContext?.clone(),
  269 + ),
  270 + );
247 271
248 // Has it finished spanning? 272 // Has it finished spanning?
249 - if (span.canSpan) {  
250 - widgetContext = span.saveContext();  
251 - } else { 273 + if (!span.canSpan) {
252 sameCount = 0; 274 sameCount = 0;
253 index++; 275 index++;
254 } 276 }
@@ -258,8 +280,17 @@ class MultiPage extends Page { @@ -258,8 +280,17 @@ class MultiPage extends Page {
258 continue; 280 continue;
259 } 281 }
260 282
261 - _paintChild(context, child, _margin.left, offsetStart - child.box.height,  
262 - pageFormat.height); 283 + _pages.last.widgets.add(
  284 + _MultiPageWidget(
  285 + child: child,
  286 + x: _margin.left,
  287 + y: offsetStart - child.box.height,
  288 + constraints: constraints,
  289 + widgetContext:
  290 + child is SpanningWidget ? child.saveContext().clone() : null,
  291 + ),
  292 + );
  293 +
263 offsetStart -= child.box.height; 294 offsetStart -= child.box.height;
264 sameCount = 0; 295 sameCount = 0;
265 index++; 296 index++;
@@ -271,6 +302,29 @@ class MultiPage extends Page { @@ -271,6 +302,29 @@ class MultiPage extends Page {
271 final EdgeInsets _margin = margin; 302 final EdgeInsets _margin = margin;
272 303
273 for (_MultiPageInstance page in _pages) { 304 for (_MultiPageInstance page in _pages) {
  305 + if (pageTheme.buildBackground != null) {
  306 + final Widget child = pageTheme.buildBackground(page.context);
  307 + if (child != null) {
  308 + child.layout(page.context, page.fullConstraints,
  309 + parentUsesSize: false);
  310 + assert(child.box != null);
  311 + _paintChild(page.context, child, _margin.left, _margin.bottom,
  312 + pageFormat.height);
  313 + }
  314 + }
  315 +
  316 + for (_MultiPageWidget widget in page.widgets) {
  317 + final Widget child = widget.child;
  318 + if (child is SpanningWidget) {
  319 + final WidgetContext context = child.saveContext();
  320 + context.apply(widget.widgetContext);
  321 + }
  322 + child.layout(page.context, widget.constraints, parentUsesSize: false);
  323 + assert(child.box != null);
  324 + _paintChild(
  325 + page.context, widget.child, widget.x, widget.y, pageFormat.height);
  326 + }
  327 +
274 if (header != null) { 328 if (header != null) {
275 final Widget headerWidget = header(page.context); 329 final Widget headerWidget = header(page.context);
276 if (headerWidget != null) { 330 if (headerWidget != null) {
@@ -84,6 +84,24 @@ class TableBorder extends BoxBorder { @@ -84,6 +84,24 @@ class TableBorder extends BoxBorder {
84 class _TableContext extends WidgetContext { 84 class _TableContext extends WidgetContext {
85 int firstLine = 0; 85 int firstLine = 0;
86 int lastLine = 0; 86 int lastLine = 0;
  87 +
  88 + @override
  89 + void apply(WidgetContext other) {
  90 + if (other is _TableContext) {
  91 + firstLine = other.firstLine;
  92 + lastLine = other.lastLine;
  93 + }
  94 + }
  95 +
  96 + @override
  97 + WidgetContext clone() {
  98 + return _TableContext()
  99 + ..firstLine = firstLine
  100 + ..lastLine = lastLine;
  101 + }
  102 +
  103 + @override
  104 + String toString() => '$runtimeType firstLine: $firstLine lastLine: $lastLine';
87 } 105 }
88 106
89 class _ColumnLayout { 107 class _ColumnLayout {
@@ -215,7 +233,7 @@ class Table extends Widget implements SpanningWidget { @@ -215,7 +233,7 @@ class Table extends Widget implements SpanningWidget {
215 final List<double> _widths = <double>[]; 233 final List<double> _widths = <double>[];
216 final List<double> _heights = <double>[]; 234 final List<double> _heights = <double>[];
217 235
218 - _TableContext _context = _TableContext(); 236 + final _TableContext _context = _TableContext();
219 237
220 final TableColumnWidth defaultColumnWidth; 238 final TableColumnWidth defaultColumnWidth;
221 final Map<int, TableColumnWidth> columnWidths; 239 final Map<int, TableColumnWidth> columnWidths;
@@ -227,7 +245,7 @@ class Table extends Widget implements SpanningWidget { @@ -227,7 +245,7 @@ class Table extends Widget implements SpanningWidget {
227 245
228 @override 246 @override
229 void restoreContext(WidgetContext context) { 247 void restoreContext(WidgetContext context) {
230 - _context = context; 248 + _context.apply(context);
231 _context.firstLine = _context.lastLine; 249 _context.firstLine = _context.lastLine;
232 } 250 }
233 251
@@ -44,7 +44,22 @@ class _WrapContext extends WidgetContext { @@ -44,7 +44,22 @@ class _WrapContext extends WidgetContext {
44 int lastChild = 0; 44 int lastChild = 0;
45 45
46 @override 46 @override
47 - String toString() => 'WrapContext first:$firstChild last:$lastChild'; 47 + void apply(WidgetContext other) {
  48 + if (other is _WrapContext) {
  49 + firstChild = other.firstChild;
  50 + lastChild = other.lastChild;
  51 + }
  52 + }
  53 +
  54 + @override
  55 + WidgetContext clone() {
  56 + return _WrapContext()
  57 + ..firstChild = firstChild
  58 + ..lastChild = lastChild;
  59 + }
  60 +
  61 + @override
  62 + String toString() => '$runtimeType first:$firstChild last:$lastChild';
48 } 63 }
49 64
50 /// A widget that displays its children in multiple horizontal or vertical runs. 65 /// A widget that displays its children in multiple horizontal or vertical runs.
@@ -18,15 +18,19 @@ @@ -18,15 +18,19 @@
18 18
19 import 'dart:io'; 19 import 'dart:io';
20 20
  21 +import 'package:pdf/pdf.dart';
21 import 'package:pdf/widgets.dart'; 22 import 'package:pdf/widgets.dart';
22 import 'package:test/test.dart'; 23 import 'package:test/test.dart';
23 24
  25 +Document pdf;
  26 +PageTheme pageTheme;
  27 +
24 void main() { 28 void main() {
25 - test('Pdf Widgets Watermark', () { 29 + setUpAll(() {
26 Document.debug = true; 30 Document.debug = true;
27 - final Document pdf = Document(); 31 + pdf = Document();
28 32
29 - final PageTheme pageTheme = PageTheme( 33 + pageTheme = PageTheme(
30 buildBackground: (Context context) => FullPage( 34 buildBackground: (Context context) => FullPage(
31 ignoreMargins: true, 35 ignoreMargins: true,
32 child: Watermark.text('DRAFT'), 36 child: Watermark.text('DRAFT'),
@@ -40,7 +44,9 @@ void main() { @@ -40,7 +44,9 @@ void main() {
40 ), 44 ),
41 ), 45 ),
42 ); 46 );
  47 + });
43 48
  49 + test('Pdf Widgets Watermark Page', () {
44 pdf.addPage( 50 pdf.addPage(
45 Page( 51 Page(
46 pageTheme: pageTheme, 52 pageTheme: pageTheme,
@@ -51,7 +57,9 @@ void main() { @@ -51,7 +57,9 @@ void main() {
51 ), 57 ),
52 ), 58 ),
53 ); 59 );
  60 + });
54 61
  62 + test('Pdf Widgets Watermark MultiPage', () {
55 pdf.addPage( 63 pdf.addPage(
56 MultiPage( 64 MultiPage(
57 pageTheme: pageTheme, 65 pageTheme: pageTheme,
@@ -63,7 +71,38 @@ void main() { @@ -63,7 +71,38 @@ void main() {
63 ), 71 ),
64 ), 72 ),
65 ); 73 );
  74 + });
  75 +
  76 + test('Pdf Widgets Watermark Page Count', () async {
  77 + final PageTheme pageTheme = PageTheme(
  78 + buildBackground: (Context context) =>
  79 + (context.pageNumber == context.pagesCount)
  80 + ? Align(
  81 + alignment: Alignment.topRight,
  82 + child: SizedBox(
  83 + width: 200,
  84 + height: 200,
  85 + child: PdfLogo(color: PdfColors.blue200)),
  86 + )
  87 + : Container(),
  88 + );
  89 +
  90 + pdf.addPage(
  91 + MultiPage(
  92 + pageTheme: pageTheme,
  93 + build: (Context context) => <Widget>[
  94 + Wrap(
  95 + children: List<Widget>.generate(
  96 + 670,
  97 + (_) => Text('Hello World '),
  98 + ),
  99 + ),
  100 + ],
  101 + ),
  102 + );
  103 + });
66 104
  105 + tearDownAll(() {
67 final File file = File('widgets-watermark.pdf'); 106 final File file = File('widgets-watermark.pdf');
68 file.writeAsBytesSync(pdf.save()); 107 file.writeAsBytesSync(pdf.save());
69 }); 108 });
No preview for this file type
  1 +widgets-monopage.pdf
No preview for this file type
  1 +widgets-multipage.pdf