Milad akarie
Committed by David PHAM-VAN

Add RTL support to EdgeInsets widget

Add RTL support Page and MultiPage
@@ -22,8 +22,7 @@ import 'point.dart'; @@ -22,8 +22,7 @@ import 'point.dart';
22 class PdfRect { 22 class PdfRect {
23 const PdfRect(this.x, this.y, this.width, this.height); 23 const PdfRect(this.x, this.y, this.width, this.height);
24 24
25 - factory PdfRect.fromLTRB(  
26 - double left, double top, double right, double bottom) { 25 + factory PdfRect.fromLTRB(double left, double top, double right, double bottom) {
27 return PdfRect(left, top, right - left, bottom - top); 26 return PdfRect(left, top, right - left, bottom - top);
28 } 27 }
29 28
@@ -36,14 +35,18 @@ class PdfRect { @@ -36,14 +35,18 @@ class PdfRect {
36 static const PdfRect zero = PdfRect(0, 0, 0, 0); 35 static const PdfRect zero = PdfRect(0, 0, 0, 0);
37 36
38 double get left => x; 37 double get left => x;
  38 +
39 double get bottom => y; 39 double get bottom => y;
  40 +
40 double get right => x + width; 41 double get right => x + width;
  42 +
41 double get top => y + height; 43 double get top => y + height;
42 44
43 @Deprecated('type => horizontalCenter') 45 @Deprecated('type => horizontalCenter')
44 double get horizondalCenter => horizontalCenter; 46 double get horizondalCenter => horizontalCenter;
45 47
46 double get horizontalCenter => x + width / 2; 48 double get horizontalCenter => x + width / 2;
  49 +
47 double get verticalCenter => y + height / 2; 50 double get verticalCenter => y + height / 2;
48 51
49 @override 52 @override
@@ -54,19 +57,36 @@ class PdfRect { @@ -54,19 +57,36 @@ class PdfRect {
54 } 57 }
55 58
56 PdfPoint get offset => PdfPoint(x, y); 59 PdfPoint get offset => PdfPoint(x, y);
  60 +
57 PdfPoint get size => PdfPoint(width, height); 61 PdfPoint get size => PdfPoint(width, height);
58 62
59 PdfPoint get topLeft => PdfPoint(x, y); 63 PdfPoint get topLeft => PdfPoint(x, y);
  64 +
60 PdfPoint get topRight => PdfPoint(right, y); 65 PdfPoint get topRight => PdfPoint(right, y);
  66 +
61 PdfPoint get bottomLeft => PdfPoint(x, top); 67 PdfPoint get bottomLeft => PdfPoint(x, top);
  68 +
62 PdfPoint get bottomRight => PdfPoint(right, top); 69 PdfPoint get bottomRight => PdfPoint(right, top);
63 70
64 /// Returns a new rectangle with edges moved outwards by the given delta. 71 /// Returns a new rectangle with edges moved outwards by the given delta.
65 PdfRect inflate(double delta) { 72 PdfRect inflate(double delta) {
66 - return PdfRect.fromLTRB(  
67 - left - delta, top - delta, right + delta, bottom + delta); 73 + return PdfRect.fromLTRB(left - delta, top - delta, right + delta, bottom + delta);
68 } 74 }
69 75
70 /// Returns a new rectangle with edges moved inwards by the given delta. 76 /// Returns a new rectangle with edges moved inwards by the given delta.
71 PdfRect deflate(double delta) => inflate(-delta); 77 PdfRect deflate(double delta) => inflate(-delta);
  78 +
  79 + PdfRect copyWith({
  80 + double? x,
  81 + double? y,
  82 + double? width,
  83 + double? height,
  84 + }) {
  85 + return PdfRect(
  86 + x ?? this.x,
  87 + y ?? this.y,
  88 + width ?? this.width,
  89 + height ?? this.height,
  90 + );
  91 + }
72 } 92 }
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 16
17 import 'dart:math' as math; 17 import 'dart:math' as math;
18 18
  19 +import 'package:pdf/widgets.dart';
19 import 'package:vector_math/vector_math_64.dart'; 20 import 'package:vector_math/vector_math_64.dart';
20 21
21 import '../../pdf.dart'; 22 import '../../pdf.dart';
@@ -80,46 +81,48 @@ class Padding extends SingleChildWidget { @@ -80,46 +81,48 @@ class Padding extends SingleChildWidget {
80 Widget? child, 81 Widget? child,
81 }) : super(child: child); 82 }) : super(child: child);
82 83
83 - final EdgeInsets padding; 84 + final EdgeInsetsGeometry padding;
84 85
85 @override 86 @override
86 void layout(Context context, BoxConstraints constraints, 87 void layout(Context context, BoxConstraints constraints,
87 {bool parentUsesSize = false}) { 88 {bool parentUsesSize = false}) {
  89 + final effectivePadding = padding.resolve(Directionality.of(context));
88 if (child != null) { 90 if (child != null) {
89 - final childConstraints = constraints.deflate(padding); 91 + final childConstraints = constraints.deflate(effectivePadding);
90 child!.layout(context, childConstraints, parentUsesSize: parentUsesSize); 92 child!.layout(context, childConstraints, parentUsesSize: parentUsesSize);
91 assert(child!.box != null); 93 assert(child!.box != null);
92 box = constraints.constrainRect( 94 box = constraints.constrainRect(
93 - width: child!.box!.width + padding.horizontal,  
94 - height: child!.box!.height + padding.vertical); 95 + width: child!.box!.width + effectivePadding.horizontal,
  96 + height: child!.box!.height + effectivePadding.vertical);
95 } else { 97 } else {
96 box = constraints.constrainRect( 98 box = constraints.constrainRect(
97 - width: padding.horizontal, height: padding.vertical); 99 + width: effectivePadding.horizontal, height: effectivePadding.vertical);
98 } 100 }
99 } 101 }
100 102
101 @override 103 @override
102 void debugPaint(Context context) { 104 void debugPaint(Context context) {
  105 + final effectivePadding = padding.resolve(Directionality.of(context));
103 context.canvas 106 context.canvas
104 ..setFillColor(PdfColors.lime) 107 ..setFillColor(PdfColors.lime)
105 ..moveTo(box!.x, box!.y) 108 ..moveTo(box!.x, box!.y)
106 ..lineTo(box!.right, box!.y) 109 ..lineTo(box!.right, box!.y)
107 ..lineTo(box!.right, box!.top) 110 ..lineTo(box!.right, box!.top)
108 ..lineTo(box!.x, box!.top) 111 ..lineTo(box!.x, box!.top)
109 - ..moveTo(box!.x + padding.left, box!.y + padding.bottom)  
110 - ..lineTo(box!.x + padding.left, box!.top - padding.top)  
111 - ..lineTo(box!.right - padding.right, box!.top - padding.top)  
112 - ..lineTo(box!.right - padding.right, box!.y + padding.bottom) 112 + ..moveTo(box!.x + effectivePadding.left, box!.y + effectivePadding.bottom)
  113 + ..lineTo(box!.x + effectivePadding.left, box!.top - effectivePadding.top)
  114 + ..lineTo(box!.right - effectivePadding.right, box!.top - effectivePadding.top)
  115 + ..lineTo(box!.right - effectivePadding.right, box!.y + effectivePadding.bottom)
113 ..fillPath(); 116 ..fillPath();
114 } 117 }
115 118
116 @override 119 @override
117 void paint(Context context) { 120 void paint(Context context) {
118 super.paint(context); 121 super.paint(context);
119 - 122 + final effectivePadding = padding.resolve(Directionality.of(context));
120 if (child != null) { 123 if (child != null) {
121 final mat = Matrix4.identity(); 124 final mat = Matrix4.identity();
122 - mat.translate(box!.x + padding.left, box!.y + padding.bottom); 125 + mat.translate(box!.x + effectivePadding.left, box!.y + effectivePadding.bottom);
123 context.canvas 126 context.canvas
124 ..saveContext() 127 ..saveContext()
125 ..setTransform(mat); 128 ..setTransform(mat);
@@ -20,16 +20,13 @@ import 'package:meta/meta.dart'; @@ -20,16 +20,13 @@ import 'package:meta/meta.dart';
20 import 'package:vector_math/vector_math_64.dart'; 20 import 'package:vector_math/vector_math_64.dart';
21 21
22 import '../../pdf.dart'; 22 import '../../pdf.dart';
23 -import 'basic.dart'; 23 +import '../../widgets.dart';
24 24
25 @immutable 25 @immutable
26 class BoxConstraints { 26 class BoxConstraints {
27 /// Creates box constraints with the given constraints. 27 /// Creates box constraints with the given constraints.
28 const BoxConstraints( 28 const BoxConstraints(
29 - {this.minWidth = 0.0,  
30 - this.maxWidth = double.infinity,  
31 - this.minHeight = 0.0,  
32 - this.maxHeight = double.infinity}); 29 + {this.minWidth = 0.0, this.maxWidth = double.infinity, this.minHeight = 0.0, this.maxHeight = double.infinity});
33 30
34 /// Creates box constraints that require the given width or height. 31 /// Creates box constraints that require the given width or height.
35 const BoxConstraints.tightFor({double? width, double? height}) 32 const BoxConstraints.tightFor({double? width, double? height})
@@ -104,8 +101,7 @@ class BoxConstraints { @@ -104,8 +101,7 @@ class BoxConstraints {
104 return result; 101 return result;
105 } 102 }
106 103
107 - PdfRect constrainRect(  
108 - {double width = double.infinity, double height = double.infinity}) { 104 + PdfRect constrainRect({double width = double.infinity, double height = double.infinity}) {
109 final result = PdfPoint(constrainWidth(width), constrainHeight(height)); 105 final result = PdfPoint(constrainWidth(width), constrainHeight(height));
110 return PdfRect.fromPoints(PdfPoint.zero, result); 106 return PdfRect.fromPoints(PdfPoint.zero, result);
111 } 107 }
@@ -162,10 +158,8 @@ class BoxConstraints { @@ -162,10 +158,8 @@ class BoxConstraints {
162 return BoxConstraints( 158 return BoxConstraints(
163 minWidth: width == null ? minWidth : width.clamp(minWidth, maxWidth), 159 minWidth: width == null ? minWidth : width.clamp(minWidth, maxWidth),
164 maxWidth: width == null ? maxWidth : width.clamp(minWidth, maxWidth), 160 maxWidth: width == null ? maxWidth : width.clamp(minWidth, maxWidth),
165 - minHeight:  
166 - height == null ? minHeight : height.clamp(minHeight, maxHeight),  
167 - maxHeight:  
168 - height == null ? maxHeight : height.clamp(minHeight, maxHeight)); 161 + minHeight: height == null ? minHeight : height.clamp(minHeight, maxHeight),
  162 + maxHeight: height == null ? maxHeight : height.clamp(minHeight, maxHeight));
169 } 163 }
170 164
171 /// Returns new box constraints that are smaller by the given edge dimensions. 165 /// Returns new box constraints that are smaller by the given edge dimensions.
@@ -197,17 +191,11 @@ class BoxConstraints { @@ -197,17 +191,11 @@ class BoxConstraints {
197 return BoxConstraints( 191 return BoxConstraints(
198 minWidth: minWidth.clamp(constraints.minWidth, constraints.maxWidth), 192 minWidth: minWidth.clamp(constraints.minWidth, constraints.maxWidth),
199 maxWidth: maxWidth.clamp(constraints.minWidth, constraints.maxWidth), 193 maxWidth: maxWidth.clamp(constraints.minWidth, constraints.maxWidth),
200 - minHeight:  
201 - minHeight.clamp(constraints.minHeight, constraints.maxHeight),  
202 - maxHeight:  
203 - maxHeight.clamp(constraints.minHeight, constraints.maxHeight)); 194 + minHeight: minHeight.clamp(constraints.minHeight, constraints.maxHeight),
  195 + maxHeight: maxHeight.clamp(constraints.minHeight, constraints.maxHeight));
204 } 196 }
205 197
206 - BoxConstraints copyWith(  
207 - {double? minWidth,  
208 - double? maxWidth,  
209 - double? minHeight,  
210 - double? maxHeight}) { 198 + BoxConstraints copyWith({double? minWidth, double? maxWidth, double? minHeight, double? maxHeight}) {
211 return BoxConstraints( 199 return BoxConstraints(
212 minWidth: minWidth ?? this.minWidth, 200 minWidth: minWidth ?? this.minWidth,
213 maxWidth: maxWidth ?? this.maxWidth, 201 maxWidth: maxWidth ?? this.maxWidth,
@@ -222,7 +210,96 @@ class BoxConstraints { @@ -222,7 +210,96 @@ class BoxConstraints {
222 } 210 }
223 211
224 @immutable 212 @immutable
225 -class EdgeInsets { 213 +abstract class EdgeInsetsGeometry {
  214 + /// Abstract const constructor. This constructor enables subclasses to provide
  215 + /// const constructors so that they can be used in const expressions.
  216 + const EdgeInsetsGeometry();
  217 +
  218 + double get _bottom;
  219 +
  220 + double get _end;
  221 +
  222 + double get _left;
  223 +
  224 + double get _right;
  225 +
  226 + double get _start;
  227 +
  228 + double get _top;
  229 +
  230 + /// The total offset in the horizontal direction.
  231 + double get horizontal => _left + _right + _start + _end;
  232 +
  233 + /// The total offset in the vertical direction.
  234 + double get vertical => _top + _bottom;
  235 +
  236 + /// Convert this instance into an [EdgeInsets], which uses literal coordinates
  237 + /// (i.e. the `left` coordinate being explicitly a distance from the left, and
  238 + /// the `right` coordinate being explicitly a distance from the right).
  239 + ///
  240 + /// See also:
  241 + ///
  242 + /// * [EdgeInsets], for which this is a no-op (returns itself).
  243 + /// * [EdgeInsetsDirectional], which flips the horizontal direction
  244 + /// based on the `direction` argument.
  245 + EdgeInsets resolve(TextDirection? direction);
  246 +
  247 + /// Returns the sum of two [EdgeInsetsGeometry] objects.
  248 + ///
  249 + /// If you know you are adding two [EdgeInsets] or two [EdgeInsetsDirectional]
  250 + /// objects, consider using the `+` operator instead, which always returns an
  251 + /// object of the same type as the operands, and is typed accordingly.
  252 + ///
  253 + /// If [add] is applied to two objects of the same type ([EdgeInsets] or
  254 + /// [EdgeInsetsDirectional]), an object of that type will be returned (though
  255 + /// this is not reflected in the type system). Otherwise, an object
  256 + /// representing a combination of both is returned. That object can be turned
  257 + /// into a concrete [EdgeInsets] using [resolve].
  258 + EdgeInsetsGeometry add(EdgeInsetsGeometry other) {
  259 + return _MixedEdgeInsets.fromLRSETB(
  260 + _left + other._left,
  261 + _right + other._right,
  262 + _start + other._start,
  263 + _end + other._end,
  264 + _top + other._top,
  265 + _bottom + other._bottom,
  266 + );
  267 + }
  268 +
  269 + @override
  270 + String toString() {
  271 + if (_start == 0.0 && _end == 0.0) {
  272 + if (_left == 0.0 && _right == 0.0 && _top == 0.0 && _bottom == 0.0) {
  273 + return 'EdgeInsets.zero';
  274 + }
  275 + if (_left == _right && _right == _top && _top == _bottom) {
  276 + return 'EdgeInsets.all(${_left.toStringAsFixed(1)})';
  277 + }
  278 + return 'EdgeInsets(${_left.toStringAsFixed(1)}, '
  279 + '${_top.toStringAsFixed(1)}, '
  280 + '${_right.toStringAsFixed(1)}, '
  281 + '${_bottom.toStringAsFixed(1)})';
  282 + }
  283 + if (_left == 0.0 && _right == 0.0) {
  284 + return 'EdgeInsetsDirectional(${_start.toStringAsFixed(1)}, '
  285 + '${_top.toStringAsFixed(1)}, '
  286 + '${_end.toStringAsFixed(1)}, '
  287 + '${_bottom.toStringAsFixed(1)})';
  288 + }
  289 + return 'EdgeInsets(${_left.toStringAsFixed(1)}, '
  290 + '${_top.toStringAsFixed(1)}, '
  291 + '${_right.toStringAsFixed(1)}, '
  292 + '${_bottom.toStringAsFixed(1)})'
  293 + ' + '
  294 + 'EdgeInsetsDirectional(${_start.toStringAsFixed(1)}, '
  295 + '0.0, '
  296 + '${_end.toStringAsFixed(1)}, '
  297 + '0.0)';
  298 + }
  299 +}
  300 +
  301 +@immutable
  302 +class EdgeInsets extends EdgeInsetsGeometry {
226 const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom); 303 const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom);
227 304
228 const EdgeInsets.all(double value) 305 const EdgeInsets.all(double value)
@@ -231,8 +308,7 @@ class EdgeInsets { @@ -231,8 +308,7 @@ class EdgeInsets {
231 right = value, 308 right = value,
232 bottom = value; 309 bottom = value;
233 310
234 - const EdgeInsets.only(  
235 - {this.left = 0.0, this.top = 0.0, this.right = 0.0, this.bottom = 0.0}); 311 + const EdgeInsets.only({this.left = 0.0, this.top = 0.0, this.right = 0.0, this.bottom = 0.0});
236 312
237 const EdgeInsets.symmetric({double vertical = 0.0, double horizontal = 0.0}) 313 const EdgeInsets.symmetric({double vertical = 0.0, double horizontal = 0.0})
238 : left = horizontal, 314 : left = horizontal,
@@ -242,19 +318,45 @@ class EdgeInsets { @@ -242,19 +318,45 @@ class EdgeInsets {
242 318
243 static const EdgeInsets zero = EdgeInsets.only(); 319 static const EdgeInsets zero = EdgeInsets.only();
244 320
  321 + /// The offset from the left.
245 final double left; 322 final double left;
246 323
  324 + @override
  325 + double get _left => left;
  326 +
  327 + /// The offset from the top.
247 final double top; 328 final double top;
248 329
  330 + @override
  331 + double get _top => top;
  332 +
  333 + /// The offset from the right.
249 final double right; 334 final double right;
250 335
  336 + @override
  337 + double get _right => right;
  338 +
  339 + /// The offset from the bottom.
251 final double bottom; 340 final double bottom;
252 341
253 - /// The total offset in the horizontal direction.  
254 - double get horizontal => left + right; 342 + @override
  343 + double get _bottom => bottom;
255 344
256 - /// The total offset in the vertical direction.  
257 - double get vertical => top + bottom; 345 + @override
  346 + double get _start => 0.0;
  347 +
  348 + @override
  349 + double get _end => 0.0;
  350 +
  351 + /// Returns the sum of two [EdgeInsets].
  352 + EdgeInsets operator +(EdgeInsets other) {
  353 + return EdgeInsets.fromLTRB(
  354 + left + other.left,
  355 + top + other.top,
  356 + right + other.right,
  357 + bottom + other.bottom,
  358 + );
  359 + }
258 360
259 EdgeInsets copyWith({ 361 EdgeInsets copyWith({
260 double? left, 362 double? left,
@@ -270,18 +372,198 @@ class EdgeInsets { @@ -270,18 +372,198 @@ class EdgeInsets {
270 ); 372 );
271 } 373 }
272 374
273 - /// Returns the sum of two [EdgeInsets] objects.  
274 - EdgeInsets add(EdgeInsets other) {  
275 - return EdgeInsets.fromLTRB(  
276 - left + other.left, 375 + @override
  376 + EdgeInsetsGeometry add(EdgeInsetsGeometry other) {
  377 + if (other is EdgeInsets) {
  378 + return this + other;
  379 + }
  380 + return super.add(other);
  381 + }
  382 +
  383 + @override
  384 + EdgeInsets resolve(TextDirection? direction) => this;
  385 +}
  386 +
  387 +class _MixedEdgeInsets extends EdgeInsetsGeometry {
  388 + const _MixedEdgeInsets.fromLRSETB(this._left, this._right, this._start, this._end, this._top, this._bottom);
  389 +
  390 + @override
  391 + final double _left;
  392 +
  393 + @override
  394 + final double _right;
  395 +
  396 + @override
  397 + final double _start;
  398 +
  399 + @override
  400 + final double _end;
  401 +
  402 + @override
  403 + final double _top;
  404 +
  405 + @override
  406 + final double _bottom;
  407 +
  408 + @override
  409 + EdgeInsets resolve(TextDirection? direction) {
  410 + assert(direction != null);
  411 + switch (direction!) {
  412 + case TextDirection.rtl:
  413 + return EdgeInsets.fromLTRB(_end + _left, _top, _start + _right, _bottom);
  414 + case TextDirection.ltr:
  415 + return EdgeInsets.fromLTRB(_start + _left, _top, _end + _right, _bottom);
  416 + }
  417 + }
  418 +}
  419 +
  420 +/// An immutable set of offsets in each of the four cardinal directions, but
  421 +/// whose horizontal components are dependent on the writing direction.
  422 +///
  423 +/// This can be used to indicate padding from the left in [TextDirection.ltr]
  424 +/// text and padding from the right in [TextDirection.rtl] text without having
  425 +/// to be aware of the current text direction.
  426 +///
  427 +/// See also:
  428 +///
  429 +/// * [EdgeInsets], a variant that uses physical labels (left and right instead
  430 +/// of start and end).
  431 +class EdgeInsetsDirectional extends EdgeInsetsGeometry {
  432 + /// Creates insets from offsets from the start, top, end, and bottom.
  433 + const EdgeInsetsDirectional.fromSTEB(this.start, this.top, this.end, this.bottom);
  434 +
  435 + /// Creates insets with only the given values non-zero.
  436 + ///
  437 + /// {@tool snippet}
  438 + ///
  439 + /// A margin indent of 40 pixels on the leading side:
  440 + ///
  441 + /// ```dart
  442 + /// const EdgeInsetsDirectional.only(start: 40.0)
  443 + /// ```
  444 + /// {@end-tool}
  445 + const EdgeInsetsDirectional.only({
  446 + this.start = 0.0,
  447 + this.top = 0.0,
  448 + this.end = 0.0,
  449 + this.bottom = 0.0,
  450 + });
  451 +
  452 + /// Creates insets with symmetric vertical and horizontal offsets.
  453 + ///
  454 + /// This is equivalent to [EdgeInsets.symmetric], since the inset is the same
  455 + /// with either [TextDirection]. This constructor is just a convenience for
  456 + /// type compatibility.
  457 + ///
  458 + /// {@tool snippet}
  459 + /// Eight pixel margin above and below, no horizontal margins:
  460 + ///
  461 + /// ```dart
  462 + /// const EdgeInsetsDirectional.symmetric(vertical: 8.0)
  463 + /// ```
  464 + /// {@end-tool}
  465 + const EdgeInsetsDirectional.symmetric({
  466 + double horizontal = 0.0,
  467 + double vertical = 0.0,
  468 + }) : start = horizontal,
  469 + end = horizontal,
  470 + top = vertical,
  471 + bottom = vertical;
  472 +
  473 + /// Creates insets where all the offsets are `value`.
  474 + ///
  475 + /// {@tool snippet}
  476 + ///
  477 + /// Typical eight-pixel margin on all sides:
  478 + ///
  479 + /// ```dart
  480 + /// const EdgeInsetsDirectional.all(8.0)
  481 + /// ```
  482 + /// {@end-tool}
  483 + const EdgeInsetsDirectional.all(double value)
  484 + : start = value,
  485 + top = value,
  486 + end = value,
  487 + bottom = value;
  488 +
  489 + /// An [EdgeInsetsDirectional] with zero offsets in each direction.
  490 + ///
  491 + /// Consider using [EdgeInsets.zero] instead, since that object has the same
  492 + /// effect, but will be cheaper to [resolve].
  493 + static const EdgeInsetsDirectional zero = EdgeInsetsDirectional.only();
  494 +
  495 + /// The offset from the start side, the side from which the user will start
  496 + /// reading text.
  497 + ///
  498 + /// This value is normalized into an [EdgeInsets.left] or [EdgeInsets.right]
  499 + /// value by the [resolve] method.
  500 + final double start;
  501 +
  502 + @override
  503 + double get _start => start;
  504 +
  505 + /// The offset from the top.
  506 + ///
  507 + /// This value is passed through to [EdgeInsets.top] unmodified by the
  508 + /// [resolve] method.
  509 + final double top;
  510 +
  511 + @override
  512 + double get _top => top;
  513 +
  514 + /// The offset from the end side, the side on which the user ends reading
  515 + /// text.
  516 + ///
  517 + /// This value is normalized into an [EdgeInsets.left] or [EdgeInsets.right]
  518 + /// value by the [resolve] method.
  519 + final double end;
  520 +
  521 + @override
  522 + double get _end => end;
  523 +
  524 + /// The offset from the bottom.
  525 + ///
  526 + /// This value is passed through to [EdgeInsets.bottom] unmodified by the
  527 + /// [resolve] method.
  528 + final double bottom;
  529 +
  530 + @override
  531 + double get _bottom => bottom;
  532 +
  533 + @override
  534 + double get _left => 0.0;
  535 +
  536 + @override
  537 + double get _right => 0.0;
  538 +
  539 + @override
  540 + EdgeInsetsGeometry add(EdgeInsetsGeometry other) {
  541 + if (other is EdgeInsetsDirectional) {
  542 + return this + other;
  543 + }
  544 + return super.add(other);
  545 + }
  546 +
  547 + /// Returns the sum of two [EdgeInsetsDirectional] objects.
  548 + EdgeInsetsDirectional operator +(EdgeInsetsDirectional other) {
  549 + return EdgeInsetsDirectional.fromSTEB(
  550 + start + other.start,
277 top + other.top, 551 top + other.top,
278 - right + other.right, 552 + end + other.end,
279 bottom + other.bottom, 553 bottom + other.bottom,
280 ); 554 );
281 } 555 }
282 556
283 @override 557 @override
284 - String toString() => 'EdgeInsets $left, $top, $right, $bottom'; 558 + EdgeInsets resolve(TextDirection? direction) {
  559 + assert(direction != null);
  560 + switch (direction!) {
  561 + case TextDirection.rtl:
  562 + return EdgeInsets.fromLTRB(end, top, start, bottom);
  563 + case TextDirection.ltr:
  564 + return EdgeInsets.fromLTRB(start, top, end, bottom);
  565 + }
  566 + }
285 } 567 }
286 568
287 class Alignment { 569 class Alignment {
@@ -374,10 +656,7 @@ class FittedSizes { @@ -374,10 +656,7 @@ class FittedSizes {
374 } 656 }
375 657
376 FittedSizes applyBoxFit(BoxFit fit, PdfPoint inputSize, PdfPoint outputSize) { 658 FittedSizes applyBoxFit(BoxFit fit, PdfPoint inputSize, PdfPoint outputSize) {
377 - if (inputSize.y <= 0.0 ||  
378 - inputSize.x <= 0.0 ||  
379 - outputSize.y <= 0.0 ||  
380 - outputSize.x <= 0.0) { 659 + if (inputSize.y <= 0.0 || inputSize.x <= 0.0 || outputSize.y <= 0.0 || outputSize.x <= 0.0) {
381 return const FittedSizes(PdfPoint.zero, PdfPoint.zero); 660 return const FittedSizes(PdfPoint.zero, PdfPoint.zero);
382 } 661 }
383 662
@@ -390,38 +669,29 @@ FittedSizes applyBoxFit(BoxFit fit, PdfPoint inputSize, PdfPoint outputSize) { @@ -390,38 +669,29 @@ FittedSizes applyBoxFit(BoxFit fit, PdfPoint inputSize, PdfPoint outputSize) {
390 case BoxFit.contain: 669 case BoxFit.contain:
391 sourceSize = inputSize; 670 sourceSize = inputSize;
392 if (outputSize.x / outputSize.y > sourceSize.x / sourceSize.y) { 671 if (outputSize.x / outputSize.y > sourceSize.x / sourceSize.y) {
393 - destinationSize =  
394 - PdfPoint(sourceSize.x * outputSize.y / sourceSize.y, outputSize.y); 672 + destinationSize = PdfPoint(sourceSize.x * outputSize.y / sourceSize.y, outputSize.y);
395 } else { 673 } else {
396 - destinationSize =  
397 - PdfPoint(outputSize.x, sourceSize.y * outputSize.x / sourceSize.x); 674 + destinationSize = PdfPoint(outputSize.x, sourceSize.y * outputSize.x / sourceSize.x);
398 } 675 }
399 break; 676 break;
400 case BoxFit.cover: 677 case BoxFit.cover:
401 if (outputSize.x / outputSize.y > inputSize.x / inputSize.y) { 678 if (outputSize.x / outputSize.y > inputSize.x / inputSize.y) {
402 - sourceSize =  
403 - PdfPoint(inputSize.x, inputSize.x * outputSize.y / outputSize.x); 679 + sourceSize = PdfPoint(inputSize.x, inputSize.x * outputSize.y / outputSize.x);
404 } else { 680 } else {
405 - sourceSize =  
406 - PdfPoint(inputSize.y * outputSize.x / outputSize.y, inputSize.y); 681 + sourceSize = PdfPoint(inputSize.y * outputSize.x / outputSize.y, inputSize.y);
407 } 682 }
408 destinationSize = outputSize; 683 destinationSize = outputSize;
409 break; 684 break;
410 case BoxFit.fitWidth: 685 case BoxFit.fitWidth:
411 - sourceSize =  
412 - PdfPoint(inputSize.x, inputSize.x * outputSize.y / outputSize.x);  
413 - destinationSize =  
414 - PdfPoint(outputSize.x, sourceSize.y * outputSize.x / sourceSize.x); 686 + sourceSize = PdfPoint(inputSize.x, inputSize.x * outputSize.y / outputSize.x);
  687 + destinationSize = PdfPoint(outputSize.x, sourceSize.y * outputSize.x / sourceSize.x);
415 break; 688 break;
416 case BoxFit.fitHeight: 689 case BoxFit.fitHeight:
417 - sourceSize =  
418 - PdfPoint(inputSize.y * outputSize.x / outputSize.y, inputSize.y);  
419 - destinationSize =  
420 - PdfPoint(sourceSize.x * outputSize.y / sourceSize.y, outputSize.y); 690 + sourceSize = PdfPoint(inputSize.y * outputSize.x / outputSize.y, inputSize.y);
  691 + destinationSize = PdfPoint(sourceSize.x * outputSize.y / sourceSize.y, outputSize.y);
421 break; 692 break;
422 case BoxFit.none: 693 case BoxFit.none:
423 - sourceSize = PdfPoint(math.min(inputSize.x, outputSize.x),  
424 - math.min(inputSize.y, outputSize.y)); 694 + sourceSize = PdfPoint(math.min(inputSize.x, outputSize.x), math.min(inputSize.y, outputSize.y));
425 destinationSize = sourceSize; 695 destinationSize = sourceSize;
426 break; 696 break;
427 case BoxFit.scaleDown: 697 case BoxFit.scaleDown:
@@ -61,14 +61,12 @@ mixin SpanningWidget on Widget { @@ -61,14 +61,12 @@ mixin SpanningWidget on Widget {
61 /// Called before relayout to restore the saved state and 61 /// Called before relayout to restore the saved state and
62 /// restart the layout in the same conditions 62 /// restart the layout in the same conditions
63 @protected 63 @protected
64 - void applyContext(covariant WidgetContext context) =>  
65 - saveContext().apply(context); 64 + void applyContext(covariant WidgetContext context) => saveContext().apply(context);
66 } 65 }
67 66
68 class NewPage extends Widget { 67 class NewPage extends Widget {
69 @override 68 @override
70 - void layout(Context context, BoxConstraints constraints,  
71 - {bool parentUsesSize = false}) { 69 + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) {
72 box = PdfRect.zero; 70 box = PdfRect.zero;
73 } 71 }
74 } 72 }
@@ -151,7 +149,7 @@ class MultiPage extends Page { @@ -151,7 +149,7 @@ class MultiPage extends Page {
151 ThemeData? theme, 149 ThemeData? theme,
152 this.maxPages = 20, 150 this.maxPages = 20,
153 PageOrientation? orientation, 151 PageOrientation? orientation,
154 - EdgeInsets? margin, 152 + EdgeInsetsGeometry? margin,
155 TextDirection? textDirection, 153 TextDirection? textDirection,
156 }) : _buildList = build, 154 }) : _buildList = build,
157 assert(maxPages > 0), 155 assert(maxPages > 0),
@@ -185,10 +183,9 @@ class MultiPage extends Page { @@ -185,10 +183,9 @@ class MultiPage extends Page {
185 /// This is not checked with a Release build. 183 /// This is not checked with a Release build.
186 final int maxPages; 184 final int maxPages;
187 185
188 - void _paintChild(  
189 - Context context, Widget child, double x, double y, double pageHeight) { 186 + void _paintChild(Context context, Widget child, double x, double y, double pageHeight) {
190 if (mustRotate) { 187 if (mustRotate) {
191 - final _margin = margin!; 188 + final _margin = resolvedMargin!;
192 context.canvas 189 context.canvas
193 ..saveContext() 190 ..saveContext()
194 ..setTransform(Matrix4.identity() 191 ..setTransform(Matrix4.identity()
@@ -198,10 +195,21 @@ class MultiPage extends Page { @@ -198,10 +195,21 @@ class MultiPage extends Page {
198 y + _margin.left - _margin.bottom, 195 y + _margin.left - _margin.bottom,
199 )); 196 ));
200 197
  198 + final availableWidth = pageHeight - resolvedMargin!.vertical;
  199 + if (pageTheme.textDirection == TextDirection.rtl) {
  200 + child.box = child.box!.copyWith(
  201 + x: (availableWidth - child.box!.width) + child.box!.x,
  202 + );
  203 + }
201 child.paint(context); 204 child.paint(context);
202 context.canvas.restoreContext(); 205 context.canvas.restoreContext();
203 } else { 206 } else {
204 - child.box = PdfRect(x, y, child.box!.width, child.box!.height); 207 + var childXPos = x;
  208 + if (pageTheme.textDirection == TextDirection.rtl) {
  209 + final availableWidth = pageFormat.width - resolvedMargin!.horizontal;
  210 + childXPos = (availableWidth - child.box!.width) + x;
  211 + }
  212 + child.box = child.box!.copyWith(x: childXPos, y: y);
205 child.paint(context); 213 child.paint(context);
206 } 214 }
207 } 215 }
@@ -211,33 +219,26 @@ class MultiPage extends Page { @@ -211,33 +219,26 @@ class MultiPage extends Page {
211 assert(pageFormat.width > 0 && pageFormat.width < double.infinity); 219 assert(pageFormat.width > 0 && pageFormat.width < double.infinity);
212 assert(pageFormat.height > 0 && pageFormat.height < double.infinity); 220 assert(pageFormat.height > 0 && pageFormat.height < double.infinity);
213 221
214 - final _margin = margin; 222 + final _margin = resolvedMargin!;
215 final _mustRotate = mustRotate; 223 final _mustRotate = mustRotate;
216 final pageHeight = _mustRotate ? pageFormat.width : pageFormat.height; 224 final pageHeight = _mustRotate ? pageFormat.width : pageFormat.height;
217 - final pageHeightMargin =  
218 - _mustRotate ? _margin!.horizontal : _margin!.vertical; 225 + final pageHeightMargin = _mustRotate ? _margin.horizontal : _margin.vertical;
219 final constraints = BoxConstraints( 226 final constraints = BoxConstraints(
220 - maxWidth: _mustRotate  
221 - ? (pageFormat.height - _margin.vertical)  
222 - : (pageFormat.width - _margin.horizontal)); 227 + maxWidth: _mustRotate ? (pageFormat.height - _margin.vertical) : (pageFormat.width - _margin.horizontal));
223 final fullConstraints = mustRotate 228 final fullConstraints = mustRotate
224 ? BoxConstraints( 229 ? BoxConstraints(
225 - maxWidth: pageFormat.height - _margin.vertical,  
226 - maxHeight: pageFormat.width - _margin.horizontal) 230 + maxWidth: pageFormat.height - _margin.vertical, maxHeight: pageFormat.width - _margin.horizontal)
227 : BoxConstraints( 231 : BoxConstraints(
228 - maxWidth: pageFormat.width - _margin.horizontal,  
229 - maxHeight: pageFormat.height - _margin.vertical); 232 + maxWidth: pageFormat.width - _margin.horizontal, maxHeight: pageFormat.height - _margin.vertical);
230 final calculatedTheme = theme ?? document.theme ?? ThemeData.base(); 233 final calculatedTheme = theme ?? document.theme ?? ThemeData.base();
231 Context? context; 234 Context? context;
232 late double offsetEnd; 235 late double offsetEnd;
233 double? offsetStart; 236 double? offsetStart;
234 var _index = 0; 237 var _index = 0;
235 var sameCount = 0; 238 var sameCount = 0;
236 - final baseContext =  
237 - Context(document: document.document).inheritFromAll(<Inherited>[ 239 + final baseContext = Context(document: document.document).inheritFromAll(<Inherited>[
238 calculatedTheme, 240 calculatedTheme,
239 - if (pageTheme.textDirection != null)  
240 - InheritedDirectionality(pageTheme.textDirection), 241 + if (pageTheme.textDirection != null) InheritedDirectionality(pageTheme.textDirection),
241 ]); 242 ]);
242 final children = _buildList(baseContext); 243 final children = _buildList(baseContext);
243 WidgetContext? widgetContext; 244 WidgetContext? widgetContext;
@@ -272,10 +273,8 @@ class MultiPage extends Page { @@ -272,10 +273,8 @@ class MultiPage extends Page {
272 return true; 273 return true;
273 }()); 274 }());
274 275
275 - offsetStart = pageHeight -  
276 - (_mustRotate ? pageHeightMargin - _margin.bottom : _margin.top);  
277 - offsetEnd =  
278 - _mustRotate ? pageHeightMargin - _margin.left : _margin.bottom; 276 + offsetStart = pageHeight - (_mustRotate ? pageHeightMargin - _margin.bottom : _margin.top);
  277 + offsetEnd = _mustRotate ? pageHeightMargin - _margin.left : _margin.bottom;
279 278
280 _pages.add(_MultiPageInstance( 279 _pages.add(_MultiPageInstance(
281 context: context, 280 context: context,
@@ -327,8 +326,7 @@ class MultiPage extends Page { @@ -327,8 +326,7 @@ class MultiPage extends Page {
327 326
328 // Else we crash if the widget is too big and cannot be separated 327 // Else we crash if the widget is too big and cannot be separated
329 if (!canSpan) { 328 if (!canSpan) {
330 - throw Exception(  
331 - 'Widget won\'t fit into the page as its height (${child.box!.height}) ' 329 + throw Exception('Widget won\'t fit into the page as its height (${child.box!.height}) '
332 'exceed a page height (${pageHeight - pageHeightMargin}). ' 330 'exceed a page height (${pageHeight - pageHeightMargin}). '
333 'You probably need a SpanningWidget or use a single page layout'); 331 'You probably need a SpanningWidget or use a single page layout');
334 } 332 }
@@ -340,8 +338,7 @@ class MultiPage extends Page { @@ -340,8 +338,7 @@ class MultiPage extends Page {
340 span.applyContext(savedContext); 338 span.applyContext(savedContext);
341 } 339 }
342 340
343 - final localConstraints =  
344 - constraints.copyWith(maxHeight: offsetStart - offsetEnd); 341 + final localConstraints = constraints.copyWith(maxHeight: offsetStart - offsetEnd);
345 span.layout(context, localConstraints, parentUsesSize: false); 342 span.layout(context, localConstraints, parentUsesSize: false);
346 assert(span.box != null); 343 assert(span.box != null);
347 widgetContext = span.saveContext(); 344 widgetContext = span.saveContext();
@@ -368,8 +365,7 @@ class MultiPage extends Page { @@ -368,8 +365,7 @@ class MultiPage extends Page {
368 _MultiPageWidget( 365 _MultiPageWidget(
369 child: child, 366 child: child,
370 constraints: constraints, 367 constraints: constraints,
371 - widgetContext:  
372 - child is SpanningWidget && canSpan ? child.cloneContext() : null, 368 + widgetContext: child is SpanningWidget && canSpan ? child.cloneContext() : null,
373 ), 369 ),
374 ); 370 );
375 371
@@ -381,28 +377,24 @@ class MultiPage extends Page { @@ -381,28 +377,24 @@ class MultiPage extends Page {
381 377
382 @override 378 @override
383 void postProcess(Document document) { 379 void postProcess(Document document) {
384 - final _margin = margin; 380 + final _margin = resolvedMargin!;
385 final _mustRotate = mustRotate; 381 final _mustRotate = mustRotate;
386 final pageHeight = _mustRotate ? pageFormat.width : pageFormat.height; 382 final pageHeight = _mustRotate ? pageFormat.width : pageFormat.height;
387 final pageWidth = _mustRotate ? pageFormat.height : pageFormat.width; 383 final pageWidth = _mustRotate ? pageFormat.height : pageFormat.width;
388 - final pageHeightMargin =  
389 - _mustRotate ? _margin!.horizontal : _margin!.vertical; 384 + final pageHeightMargin = _mustRotate ? _margin.horizontal : _margin.vertical;
390 final pageWidthMargin = _mustRotate ? _margin.vertical : _margin.horizontal; 385 final pageWidthMargin = _mustRotate ? _margin.vertical : _margin.horizontal;
391 final availableWidth = pageWidth - pageWidthMargin; 386 final availableWidth = pageWidth - pageWidthMargin;
392 387
393 for (final page in _pages) { 388 for (final page in _pages) {
394 - var offsetStart = pageHeight -  
395 - (_mustRotate ? pageHeightMargin - _margin.bottom : _margin.top);  
396 - var offsetEnd =  
397 - _mustRotate ? pageHeightMargin - _margin.left : _margin.bottom; 389 + var offsetStart = pageHeight - (_mustRotate ? pageHeightMargin - _margin.bottom : _margin.top);
  390 + var offsetEnd = _mustRotate ? pageHeightMargin - _margin.left : _margin.bottom;
398 391
399 if (pageTheme.buildBackground != null) { 392 if (pageTheme.buildBackground != null) {
400 final child = pageTheme.buildBackground!(page.context); 393 final child = pageTheme.buildBackground!(page.context);
401 394
402 child.layout(page.context, page.fullConstraints, parentUsesSize: false); 395 child.layout(page.context, page.fullConstraints, parentUsesSize: false);
403 assert(child.box != null); 396 assert(child.box != null);
404 - _paintChild(page.context, child, _margin.left, _margin.bottom,  
405 - pageFormat.height); 397 + _paintChild(page.context, child, _margin.left, _margin.bottom, pageFormat.height);
406 } 398 }
407 399
408 var totalFlex = 0; 400 var totalFlex = 0;
@@ -428,23 +420,20 @@ class MultiPage extends Page { @@ -428,23 +420,20 @@ class MultiPage extends Page {
428 if (header != null) { 420 if (header != null) {
429 final headerWidget = header!(page.context); 421 final headerWidget = header!(page.context);
430 422
431 - headerWidget.layout(page.context, page.constraints,  
432 - parentUsesSize: false); 423 + headerWidget.layout(page.context, page.constraints, parentUsesSize: false);
433 assert(headerWidget.box != null); 424 assert(headerWidget.box != null);
434 offsetStart -= headerWidget.box!.height; 425 offsetStart -= headerWidget.box!.height;
435 - _paintChild(page.context, headerWidget, _margin.left,  
436 - page.offsetStart! - headerWidget.box!.height, pageFormat.height); 426 + _paintChild(
  427 + page.context, headerWidget, _margin.left, page.offsetStart! - headerWidget.box!.height, pageFormat.height);
437 } 428 }
438 429
439 if (footer != null) { 430 if (footer != null) {
440 final footerWidget = footer!(page.context); 431 final footerWidget = footer!(page.context);
441 432
442 - footerWidget.layout(page.context, page.constraints,  
443 - parentUsesSize: false); 433 + footerWidget.layout(page.context, page.constraints, parentUsesSize: false);
444 assert(footerWidget.box != null); 434 assert(footerWidget.box != null);
445 offsetEnd += footerWidget.box!.height; 435 offsetEnd += footerWidget.box!.height;
446 - _paintChild(page.context, footerWidget, _margin.left, _margin.bottom,  
447 - pageFormat.height); 436 + _paintChild(page.context, footerWidget, _margin.left, _margin.bottom, pageFormat.height);
448 } 437 }
449 438
450 final freeSpace = math.max(0.0, offsetStart - offsetEnd - allocatedSize); 439 final freeSpace = math.max(0.0, offsetStart - offsetEnd - allocatedSize);
@@ -473,16 +462,14 @@ class MultiPage extends Page { @@ -473,16 +462,14 @@ class MultiPage extends Page {
473 break; 462 break;
474 case MainAxisAlignment.spaceBetween: 463 case MainAxisAlignment.spaceBetween:
475 leadingSpace = 0.0; 464 leadingSpace = 0.0;
476 - betweenSpace =  
477 - totalChildren > 1 ? freeSpace / (totalChildren - 1) : 0.0; 465 + betweenSpace = totalChildren > 1 ? freeSpace / (totalChildren - 1) : 0.0;
478 break; 466 break;
479 case MainAxisAlignment.spaceAround: 467 case MainAxisAlignment.spaceAround:
480 betweenSpace = totalChildren > 0 ? freeSpace / totalChildren : 0.0; 468 betweenSpace = totalChildren > 0 ? freeSpace / totalChildren : 0.0;
481 leadingSpace = betweenSpace / 2.0; 469 leadingSpace = betweenSpace / 2.0;
482 break; 470 break;
483 case MainAxisAlignment.spaceEvenly: 471 case MainAxisAlignment.spaceEvenly:
484 - betweenSpace =  
485 - totalChildren > 0 ? freeSpace / (totalChildren + 1) : 0.0; 472 + betweenSpace = totalChildren > 0 ? freeSpace / (totalChildren + 1) : 0.0;
486 leadingSpace = betweenSpace; 473 leadingSpace = betweenSpace;
487 break; 474 break;
488 } 475 }
@@ -494,11 +481,8 @@ class MultiPage extends Page { @@ -494,11 +481,8 @@ class MultiPage extends Page {
494 final flex = child is Flexible ? child.flex : 0; 481 final flex = child is Flexible ? child.flex : 0;
495 final fit = child is Flexible ? child.fit : FlexFit.loose; 482 final fit = child is Flexible ? child.fit : FlexFit.loose;
496 if (flex > 0) { 483 if (flex > 0) {
497 - assert(child is! SpanningWidget || child.canSpan == false,  
498 - 'Cannot have a spanning widget flexible');  
499 - final maxChildExtent = child == lastFlexChild  
500 - ? (freeSpace - allocatedFlexSpace)  
501 - : spacePerFlex * flex; 484 + assert(child is! SpanningWidget || child.canSpan == false, 'Cannot have a spanning widget flexible');
  485 + final maxChildExtent = child == lastFlexChild ? (freeSpace - allocatedFlexSpace) : spacePerFlex * flex;
502 late double minChildExtent; 486 late double minChildExtent;
503 switch (fit) { 487 switch (fit) {
504 case FlexFit.tight: 488 case FlexFit.tight:
@@ -547,8 +531,7 @@ class MultiPage extends Page { @@ -547,8 +531,7 @@ class MultiPage extends Page {
547 if (child is SpanningWidget && child.canSpan) { 531 if (child is SpanningWidget && child.canSpan) {
548 child.applyContext(widget.widgetContext!); 532 child.applyContext(widget.widgetContext!);
549 } 533 }
550 - _paintChild(page.context, widget.child, _margin.left + x, pos,  
551 - pageFormat.height); 534 + _paintChild(page.context, widget.child, _margin.left + x, pos, pageFormat.height);
552 pos -= betweenSpace; 535 pos -= betweenSpace;
553 } 536 }
554 537
@@ -557,8 +540,7 @@ class MultiPage extends Page { @@ -557,8 +540,7 @@ class MultiPage extends Page {
557 540
558 child.layout(page.context, page.fullConstraints, parentUsesSize: false); 541 child.layout(page.context, page.fullConstraints, parentUsesSize: false);
559 assert(child.box != null); 542 assert(child.box != null);
560 - _paintChild(page.context, child, _margin.left, _margin.bottom,  
561 - pageFormat.height); 543 + _paintChild(page.context, child, _margin.left, _margin.bottom, pageFormat.height);
562 } 544 }
563 } 545 }
564 } 546 }
@@ -40,7 +40,7 @@ class Page { @@ -40,7 +40,7 @@ class Page {
40 required BuildCallback build, 40 required BuildCallback build,
41 ThemeData? theme, 41 ThemeData? theme,
42 PageOrientation? orientation, 42 PageOrientation? orientation,
43 - EdgeInsets? margin, 43 + EdgeInsetsGeometry? margin,
44 bool clip = false, 44 bool clip = false,
45 TextDirection? textDirection, 45 TextDirection? textDirection,
46 }) : assert( 46 }) : assert(
@@ -77,11 +77,14 @@ class Page { @@ -77,11 +77,14 @@ class Page {
77 77
78 PdfPage? _pdfPage; 78 PdfPage? _pdfPage;
79 79
80 - EdgeInsets? get margin => pageTheme.margin; 80 + EdgeInsetsGeometry? get margin => pageTheme.margin;
  81 +
  82 + EdgeInsets? get resolvedMargin => margin?.resolve(pageTheme.textDirection);
81 83
82 @protected 84 @protected
83 void debugPaint(Context context) { 85 void debugPaint(Context context) {
84 - final _margin = margin!; 86 + final _margin = resolvedMargin!;
  87 +
85 context.canvas 88 context.canvas
86 ..setFillColor(PdfColors.lightGreen) 89 ..setFillColor(PdfColors.lightGreen)
87 ..moveTo(0, 0) 90 ..moveTo(0, 0)
@@ -90,8 +93,7 @@ class Page { @@ -90,8 +93,7 @@ class Page {
90 ..lineTo(0, pageFormat.height) 93 ..lineTo(0, pageFormat.height)
91 ..moveTo(_margin.left, _margin.bottom) 94 ..moveTo(_margin.left, _margin.bottom)
92 ..lineTo(_margin.left, pageFormat.height - _margin.top) 95 ..lineTo(_margin.left, pageFormat.height - _margin.top)
93 - ..lineTo(  
94 - pageFormat.width - _margin.right, pageFormat.height - _margin.top) 96 + ..lineTo(pageFormat.width - _margin.right, pageFormat.height - _margin.top)
95 ..lineTo(pageFormat.width - _margin.right, _margin.bottom) 97 ..lineTo(pageFormat.width - _margin.right, _margin.bottom)
96 ..fillPath(); 98 ..fillPath();
97 } 99 }
@@ -99,8 +101,7 @@ class Page { @@ -99,8 +101,7 @@ class Page {
99 void generate(Document document, {bool insert = true, int? index}) { 101 void generate(Document document, {bool insert = true, int? index}) {
100 if (index != null) { 102 if (index != null) {
101 if (insert) { 103 if (insert) {
102 - _pdfPage =  
103 - PdfPage(document.document, pageFormat: pageFormat, index: index); 104 + _pdfPage = PdfPage(document.document, pageFormat: pageFormat, index: index);
104 } else { 105 } else {
105 _pdfPage = document.document.page(index); 106 _pdfPage = document.document.page(index);
106 } 107 }
@@ -112,14 +113,12 @@ class Page { @@ -112,14 +113,12 @@ class Page {
112 void postProcess(Document document) { 113 void postProcess(Document document) {
113 final canvas = _pdfPage!.getGraphics(); 114 final canvas = _pdfPage!.getGraphics();
114 canvas.reset(); 115 canvas.reset();
115 - final _margin = margin; 116 + final _margin = resolvedMargin;
116 var constraints = mustRotate 117 var constraints = mustRotate
117 ? BoxConstraints( 118 ? BoxConstraints(
118 - maxWidth: pageFormat.height - _margin!.vertical,  
119 - maxHeight: pageFormat.width - _margin.horizontal) 119 + maxWidth: pageFormat.height - _margin!.vertical, maxHeight: pageFormat.width - _margin.horizontal)
120 : BoxConstraints( 120 : BoxConstraints(
121 - maxWidth: pageFormat.width - _margin!.horizontal,  
122 - maxHeight: pageFormat.height - _margin.vertical); 121 + maxWidth: pageFormat.width - _margin!.horizontal, maxHeight: pageFormat.height - _margin.vertical);
123 122
124 final calculatedTheme = theme ?? document.theme ?? ThemeData.base(); 123 final calculatedTheme = theme ?? document.theme ?? ThemeData.base();
125 final context = Context( 124 final context = Context(
@@ -128,8 +127,7 @@ class Page { @@ -128,8 +127,7 @@ class Page {
128 canvas: canvas, 127 canvas: canvas,
129 ).inheritFromAll(<Inherited>[ 128 ).inheritFromAll(<Inherited>[
130 calculatedTheme, 129 calculatedTheme,
131 - if (pageTheme.textDirection != null)  
132 - InheritedDirectionality(pageTheme.textDirection), 130 + if (pageTheme.textDirection != null) InheritedDirectionality(pageTheme.textDirection),
133 ]); 131 ]);
134 132
135 Widget? background; 133 Widget? background;
@@ -141,8 +139,7 @@ class Page { @@ -141,8 +139,7 @@ class Page {
141 final size = layout(content, context, constraints); 139 final size = layout(content, context, constraints);
142 140
143 if (_pdfPage!.pageFormat.height == double.infinity) { 141 if (_pdfPage!.pageFormat.height == double.infinity) {
144 - _pdfPage!.pageFormat =  
145 - _pdfPage!.pageFormat.copyWith(width: size.x, height: size.y); 142 + _pdfPage!.pageFormat = _pdfPage!.pageFormat.copyWith(width: size.x, height: size.y);
146 constraints = mustRotate 143 constraints = mustRotate
147 ? BoxConstraints( 144 ? BoxConstraints(
148 maxWidth: _pdfPage!.pageFormat.height - _margin.vertical, 145 maxWidth: _pdfPage!.pageFormat.height - _margin.vertical,
@@ -181,43 +178,49 @@ class Page { @@ -181,43 +178,49 @@ class Page {
181 } 178 }
182 179
183 @protected 180 @protected
184 - PdfPoint layout(Widget child, Context context, BoxConstraints constraints,  
185 - {bool parentUsesSize = false}) {  
186 - final _margin = margin!; 181 + PdfPoint layout(Widget child, Context context, BoxConstraints constraints, {bool parentUsesSize = false}) {
  182 + final _margin = resolvedMargin!;
187 child.layout(context, constraints, parentUsesSize: parentUsesSize); 183 child.layout(context, constraints, parentUsesSize: parentUsesSize);
188 assert(child.box != null); 184 assert(child.box != null);
189 185
190 - final width = pageFormat.width == double.infinity  
191 - ? child.box!.width + _margin.left + _margin.right  
192 - : pageFormat.width; 186 + final width =
  187 + pageFormat.width == double.infinity ? child.box!.width + _margin.left + _margin.right : pageFormat.width;
193 188
194 - final height = pageFormat.height == double.infinity  
195 - ? child.box!.height + _margin.top + _margin.bottom  
196 - : pageFormat.height; 189 + final height =
  190 + pageFormat.height == double.infinity ? child.box!.height + _margin.top + _margin.bottom : pageFormat.height;
197 191
198 - child.box = PdfRect(_margin.left, height - child.box!.height - _margin.top,  
199 - child.box!.width, child.box!.height); 192 + child.box = PdfRect(_margin.left, height - child.box!.height - _margin.top, child.box!.width, child.box!.height);
200 193
201 return PdfPoint(width, height); 194 return PdfPoint(width, height);
202 } 195 }
203 196
204 @protected 197 @protected
205 void paint(Widget child, Context context) { 198 void paint(Widget child, Context context) {
  199 + final _margin = resolvedMargin!;
  200 + final box = PdfRect(
  201 + _margin.left,
  202 + _margin.bottom,
  203 + pageFormat.width - _margin.horizontal,
  204 + pageFormat.height - _margin.vertical,
  205 + );
206 if (pageTheme.clip) { 206 if (pageTheme.clip) {
207 - final _margin = margin!;  
208 context.canvas 207 context.canvas
209 ..saveContext() 208 ..saveContext()
210 - ..drawRect(  
211 - _margin.left,  
212 - _margin.bottom,  
213 - pageFormat.width - _margin.horizontal,  
214 - pageFormat.height - _margin.vertical,  
215 - ) 209 + ..drawRect(box.x, box.y, box.width, box.height)
216 ..clipPath(); 210 ..clipPath();
217 } 211 }
218 212
  213 + if (pageTheme.textDirection == TextDirection.rtl) {
  214 + child.box = PdfRect(
  215 + ((mustRotate ? box.height : box.width) - child.box!.width) + child.box!.x,
  216 + child.box!.y,
  217 + child.box!.width,
  218 + child.box!.height,
  219 + );
  220 + }
  221 +
219 if (mustRotate) { 222 if (mustRotate) {
220 - final _margin = margin!; 223 + final _margin = resolvedMargin!;
221 context.canvas 224 context.canvas
222 ..saveContext() 225 ..saveContext()
223 ..setTransform(Matrix4.identity() 226 ..setTransform(Matrix4.identity()
@@ -30,7 +30,7 @@ class PageTheme { @@ -30,7 +30,7 @@ class PageTheme {
30 this.buildForeground, 30 this.buildForeground,
31 this.theme, 31 this.theme,
32 PageOrientation? orientation, 32 PageOrientation? orientation,
33 - EdgeInsets? margin, 33 + EdgeInsetsGeometry? margin,
34 this.clip = false, 34 this.clip = false,
35 this.textDirection, 35 this.textDirection,
36 }) : pageFormat = pageFormat ?? PdfPageFormat.standard, 36 }) : pageFormat = pageFormat ?? PdfPageFormat.standard,
@@ -41,7 +41,7 @@ class PageTheme { @@ -41,7 +41,7 @@ class PageTheme {
41 41
42 final PageOrientation orientation; 42 final PageOrientation orientation;
43 43
44 - final EdgeInsets? _margin; 44 + final EdgeInsetsGeometry? _margin;
45 45
46 final BuildCallback? buildBackground; 46 final BuildCallback? buildBackground;
47 47
@@ -54,27 +54,30 @@ class PageTheme { @@ -54,27 +54,30 @@ class PageTheme {
54 final TextDirection? textDirection; 54 final TextDirection? textDirection;
55 55
56 bool get mustRotate => 56 bool get mustRotate =>
57 - (orientation == PageOrientation.landscape &&  
58 - pageFormat.height > pageFormat.width) ||  
59 - (orientation == PageOrientation.portrait &&  
60 - pageFormat.width > pageFormat.height); 57 + (orientation == PageOrientation.landscape && pageFormat.height > pageFormat.width) ||
  58 + (orientation == PageOrientation.portrait && pageFormat.width > pageFormat.height);
61 59
62 - EdgeInsets? get margin { 60 + EdgeInsetsGeometry? get margin {
63 if (_margin != null) { 61 if (_margin != null) {
  62 + final effectiveMargin = _margin!.resolve(textDirection);
64 if (mustRotate) { 63 if (mustRotate) {
65 return EdgeInsets.fromLTRB( 64 return EdgeInsets.fromLTRB(
66 - _margin!.bottom, _margin!.left, _margin!.top, _margin!.right); 65 + effectiveMargin.bottom,
  66 + effectiveMargin.left,
  67 + effectiveMargin.top,
  68 + effectiveMargin.right,
  69 + );
67 } else { 70 } else {
68 return _margin; 71 return _margin;
69 } 72 }
70 } 73 }
71 74
72 if (mustRotate) { 75 if (mustRotate) {
73 - return EdgeInsets.fromLTRB(pageFormat.marginBottom, pageFormat.marginLeft,  
74 - pageFormat.marginTop, pageFormat.marginRight); 76 + return EdgeInsets.fromLTRB(
  77 + pageFormat.marginBottom, pageFormat.marginLeft, pageFormat.marginTop, pageFormat.marginRight);
75 } else { 78 } else {
76 - return EdgeInsets.fromLTRB(pageFormat.marginLeft, pageFormat.marginTop,  
77 - pageFormat.marginRight, pageFormat.marginBottom); 79 + return EdgeInsets.fromLTRB(
  80 + pageFormat.marginLeft, pageFormat.marginTop, pageFormat.marginRight, pageFormat.marginBottom);
78 } 81 }
79 } 82 }
80 83
@@ -40,11 +40,7 @@ final _yellowBox = Container( @@ -40,11 +40,7 @@ final _yellowBox = Container(
40 color: PdfColors.yellow, 40 color: PdfColors.yellow,
41 ); 41 );
42 42
43 -final _greenBox = Container(  
44 - width: 50,  
45 - height: 50,  
46 - color: PdfColors.green,  
47 -); 43 +
48 void main() { 44 void main() {
49 setUpAll(() { 45 setUpAll(() {
50 Document.debug = true; 46 Document.debug = true;
@@ -143,10 +139,9 @@ void main() { @@ -143,10 +139,9 @@ void main() {
143 build: (Context context) => SizedBox( 139 build: (Context context) => SizedBox(
144 width: 150, 140 width: 150,
145 height: 150, 141 height: 150,
146 - child: Wrap(  
147 - children: [_blueBox, _redBox,_yellowBox],  
148 - )  
149 - ), 142 + child: Wrap(
  143 + children: [_blueBox, _redBox, _yellowBox],
  144 + )),
150 ), 145 ),
151 ); 146 );
152 }); 147 });
@@ -160,9 +155,8 @@ void main() { @@ -160,9 +155,8 @@ void main() {
160 width: 150, 155 width: 150,
161 height: 150, 156 height: 150,
162 child: Wrap( 157 child: Wrap(
163 - children: [_blueBox, _redBox,_yellowBox],  
164 - )  
165 - ), 158 + children: [_blueBox, _redBox, _yellowBox],
  159 + )),
166 ), 160 ),
167 ); 161 );
168 }); 162 });
@@ -178,9 +172,8 @@ void main() { @@ -178,9 +172,8 @@ void main() {
178 spacing: 10, 172 spacing: 10,
179 runSpacing: 10, 173 runSpacing: 10,
180 runAlignment: WrapAlignment.center, 174 runAlignment: WrapAlignment.center,
181 - children: [_blueBox, _redBox,_yellowBox],  
182 - )  
183 - ), 175 + children: [_blueBox, _redBox, _yellowBox],
  176 + )),
184 ), 177 ),
185 ); 178 );
186 }); 179 });
@@ -197,9 +190,96 @@ void main() { @@ -197,9 +190,96 @@ void main() {
197 spacing: 10, 190 spacing: 10,
198 runSpacing: 10, 191 runSpacing: 10,
199 runAlignment: WrapAlignment.end, 192 runAlignment: WrapAlignment.end,
200 - children: [_blueBox, _redBox,_yellowBox],  
201 - )  
202 - ), 193 + children: [_blueBox, _redBox, _yellowBox],
  194 + )),
  195 + ),
  196 + );
  197 + });
  198 +
  199 + test('RTL Page Should render child aligned right', () {
  200 + pdf.addPage(
  201 + Page(
  202 + textDirection: TextDirection.rtl,
  203 + pageFormat: const PdfPageFormat(150, 150),
  204 + build: (Context context) {
  205 + return _blueBox;
  206 + },
  207 + ),
  208 + );
  209 + });
  210 +
  211 + test('LTR Page Should render child aligned left', () {
  212 + pdf.addPage(
  213 + Page(
  214 + textDirection: TextDirection.ltr,
  215 + pageFormat: const PdfPageFormat(150, 150),
  216 + build: (Context context) {
  217 + return _blueBox;
  218 + },
  219 + ),
  220 + );
  221 + });
  222 +
  223 + test('RTL Multi Page Should render child aligned right', () {
  224 + pdf.addPage(
  225 + MultiPage(
  226 + textDirection: TextDirection.rtl,
  227 + pageFormat: const PdfPageFormat(150, 150),
  228 + build: (Context context) {
  229 + return [
  230 + ListView(children: [
  231 + for(int i = 0; i < 30; i++)
  232 + Text('Hello World')
  233 + ]),
  234 + ];
  235 + },
  236 + ),
  237 + );
  238 + });
  239 +
  240 + test('LTR Multi Page Should render child aligned left', () {
  241 + pdf.addPage(
  242 + MultiPage(
  243 + textDirection: TextDirection.ltr,
  244 + pageFormat: const PdfPageFormat(150, 150),
  245 + build: (Context context) {
  246 + return [
  247 + ListView(children: [
  248 + for(int i = 0; i < 30; i++)
  249 + Text('Hello World')
  250 + ]),
  251 + ];
  252 + },
  253 + ),
  254 + );
  255 + });
  256 +
  257 + test('Should render a blue box padded from right', () {
  258 + pdf.addPage(
  259 + Page(
  260 + textDirection: TextDirection.rtl,
  261 + pageFormat: const PdfPageFormat(150, 150),
  262 + build: (Context context) {
  263 + return Padding(
  264 + padding: const EdgeInsetsDirectional.only(start: 20),
  265 + child: _blueBox,
  266 + );
  267 + },
  268 + ),
  269 + );
  270 + });
  271 +
  272 + test('Should render a blue box padded from left', () {
  273 + pdf.addPage(
  274 + Page(
  275 + textDirection: TextDirection.ltr,
  276 + pageFormat: const PdfPageFormat(150, 150),
  277 + build: (Context context) {
  278 + return Padding(
  279 + padding: const EdgeInsetsDirectional.only(start: 20),
  280 + child: _blueBox,
  281 + );
  282 + },
203 ), 283 ),
204 ); 284 );
205 }); 285 });