Milad akarie
Committed by David PHAM-VAN

Add TextAlign.start and TextAlign.end and fix Line.realign accordingly

change widgets geometry to directional geometry
... ... @@ -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!);
}
}
... ...
... ... @@ -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;
... ...
... ... @@ -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,
... ...
... ... @@ -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,
... ...
... ... @@ -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;
... ...
... ... @@ -40,8 +40,8 @@ mixin TableHelper {
required List<List<dynamic>> data,
EdgeInsetsGeometry cellPadding = const EdgeInsets.all(5),
double cellHeight = 0,
Alignment cellAlignment = Alignment.topLeft,
Map<int, Alignment>? cellAlignments,
AlignmentGeometry cellAlignment = Alignment.topLeft,
Map<int, AlignmentGeometry>? cellAlignments,
TextStyle? cellStyle,
TextStyle? oddCellStyle,
OnCellFormat? cellFormat,
... ... @@ -50,8 +50,8 @@ mixin TableHelper {
List<dynamic>? headers,
EdgeInsetsGeometry? headerPadding,
double? headerHeight,
Alignment headerAlignment = Alignment.center,
Map<int, Alignment>? headerAlignments,
AlignmentGeometry headerAlignment = Alignment.center,
Map<int, AlignmentGeometry>? 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 = <Widget>[];
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,
),
),
... ...
... ... @@ -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) {
... ...
... ... @@ -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);
}
... ...
... ... @@ -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],
),
],
),
);
},
),
);
... ...