diff --git a/pdf/lib/src/widgets/basic.dart b/pdf/lib/src/widgets/basic.dart index 53c5a21e..93946fbf 100644 --- a/pdf/lib/src/widgets/basic.dart +++ b/pdf/lib/src/widgets/basic.dart @@ -39,22 +39,16 @@ class LimitedBox extends SingleChildWidget { BoxConstraints _limitConstraints(BoxConstraints constraints) { return BoxConstraints( minWidth: constraints.minWidth, - maxWidth: constraints.hasBoundedWidth - ? constraints.maxWidth - : constraints.constrainWidth(maxWidth), + maxWidth: constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(maxWidth), minHeight: constraints.minHeight, - maxHeight: constraints.hasBoundedHeight - ? constraints.maxHeight - : constraints.constrainHeight(maxHeight)); + maxHeight: constraints.hasBoundedHeight ? constraints.maxHeight : constraints.constrainHeight(maxHeight)); } @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { PdfPoint size; if (child != null) { - child!.layout(context, _limitConstraints(constraints), - parentUsesSize: true); + child!.layout(context, _limitConstraints(constraints), parentUsesSize: true); assert(child!.box != null); size = constraints.constrain(child!.box!.size); } else { @@ -79,9 +73,8 @@ class Padding extends SingleChildWidget { final EdgeInsetsGeometry padding; @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { - final effectivePadding = padding.resolve(Directionality.of(context)); + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { + final effectivePadding = padding.resolve(Directionality.of(context)); if (child != null) { final childConstraints = constraints.deflate(effectivePadding); child!.layout(context, childConstraints, parentUsesSize: parentUsesSize); @@ -90,8 +83,7 @@ class Padding extends SingleChildWidget { width: child!.box!.width + effectivePadding.horizontal, height: child!.box!.height + effectivePadding.vertical); } else { - box = constraints.constrainRect( - width: effectivePadding.horizontal, height: effectivePadding.vertical); + box = constraints.constrainRect(width: effectivePadding.horizontal, height: effectivePadding.vertical); } } @@ -114,10 +106,10 @@ class Padding extends SingleChildWidget { @override void paint(Context context) { super.paint(context); - final effectivePadding = padding.resolve(Directionality.of(context)); + final resolvedPadding = padding.resolve(Directionality.of(context)); if (child != null) { final mat = Matrix4.identity(); - mat.translate(box!.x + effectivePadding.left, box!.y + effectivePadding.bottom); + mat.translate(box!.x + resolvedPadding.left, box!.y + resolvedPadding.bottom); context.canvas ..saveContext() ..setTransform(mat); @@ -190,13 +182,13 @@ class Transform extends SingleChildWidget { final PdfPoint? origin; /// The alignment of the origin, relative to the size of the box. - final Alignment? alignment; + final AlignmentGeometry? alignment; final bool adjustLayout; final bool unconstrained; - Matrix4 get _effectiveTransform { + Matrix4 _effectiveTransform(Context context) { final result = Matrix4.identity(); if (origin != null) { result.translate(origin!.x, origin!.y); @@ -204,7 +196,8 @@ class Transform extends SingleChildWidget { result.translate(box!.x, box!.y); late PdfPoint translation; if (alignment != null) { - translation = alignment!.alongSize(box!.size); + final resolvedAlignment = alignment!.resolve(Directionality.of(context)); + translation = resolvedAlignment.alongSize(box!.size); result.translate(translation.x, translation.y); } result.multiply(transform); @@ -218,8 +211,7 @@ class Transform extends SingleChildWidget { } @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { if (!adjustLayout) { return super.layout(context, constraints, parentUsesSize: parentUsesSize); } @@ -248,20 +240,14 @@ class Transform extends SingleChildWidget { 0, ]); - final dx = -math.min( - math.min(math.min(values[0], values[3]), values[6]), values[9]); - final dy = -math.min( - math.min(math.min(values[1], values[4]), values[7]), values[10]); + final dx = -math.min(math.min(math.min(values[0], values[3]), values[6]), values[9]); + final dy = -math.min(math.min(math.min(values[1], values[4]), values[7]), values[10]); box = PdfRect.fromLTRB( 0, 0, - math.max(math.max(math.max(values[0], values[3]), values[6]), - values[9]) + - dx, - math.max(math.max(math.max(values[1], values[4]), values[7]), - values[10]) + - dy, + math.max(math.max(math.max(values[0], values[3]), values[6]), values[9]) + dx, + math.max(math.max(math.max(values[1], values[4]), values[7]), values[10]) + dy, ); transform.leftTranslate(dx, dy); @@ -275,7 +261,7 @@ class Transform extends SingleChildWidget { super.paint(context); if (child != null) { - final mat = _effectiveTransform; + final mat = _effectiveTransform(context); context.canvas ..saveContext() ..setTransform(mat); @@ -288,11 +274,7 @@ class Transform extends SingleChildWidget { /// A widget that aligns its child within itself and optionally sizes itself /// based on the child's size. class Align extends SingleChildWidget { - Align( - {this.alignment = Alignment.center, - this.widthFactor, - this.heightFactor, - Widget? child}) + Align({this.alignment = Alignment.center, this.widthFactor, this.heightFactor, Widget? child}) : assert(widthFactor == null || widthFactor >= 0.0), assert(heightFactor == null || heightFactor >= 0.0), super(child: child); @@ -307,30 +289,22 @@ class Align extends SingleChildWidget { final double? heightFactor; @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { - final shrinkWrapWidth = - widthFactor != null || constraints.maxWidth == double.infinity; - final shrinkWrapHeight = - heightFactor != null || constraints.maxHeight == double.infinity; + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { + final shrinkWrapWidth = widthFactor != null || constraints.maxWidth == double.infinity; + final shrinkWrapHeight = heightFactor != null || constraints.maxHeight == double.infinity; if (child != null) { child!.layout(context, constraints.loosen(), parentUsesSize: true); assert(child!.box != null); box = constraints.constrainRect( - width: shrinkWrapWidth - ? child!.box!.width * (widthFactor ?? 1.0) - : double.infinity, - height: shrinkWrapHeight - ? child!.box!.height * (heightFactor ?? 1.0) - : double.infinity); + width: shrinkWrapWidth ? child!.box!.width * (widthFactor ?? 1.0) : double.infinity, + height: shrinkWrapHeight ? child!.box!.height * (heightFactor ?? 1.0) : double.infinity); final resolvedAlignment = alignment.resolve(Directionality.of(context)); child!.box = resolvedAlignment.inscribe(child!.box!.size, box!); } else { box = constraints.constrainRect( - width: shrinkWrapWidth ? 0.0 : double.infinity, - height: shrinkWrapHeight ? 0.0 : double.infinity); + width: shrinkWrapWidth ? 0.0 : double.infinity, height: shrinkWrapHeight ? 0.0 : double.infinity); } } @@ -353,58 +327,40 @@ class Align extends SingleChildWidget { box!.left + child!.box!.horizontalCenter, box!.bottom, ) - ..lineTo(box!.left + child!.box!.horizontalCenter, - box!.bottom + child!.box!.bottom) - ..lineTo(box!.left + child!.box!.horizontalCenter - headSize, - box!.bottom + child!.box!.bottom - headSize) - ..moveTo(box!.left + child!.box!.horizontalCenter, - box!.bottom + child!.box!.bottom) - ..lineTo(box!.left + child!.box!.horizontalCenter + headSize, - box!.bottom + child!.box!.bottom - headSize); + ..lineTo(box!.left + child!.box!.horizontalCenter, box!.bottom + child!.box!.bottom) + ..lineTo(box!.left + child!.box!.horizontalCenter - headSize, box!.bottom + child!.box!.bottom - headSize) + ..moveTo(box!.left + child!.box!.horizontalCenter, box!.bottom + child!.box!.bottom) + ..lineTo(box!.left + child!.box!.horizontalCenter + headSize, box!.bottom + child!.box!.bottom - headSize); } if (box!.bottom + child!.box!.top < box!.top) { - final headSize = - math.min((box!.top - child!.box!.top - box!.bottom) * 0.2, 10); + final headSize = math.min((box!.top - child!.box!.top - box!.bottom) * 0.2, 10); context.canvas ..moveTo(box!.left + child!.box!.horizontalCenter, box!.top) - ..lineTo(box!.left + child!.box!.horizontalCenter, - box!.bottom + child!.box!.top) - ..lineTo(box!.left + child!.box!.horizontalCenter - headSize, - box!.bottom + child!.box!.top + headSize) - ..moveTo(box!.left + child!.box!.horizontalCenter, - box!.bottom + child!.box!.top) - ..lineTo(box!.left + child!.box!.horizontalCenter + headSize, - box!.bottom + child!.box!.top + headSize); + ..lineTo(box!.left + child!.box!.horizontalCenter, box!.bottom + child!.box!.top) + ..lineTo(box!.left + child!.box!.horizontalCenter - headSize, box!.bottom + child!.box!.top + headSize) + ..moveTo(box!.left + child!.box!.horizontalCenter, box!.bottom + child!.box!.top) + ..lineTo(box!.left + child!.box!.horizontalCenter + headSize, box!.bottom + child!.box!.top + headSize); } if (child!.box!.left > 0) { final headSize = math.min(child!.box!.left * 0.2, 10); context.canvas ..moveTo(box!.left, box!.bottom + child!.box!.verticalCenter) - ..lineTo(box!.left + child!.box!.left, - box!.bottom + child!.box!.verticalCenter) - ..lineTo(box!.left + child!.box!.left - headSize, - box!.bottom + child!.box!.verticalCenter - headSize) - ..moveTo(box!.left + child!.box!.left, - box!.bottom + child!.box!.verticalCenter) - ..lineTo(box!.left + child!.box!.left - headSize, - box!.bottom + child!.box!.verticalCenter + headSize); + ..lineTo(box!.left + child!.box!.left, box!.bottom + child!.box!.verticalCenter) + ..lineTo(box!.left + child!.box!.left - headSize, box!.bottom + child!.box!.verticalCenter - headSize) + ..moveTo(box!.left + child!.box!.left, box!.bottom + child!.box!.verticalCenter) + ..lineTo(box!.left + child!.box!.left - headSize, box!.bottom + child!.box!.verticalCenter + headSize); } if (box!.left + child!.box!.right < box!.right) { - final headSize = - math.min((box!.right - child!.box!.right - box!.left) * 0.2, 10); + final headSize = math.min((box!.right - child!.box!.right - box!.left) * 0.2, 10); context.canvas ..moveTo(box!.right, box!.bottom + child!.box!.verticalCenter) - ..lineTo(box!.left + child!.box!.right, - box!.bottom + child!.box!.verticalCenter) - ..lineTo(box!.left + child!.box!.right + headSize, - box!.bottom + child!.box!.verticalCenter - headSize) - ..moveTo(box!.left + child!.box!.right, - box!.bottom + child!.box!.verticalCenter) - ..lineTo(box!.left + child!.box!.right + headSize, - box!.bottom + child!.box!.verticalCenter + headSize); + ..lineTo(box!.left + child!.box!.right, box!.bottom + child!.box!.verticalCenter) + ..lineTo(box!.left + child!.box!.right + headSize, box!.bottom + child!.box!.verticalCenter - headSize) + ..moveTo(box!.left + child!.box!.right, box!.bottom + child!.box!.verticalCenter) + ..lineTo(box!.left + child!.box!.right + headSize, box!.bottom + child!.box!.verticalCenter + headSize); } context.canvas.strokePath(); @@ -419,23 +375,19 @@ class Align extends SingleChildWidget { /// A widget that imposes additional constraints on its child. class ConstrainedBox extends SingleChildWidget { - ConstrainedBox({required this.constraints, Widget? child}) - : super(child: child); + ConstrainedBox({required this.constraints, Widget? child}) : super(child: child); /// The additional constraints to impose on the child. final BoxConstraints constraints; @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { if (child != null) { - child!.layout(context, this.constraints.enforce(constraints), - parentUsesSize: true); + child!.layout(context, this.constraints.enforce(constraints), parentUsesSize: true); assert(child!.box != null); box = child!.box; } else { - box = PdfRect.fromPoints( - PdfPoint.zero, this.constraints.enforce(constraints).smallest); + box = PdfRect.fromPoints(PdfPoint.zero, this.constraints.enforce(constraints).smallest); } } @@ -448,8 +400,7 @@ class ConstrainedBox extends SingleChildWidget { class Center extends Align { Center({double? widthFactor, double? heightFactor, Widget? child}) - : super( - widthFactor: widthFactor, heightFactor: heightFactor, child: child); + : super(widthFactor: widthFactor, heightFactor: heightFactor, child: child); } /// Scales and positions its child within itself according to [fit]. @@ -464,17 +415,15 @@ class FittedBox extends SingleChildWidget { final BoxFit fit; /// How to align the child within its parent's bounds. - final Alignment alignment; + final AlignmentGeometry alignment; @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { PdfPoint size; if (child != null) { child!.layout(context, const BoxConstraints(), parentUsesSize: true); assert(child!.box != null); - size = constraints - .constrainSizeAndAttemptToPreserveAspectRatio(child!.box!.size); + size = constraints.constrainSizeAndAttemptToPreserveAspectRatio(child!.box!.size); } else { size = constraints.smallest; } @@ -486,18 +435,17 @@ class FittedBox extends SingleChildWidget { super.paint(context); if (child != null) { + final resolvedAlignment = alignment.resolve(Directionality.of(context)); final childSize = child!.box!.size; final sizes = applyBoxFit(fit, childSize, box!.size); final scaleX = sizes.destination!.x / sizes.source!.x; final scaleY = sizes.destination!.y / sizes.source!.y; - final sourceRect = alignment.inscribe( - sizes.source!, PdfRect.fromPoints(PdfPoint.zero, childSize)); - final destinationRect = alignment.inscribe(sizes.destination!, box!); + final sourceRect = resolvedAlignment.inscribe(sizes.source!, PdfRect.fromPoints(PdfPoint.zero, childSize)); + final destinationRect = resolvedAlignment.inscribe(sizes.destination!, box!); - final mat = - Matrix4.translationValues(destinationRect.x, destinationRect.y, 0) - ..scale(scaleX, scaleY, 1) - ..translate(-sourceRect.x, -sourceRect.y); + final mat = Matrix4.translationValues(destinationRect.x, destinationRect.y, 0) + ..scale(scaleX, scaleY, 1) + ..translate(-sourceRect.x, -sourceRect.y); context.canvas ..saveContext() @@ -555,12 +503,10 @@ class AspectRatio extends SingleChildWidget { } @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { box = PdfRect.fromPoints(PdfPoint.zero, _applyAspectRatio(constraints)); if (child != null) { - child!.layout(context, - BoxConstraints.tightFor(width: box!.width, height: box!.height)); + child!.layout(context, BoxConstraints.tightFor(width: box!.width, height: box!.height)); } assert(child!.box != null); } @@ -587,8 +533,7 @@ class CustomPaint extends SingleChildWidget { final PdfPoint size; @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { if (child != null) { child!.layout(context, constraints, parentUsesSize: parentUsesSize); assert(child!.box != null); @@ -655,9 +600,7 @@ class SizedBox extends StatelessWidget { @override Widget build(Context context) { - return ConstrainedBox( - child: child, - constraints: BoxConstraints.tightFor(width: width, height: height)); + return ConstrainedBox(child: child, constraints: BoxConstraints.tightFor(width: width, height: height)); } } @@ -680,8 +623,7 @@ class Builder extends StatelessWidget { } /// The signature of the [LayoutBuilder] builder function. -typedef LayoutWidgetBuilder = Widget Function( - Context context, BoxConstraints? constraints); +typedef LayoutWidgetBuilder = Widget Function(Context context, BoxConstraints? constraints); /// Builds a widget tree that can depend on the parent widget's size. class LayoutBuilder extends StatelessWidget { @@ -696,8 +638,7 @@ class LayoutBuilder extends StatelessWidget { BoxConstraints? _constraints; @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { _constraints = constraints; super.layout(context, constraints); } @@ -744,8 +685,7 @@ class FullPage extends SingleChildWidget { } @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { final constraints = _getConstraints(context); if (child != null) { @@ -937,7 +877,7 @@ class OverflowBox extends SingleChildWidget { }) : super(child: child); /// How to align the child. - final Alignment alignment; + final AlignmentGeometry alignment; /// The minimum width constraint to give the child. Set this to null (the /// default) to use the constraint from the parent instead. @@ -965,15 +905,14 @@ class OverflowBox extends SingleChildWidget { } @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { box = PdfRect.fromPoints(PdfPoint.zero, constraints.smallest); if (child != null) { - child!.layout(context, _getInnerConstraints(constraints), - parentUsesSize: true); + child!.layout(context, _getInnerConstraints(constraints), parentUsesSize: true); assert(child!.box != null); - child!.box = alignment.inscribe(child!.box!.size, box!); + final resolvedAlignment = alignment.resolve(Directionality.of(context)); + child!.box = resolvedAlignment.inscribe(child!.box!.size, box!); } } diff --git a/pdf/lib/src/widgets/content.dart b/pdf/lib/src/widgets/content.dart index 40562b79..24878f17 100644 --- a/pdf/lib/src/widgets/content.dart +++ b/pdf/lib/src/widgets/content.dart @@ -330,9 +330,9 @@ class Footer extends StatelessWidget { final Widget? trailing; - final EdgeInsets? margin; + final EdgeInsetsGeometry? margin; - final EdgeInsets? padding; + final EdgeInsetsGeometry? padding; final BoxDecoration? decoration; diff --git a/pdf/lib/src/widgets/decoration.dart b/pdf/lib/src/widgets/decoration.dart index ac4cc456..fe10c001 100644 --- a/pdf/lib/src/widgets/decoration.dart +++ b/pdf/lib/src/widgets/decoration.dart @@ -113,10 +113,10 @@ class LinearGradient extends Gradient { }) : super(colors: colors, stops: stops); /// The offset at which stop 0.0 of the gradient is placed. - final Alignment begin; + final AlignmentGeometry begin; /// The offset at which stop 1.0 of the gradient is placed. - final Alignment end; + final AlignmentGeometry end; /// How this gradient should tile the plane beyond in the region before final TileMode tileMode; @@ -134,7 +134,7 @@ class LinearGradient extends Gradient { } assert(stops == null || stops!.length == colors.length); - + final textDirection = Directionality.of(context); context.canvas ..saveContext() ..clipPath() @@ -148,8 +148,8 @@ class LinearGradient extends Gradient { colors, stops, ), - start: begin.withinRect(box), - end: end.withinRect(box), + start: begin.resolve(textDirection).withinRect(box), + end: end.resolve(textDirection).withinRect(box), extendStart: true, extendEnd: true, ), @@ -175,7 +175,7 @@ class RadialGradient extends Gradient { }) : super(colors: colors, stops: stops); /// The center of the gradient - final Alignment center; + final AlignmentGeometry center; /// The radius of the gradient final double radius; @@ -185,7 +185,7 @@ class RadialGradient extends Gradient { final TileMode tileMode; /// The focal point of the gradient. - final Alignment? focal; + final AlignmentGeometry? focal; /// The radius of the focal point of the gradient. final double focalRadius; @@ -207,7 +207,7 @@ class RadialGradient extends Gradient { final _focal = focal ?? center; final _radius = math.min(box.width, box.height); - + final textDirection = Directionality.of(context); context.canvas ..saveContext() ..clipPath() @@ -221,8 +221,8 @@ class RadialGradient extends Gradient { colors, stops, ), - start: _focal.withinRect(box), - end: center.withinRect(box), + start: _focal.resolve(textDirection).withinRect(box), + end: center.resolve(textDirection).withinRect(box), radius0: focalRadius * _radius, radius1: radius * _radius, extendStart: true, diff --git a/pdf/lib/src/widgets/forms.dart b/pdf/lib/src/widgets/forms.dart index 5ad83c47..46150414 100644 --- a/pdf/lib/src/widgets/forms.dart +++ b/pdf/lib/src/widgets/forms.dart @@ -227,7 +227,7 @@ class FlatButton extends SingleChildWidget with AnnotationAppearance { PdfColor color = PdfColors.blue, PdfColor colorDown = PdfColors.red, PdfColor colorRollover = PdfColors.blueAccent, - EdgeInsets? padding, + EdgeInsetsGeometry? padding, BoxDecoration? decoration, this.flags, required Widget child, diff --git a/pdf/lib/src/widgets/page_theme.dart b/pdf/lib/src/widgets/page_theme.dart index 2ac9417e..a7e06a85 100644 --- a/pdf/lib/src/widgets/page_theme.dart +++ b/pdf/lib/src/widgets/page_theme.dart @@ -59,13 +59,13 @@ class PageTheme { EdgeInsetsGeometry? get margin { if (_margin != null) { - final effectiveMargin = _margin!.resolve(textDirection); + final resolvedMargin = _margin!.resolve(textDirection); if (mustRotate) { return EdgeInsets.fromLTRB( - effectiveMargin.bottom, - effectiveMargin.left, - effectiveMargin.top, - effectiveMargin.right, + resolvedMargin.bottom, + resolvedMargin.left, + resolvedMargin.top, + resolvedMargin.right, ); } else { return _margin; diff --git a/pdf/lib/src/widgets/table_helper.dart b/pdf/lib/src/widgets/table_helper.dart index 63bb18e0..72438fc4 100644 --- a/pdf/lib/src/widgets/table_helper.dart +++ b/pdf/lib/src/widgets/table_helper.dart @@ -40,8 +40,8 @@ mixin TableHelper { required List> data, EdgeInsetsGeometry cellPadding = const EdgeInsets.all(5), double cellHeight = 0, - Alignment cellAlignment = Alignment.topLeft, - Map? cellAlignments, + AlignmentGeometry cellAlignment = Alignment.topLeft, + Map? cellAlignments, TextStyle? cellStyle, TextStyle? oddCellStyle, OnCellFormat? cellFormat, @@ -50,8 +50,8 @@ mixin TableHelper { List? headers, EdgeInsetsGeometry? headerPadding, double? headerHeight, - Alignment headerAlignment = Alignment.center, - Map? headerAlignments, + AlignmentGeometry headerAlignment = Alignment.center, + Map? headerAlignments, TextStyle? headerStyle, OnCellFormat? headerFormat, TableBorder? border = const TableBorder( @@ -120,14 +120,14 @@ mixin TableHelper { rowNum++; } + final textDirection = context == null ? TextDirection.ltr : Directionality.of(context); for (final row in data) { final tableRow = []; final isOdd = (rowNum - headerCount) % 2 != 0; - if (rowNum < headerCount) { for (final dynamic cell in row) { final align = headerAlignments[tableRow.length] ?? headerAlignment; - final textAlign = _textAlign(align); + final textAlign = _textAlign(align.resolve(textDirection)); tableRow.add( Container( @@ -165,7 +165,7 @@ mixin TableHelper { ? cell.toString() : cellFormat(tableRow.length, cell), style: isOdd ? oddCellStyle : cellStyle, - textAlign: _textAlign(align), + textAlign: _textAlign(align.resolve(textDirection)), textDirection: tableDirection, ), ), diff --git a/pdf/lib/src/widgets/text.dart b/pdf/lib/src/widgets/text.dart index 0a564ee7..9b3962d6 100644 --- a/pdf/lib/src/widgets/text.dart +++ b/pdf/lib/src/widgets/text.dart @@ -32,7 +32,7 @@ import 'text_style.dart'; import 'theme.dart'; import 'widget.dart'; -enum TextAlign { left, right, center, justify } +enum TextAlign { left, right, start, end, center, justify } enum TextDirection { ltr, rtl } @@ -83,8 +83,7 @@ abstract class _Span { } class _TextDecoration { - _TextDecoration(this.style, this.annotation, this.startSpan, this.endSpan) - : assert(startSpan <= endSpan); + _TextDecoration(this.style, this.annotation, this.startSpan, this.endSpan) : assert(startSpan <= endSpan); static const double _space = -0.15; @@ -104,8 +103,7 @@ class _TextDecoration { } final x1 = spans[startSpan].offset.x + spans[startSpan].left; - final x2 = - spans[endSpan].offset.x + spans[endSpan].left + spans[endSpan].width; + final x2 = spans[endSpan].offset.x + spans[endSpan].left + spans[endSpan].width; var y1 = spans[startSpan].offset.y + spans[startSpan].top; var y2 = y1 + spans[startSpan].height; @@ -120,8 +118,7 @@ class _TextDecoration { return _box; } - _TextDecoration copyWith({int? endSpan}) => - _TextDecoration(style, annotation, startSpan, endSpan ?? this.endSpan); + _TextDecoration copyWith({int? endSpan}) => _TextDecoration(style, annotation, startSpan, endSpan ?? this.endSpan); void backgroundPaint( Context context, @@ -166,15 +163,11 @@ class _TextDecoration { final box = _getBox(spans); final font = style.font!.getFont(context); - final space = - _space * style.fontSize! * textScaleFactor * style.decorationThickness!; + final space = _space * style.fontSize! * textScaleFactor * style.decorationThickness!; context.canvas ..setStrokeColor(style.decorationColor ?? style.color) - ..setLineWidth(style.decorationThickness! * - style.fontSize! * - textScaleFactor * - 0.05); + ..setLineWidth(style.decorationThickness! * style.fontSize! * textScaleFactor * 0.05); if (style.decoration!.contains(TextDecoration.underline)) { final base = -font.descent * style.fontSize! * textScaleFactor / 2; @@ -247,8 +240,7 @@ class _TextDecoration { context.canvas ..setLineWidth(.5) - ..drawRect( - globalBox.x + box.x, globalBox.top + box.y, box.width, box.height) + ..drawRect(globalBox.x + box.x, globalBox.top + box.y, box.width, box.height) ..setStrokeColor(PdfColors.yellow) ..strokePath(); } @@ -310,14 +302,11 @@ class _Word extends _Span { context.canvas ..setLineWidth(.5) - ..drawRect(globalBox!.x + offset.x + metrics.left, - globalBox.top + offset.y + metrics.top, metrics.width, metrics.height) + ..drawRect( + globalBox!.x + offset.x + metrics.left, globalBox.top + offset.y + metrics.top, metrics.width, metrics.height) ..setStrokeColor(PdfColors.orange) ..strokePath() - ..drawLine( - globalBox.x + offset.x - deb, - globalBox.top + offset.y, - globalBox.x + offset.x + metrics.right + deb, + ..drawLine(globalBox.x + offset.x - deb, globalBox.top + offset.y, globalBox.x + offset.x + metrics.right + deb, globalBox.top + offset.y) ..setStrokeColor(PdfColors.deepPurple) ..strokePath(); @@ -363,10 +352,8 @@ class _WidgetSpan extends _Span { double textScaleFactor, PdfPoint point, ) { - widget.box = PdfRect.fromPoints( - PdfPoint( - point.x + widget.box!.offset.x, point.y + widget.box!.offset.y), - widget.box!.size); + widget.box = + PdfRect.fromPoints(PdfPoint(point.x + widget.box!.offset.x, point.y + widget.box!.offset.y), widget.box!.size); widget.paint(context); } @@ -380,8 +367,7 @@ class _WidgetSpan extends _Span { context.canvas ..setLineWidth(.5) - ..drawRect( - globalBox!.x + offset.x, globalBox.top + offset.y, width, height) + ..drawRect(globalBox!.x + offset.x, globalBox.top + offset.y, width, height) ..setStrokeColor(PdfColors.orange) ..strokePath() ..drawLine( @@ -564,14 +550,11 @@ class _Line { double get height { final list = parent._spans.sublist(firstSpan, lastSpan); - return list.isEmpty - ? 0 - : list.reduce((a, b) => a.height > b.height ? a : b).height; + return list.isEmpty ? 0 : list.reduce((a, b) => a.height > b.height ? a : b).height; } @override - String toString() => - '$runtimeType $firstSpan-$lastSpan baseline: $baseline width:$wordsWidth'; + String toString() => '$runtimeType $firstSpan-$lastSpan baseline: $baseline width:$wordsWidth'; void realign(double totalWidth) { final spans = parent._spans.sublist(firstSpan, lastSpan); @@ -580,38 +563,43 @@ class _Line { var delta = 0.0; switch (textAlign) { case TextAlign.left: - delta = isRTL ? totalWidth - wordsWidth : 0; + delta = isRTL ? wordsWidth : 0; break; case TextAlign.right: - delta = isRTL ? 0 : totalWidth - wordsWidth; + delta = totalWidth - wordsWidth; + break; + case TextAlign.start: + delta = isRTL ? totalWidth : 0; + break; + case TextAlign.end: + delta = isRTL ? wordsWidth : totalWidth - wordsWidth; break; case TextAlign.center: delta = (totalWidth - wordsWidth) / 2.0; break; case TextAlign.justify: + delta = isRTL ? totalWidth : 0; if (!justify) { break; } - delta = (totalWidth - wordsWidth) / (spans.length - 1); + final gap = (totalWidth - wordsWidth) / (spans.length - 1); var x = 0.0; for (final span in spans) { - if (isRTL) { - final xOffset = span.offset.x + span.width; - span.offset = - PdfPoint((totalWidth - xOffset) - x, span.offset.y - baseline); - } else { - span.offset = span.offset.translate(x, -baseline); - } - x += delta; + span.offset = PdfPoint( + isRTL ? delta - x - (span.offset.x + span.width) : span.offset.x + x, + span.offset.y - baseline, + ); + x += gap; } + return; } if (isRTL) { for (final span in spans) { span.offset = PdfPoint( - totalWidth - (span.offset.x + span.width) - delta, + delta - (span.offset.x + span.width), span.offset.y - baseline, ); } @@ -621,8 +609,6 @@ class _Line { for (final span in spans) { span.offset = span.offset.translate(delta, -baseline); } - - return; } } @@ -646,8 +632,7 @@ class RichTextContext extends WidgetContext { } @override - String toString() => - '$runtimeType Offset: $startOffset -> $endOffset Span: $spanStart -> $spanEnd'; + String toString() => '$runtimeType Offset: $startOffset -> $endOffset Span: $spanStart -> $spanEnd'; } class RichText extends Widget with SpanningWidget { @@ -696,8 +681,7 @@ class RichText extends Widget with SpanningWidget { if (append && _decorations.isNotEmpty) { final last = _decorations.last; if (last.style == td.style && last.annotation == td.annotation) { - _decorations[_decorations.length - 1] = - last.copyWith(endSpan: td.endSpan); + _decorations[_decorations.length - 1] = last.copyWith(endSpan: td.endSpan); return; } } @@ -867,8 +851,7 @@ class RichText extends Widget with SpanningWidget { } @override - void layout(Context context, BoxConstraints constraints, - {bool parentUsesSize = false}) { + void layout(Context context, BoxConstraints constraints, {bool parentUsesSize = false}) { _spans.clear(); _decorations.clear(); @@ -876,20 +859,12 @@ class RichText extends Widget with SpanningWidget { final _softWrap = softWrap ?? theme.softWrap; final _maxLines = maxLines ?? theme.maxLines; final _textDirection = textDirection ?? Directionality.of(context); - _textAlign = textAlign ?? - theme.textAlign ?? - (_textDirection == TextDirection.rtl - ? TextAlign.right - : TextAlign.left); + _textAlign = textAlign ?? theme.textAlign ?? TextAlign.start; final _overflow = this.overflow ?? theme.overflow; - final constraintWidth = constraints.hasBoundedWidth - ? constraints.maxWidth - : constraints.constrainWidth(); - final constraintHeight = constraints.hasBoundedHeight - ? constraints.maxHeight - : constraints.constrainHeight(); + final constraintWidth = constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(); + final constraintHeight = constraints.hasBoundedHeight ? constraints.maxHeight : constraints.constrainHeight(); var offsetX = 0.0; var offsetY = _context.startOffset; @@ -916,13 +891,10 @@ class RichText extends Widget with SpanningWidget { final font = style!.font!.getFont(context); - final space = - font.stringMetrics(' ') * (style.fontSize! * textScaleFactor); + final space = font.stringMetrics(' ') * (style.fontSize! * textScaleFactor); - final spanLines = (_textDirection == TextDirection.rtl - ? bidi.logicalToVisual(span.text!) - : span.text)! - .split('\n'); + final spanLines = + (_textDirection == TextDirection.rtl ? bidi.logicalToVisual(span.text!) : span.text)!.split('\n'); for (var line = 0; line < spanLines.length; line++) { final words = spanLines[line].split(RegExp(r'\s')); @@ -930,18 +902,15 @@ class RichText extends Widget with SpanningWidget { final word = words[index]; if (word.isEmpty) { - offsetX += space.advanceWidth * style.wordSpacing! + - style.letterSpacing!; + offsetX += space.advanceWidth * style.wordSpacing! + style.letterSpacing!; continue; } - final metrics = font.stringMetrics(word, - letterSpacing: style.letterSpacing! / - (style.fontSize! * textScaleFactor)) * - (style.fontSize! * textScaleFactor); + final metrics = + font.stringMetrics(word, letterSpacing: style.letterSpacing! / (style.fontSize! * textScaleFactor)) * + (style.fontSize! * textScaleFactor); - if (_softWrap && - offsetX + metrics.width > constraintWidth + 0.00001) { + if (_softWrap && offsetX + metrics.width > constraintWidth + 0.00001) { if (spanCount > 0 && metrics.width <= constraintWidth) { overflow = true; lines.add(_Line( @@ -949,9 +918,7 @@ class RichText extends Widget with SpanningWidget { spanStart, spanCount, bottom, - offsetX - - space.advanceWidth * style.wordSpacing! - - style.letterSpacing!, + offsetX - space.advanceWidth * style.wordSpacing! - style.letterSpacing!, _textDirection, true, )); @@ -1013,9 +980,7 @@ class RichText extends Widget with SpanningWidget { ), ); - offsetX += metrics.advanceWidth + - space.advanceWidth * style.wordSpacing! + - style.letterSpacing!; + offsetX += metrics.advanceWidth + space.advanceWidth * style.wordSpacing! + style.letterSpacing!; } if (line < spanLines.length - 1) { @@ -1024,9 +989,7 @@ class RichText extends Widget with SpanningWidget { spanStart, spanCount, bottom, - offsetX - - space.advanceWidth * style.wordSpacing! - - style.letterSpacing!, + offsetX - space.advanceWidth * style.wordSpacing! - style.letterSpacing!, _textDirection, false, )); @@ -1055,8 +1018,7 @@ class RichText extends Widget with SpanningWidget { } } - offsetX -= - space.advanceWidth * style.wordSpacing! - style.letterSpacing!; + offsetX -= space.advanceWidth * style.wordSpacing! - style.letterSpacing!; } else if (span is WidgetSpan) { span.child.layout( context, @@ -1159,8 +1121,7 @@ class RichText extends Widget with SpanningWidget { } } - box = PdfRect(0, 0, constraints.constrainWidth(width), - constraints.constrainHeight(offsetY)); + box = PdfRect(0, 0, constraints.constrainWidth(width), constraints.constrainHeight(offsetY)); _context ..endOffset = offsetY - _context.startOffset @@ -1180,8 +1141,7 @@ class RichText extends Widget with SpanningWidget { for (var index = 0; index < _decorations.length; index++) { final decoration = _decorations[index]; - if (decoration.startSpan >= _context.spanEnd || - decoration.endSpan < _context.spanStart) { + if (decoration.startSpan >= _context.spanEnd || decoration.endSpan < _context.spanStart) { _decorations.removeAt(index); index--; } @@ -1276,8 +1236,7 @@ class RichText extends Widget with SpanningWidget { while (low + 1 < high) { final metrics = font.stringMetrics(word.substring(0, pos), - letterSpacing: - style.letterSpacing! / (style.fontSize! * textScaleFactor)) * + letterSpacing: style.letterSpacing! / (style.fontSize! * textScaleFactor)) * (style.fontSize! * textScaleFactor); if (metrics.width > maxWidth) { diff --git a/pdf/lib/src/widgets/widget.dart b/pdf/lib/src/widgets/widget.dart index 51df0630..f1503e01 100644 --- a/pdf/lib/src/widgets/widget.dart +++ b/pdf/lib/src/widgets/widget.dart @@ -18,6 +18,7 @@ import 'dart:collection'; import 'dart:math' as math; import 'package:meta/meta.dart'; +import 'package:pdf/widgets.dart'; import 'package:vector_math/vector_math_64.dart'; import '../../pdf.dart'; @@ -146,7 +147,7 @@ abstract class Widget { PdfGraphics? canvas, BoxConstraints? constraints, required PdfPoint offset, - Alignment? alignment, + AlignmentGeometry? alignment, Context? context, }) { context ??= Context( @@ -165,7 +166,8 @@ abstract class Widget { assert(widget.box != null); if (alignment != null) { - final d = alignment.withinRect(widget.box!); + final resolvedAlignment = alignment.resolve(Directionality.of(context)); + final d = resolvedAlignment.withinRect(widget.box!); offset = PdfPoint(offset.x - d.x, offset.y - d.y); } diff --git a/pdf/test/rtl_layout_test.dart b/pdf/test/rtl_layout_test.dart index 63ea0593..570b4561 100644 --- a/pdf/test/rtl_layout_test.dart +++ b/pdf/test/rtl_layout_test.dart @@ -15,6 +15,7 @@ */ import 'dart:io'; +import 'dart:typed_data'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart'; @@ -41,9 +42,29 @@ final _yellowBox = Container( ); void main() { + late final arabicFont; setUpAll(() { Document.debug = true; pdf = Document(); + final fontData = File('test/fonts/cairo.ttf').readAsBytesSync(); + // final fontData = File('test/fonts/hacen_tunisia.ttf').readAsBytesSync(); + arabicFont = Font.ttf(fontData.buffer.asByteData()); + }); + + test('Should render Text aligned right', () { + pdf.addPage( + Page( + textDirection: TextDirection.rtl, + pageFormat: const PdfPageFormat(150, 50), + build: (Context context) => SizedBox( + width: 150, + child: Text( + 'مرحبا بالعالم', + style: TextStyle(font: arabicFont), + ), + ), + ), + ); }); test('Should render a blue box followed by a red box ordered RTL aligned right', () { @@ -51,13 +72,12 @@ void main() { Page( textDirection: TextDirection.rtl, pageFormat: const PdfPageFormat(150, 50), - build: (Context context) => - TestAnnotation( - anno: 'RTL Row', - child: Row( - children: [_blueBox, _redBox], - ), - ), + build: (Context context) => TestAnnotation( + anno: 'RTL Row', + child: Row( + children: [_blueBox, _redBox], + ), + ), ), ); }); @@ -67,14 +87,13 @@ void main() { Page( textDirection: TextDirection.rtl, pageFormat: const PdfPageFormat(150, 50), - build: (Context context) => - TestAnnotation( - anno: 'RTL Row MainAlignment.center', - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [_blueBox, _redBox], - ), - ), + build: (Context context) => TestAnnotation( + anno: 'RTL Row MainAlignment.center', + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [_blueBox, _redBox], + ), + ), ), ); }); @@ -84,18 +103,17 @@ void main() { Page( pageFormat: const PdfPageFormat(150, 100), textDirection: TextDirection.rtl, - build: (Context context) => - TestAnnotation( - anno: 'RTL Row CrossAlignment.end', - child: SizedBox( - width: 150, - height: 100, - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [_blueBox, _redBox], - ), - ), + build: (Context context) => TestAnnotation( + anno: 'RTL Row CrossAlignment.end', + child: SizedBox( + width: 150, + height: 100, + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [_blueBox, _redBox], ), + ), + ), ), ); }); @@ -103,13 +121,12 @@ void main() { pdf.addPage( Page( pageFormat: const PdfPageFormat(150, 50), - build: (Context context) => - TestAnnotation( - anno: 'LTR Row', - child: Row( - children: [_blueBox, _redBox], - ), - ), + build: (Context context) => TestAnnotation( + anno: 'LTR Row', + child: Row( + children: [_blueBox, _redBox], + ), + ), ), ); }); @@ -118,18 +135,17 @@ void main() { Page( textDirection: TextDirection.rtl, pageFormat: const PdfPageFormat(150, 150), - build: (Context context) => - TestAnnotation( - anno: 'RTL Column crossAlignment.start', - child: SizedBox( - width: 150, - height: 150, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [_blueBox, _redBox], - ), - ), + build: (Context context) => TestAnnotation( + anno: 'RTL Column crossAlignment.start', + child: SizedBox( + width: 150, + height: 150, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [_blueBox, _redBox], ), + ), + ), ), ); }); @@ -138,18 +154,17 @@ void main() { Page( textDirection: TextDirection.ltr, pageFormat: const PdfPageFormat(150, 150), - build: (Context context) => - TestAnnotation( - anno: 'LTR Column crossAlignment.start', - child: SizedBox( - width: 150, - height: 150, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [_blueBox, _redBox], - ), - ), + build: (Context context) => TestAnnotation( + anno: 'LTR Column crossAlignment.start', + child: SizedBox( + width: 150, + height: 150, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [_blueBox, _redBox], ), + ), + ), ), ); }); @@ -159,17 +174,16 @@ void main() { Page( textDirection: TextDirection.rtl, pageFormat: const PdfPageFormat(150, 150), - build: (Context context) => - TestAnnotation( - anno: 'RTL Wrap', - child: SizedBox( - width: 150, - height: 150, - child: Wrap( - children: [_blueBox, _redBox, _yellowBox], - ), - ), + build: (Context context) => TestAnnotation( + anno: 'RTL Wrap', + child: SizedBox( + width: 150, + height: 150, + child: Wrap( + children: [_blueBox, _redBox, _yellowBox], ), + ), + ), ), ); }); @@ -179,17 +193,16 @@ void main() { Page( textDirection: TextDirection.ltr, pageFormat: const PdfPageFormat(150, 150), - build: (Context context) => - TestAnnotation( - anno: 'LTR Wrap', - child: SizedBox( - width: 150, - height: 150, - child: Wrap( - children: [_blueBox, _redBox, _yellowBox], - ), - ), + build: (Context context) => TestAnnotation( + anno: 'LTR Wrap', + child: SizedBox( + width: 150, + height: 150, + child: Wrap( + children: [_blueBox, _redBox, _yellowBox], ), + ), + ), ), ); }); @@ -198,20 +211,19 @@ void main() { Page( textDirection: TextDirection.rtl, pageFormat: const PdfPageFormat(150, 150), - build: (Context context) => - TestAnnotation( - anno: 'RTL Wrap WrapAlignment.center', - child: SizedBox( - width: 150, - height: 150, - child: Wrap( - spacing: 10, - runSpacing: 10, - runAlignment: WrapAlignment.center, - children: [_blueBox, _redBox, _yellowBox], - ), - ), + build: (Context context) => TestAnnotation( + anno: 'RTL Wrap WrapAlignment.center', + child: SizedBox( + width: 150, + height: 150, + child: Wrap( + spacing: 10, + runSpacing: 10, + runAlignment: WrapAlignment.center, + children: [_blueBox, _redBox, _yellowBox], ), + ), + ), ), ); }); @@ -221,19 +233,18 @@ void main() { Page( textDirection: TextDirection.rtl, pageFormat: const PdfPageFormat(150, 150), - build: (Context context) => - TestAnnotation( - anno: 'RTL Wrap WrapAlignment.end', - child: SizedBox( - width: 150, - height: 150, - child: Wrap( - spacing: 10, - runSpacing: 10, - runAlignment: WrapAlignment.end, - children: [_blueBox, _redBox, _yellowBox], - )), - ), + build: (Context context) => TestAnnotation( + anno: 'RTL Wrap WrapAlignment.end', + child: SizedBox( + width: 150, + height: 150, + child: Wrap( + spacing: 10, + runSpacing: 10, + runAlignment: WrapAlignment.end, + children: [_blueBox, _redBox, _yellowBox], + )), + ), ), ); }); @@ -529,17 +540,20 @@ void main() { textDirection: TextDirection.rtl, pageFormat: const PdfPageFormat(150, 150), build: (Context context) { - return TestAnnotation(anno: 'RTL GridView Axis.horizontal', child: GridView( - crossAxisCount: 3, - childAspectRatio: 1, - direction: Axis.horizontal, - children: [ - for (int i = 0; i < 7; i++) - Container( - color: [PdfColors.blue, PdfColors.red, PdfColors.yellow][i % 3], - ), - ], - ),); + return TestAnnotation( + anno: 'RTL GridView Axis.horizontal', + child: GridView( + crossAxisCount: 3, + childAspectRatio: 1, + direction: Axis.horizontal, + children: [ + for (int i = 0; i < 7; i++) + Container( + color: [PdfColors.blue, PdfColors.red, PdfColors.yellow][i % 3], + ), + ], + ), + ); }, ), ); @@ -551,17 +565,20 @@ void main() { textDirection: TextDirection.ltr, pageFormat: const PdfPageFormat(150, 150), build: (Context context) { - return TestAnnotation(anno: 'LTR GridView Axis.horizontal', child: GridView( - crossAxisCount: 3, - childAspectRatio: 1, - direction: Axis.horizontal, - children: [ - for (int i = 0; i < 7; i++) - Container( - color: [PdfColors.blue, PdfColors.red, PdfColors.yellow][i % 3], - ), - ], - ),); + return TestAnnotation( + anno: 'LTR GridView Axis.horizontal', + child: GridView( + crossAxisCount: 3, + childAspectRatio: 1, + direction: Axis.horizontal, + children: [ + for (int i = 0; i < 7; i++) + Container( + color: [PdfColors.blue, PdfColors.red, PdfColors.yellow][i % 3], + ), + ], + ), + ); }, ), );