Showing
5 changed files
with
117 additions
and
80 deletions
@@ -33,14 +33,14 @@ enum TextDirection { ltr, rtl } | @@ -33,14 +33,14 @@ enum TextDirection { ltr, rtl } | ||
33 | abstract class _Span { | 33 | abstract class _Span { |
34 | _Span(this.style); | 34 | _Span(this.style); |
35 | 35 | ||
36 | - final TextStyle? style; | 36 | + final TextStyle style; |
37 | 37 | ||
38 | - PdfPoint offset = PdfPoint.zero; | 38 | + var offset = PdfPoint.zero; |
39 | 39 | ||
40 | - late double left; | ||
41 | - late double top; | ||
42 | - double? width; | ||
43 | - double? height; | 40 | + double get left; |
41 | + double get top; | ||
42 | + double get width; | ||
43 | + double get height; | ||
44 | 44 | ||
45 | @override | 45 | @override |
46 | String toString() { | 46 | String toString() { |
@@ -83,13 +83,13 @@ class _TextDecoration { | @@ -83,13 +83,13 @@ class _TextDecoration { | ||
83 | } | 83 | } |
84 | final x1 = spans[startSpan].offset.x + spans[startSpan].left; | 84 | final x1 = spans[startSpan].offset.x + spans[startSpan].left; |
85 | final x2 = | 85 | final x2 = |
86 | - spans[endSpan].offset.x + spans[endSpan].left + spans[endSpan].width!; | 86 | + spans[endSpan].offset.x + spans[endSpan].left + spans[endSpan].width; |
87 | var y1 = spans[startSpan].offset.y + spans[startSpan].top; | 87 | var y1 = spans[startSpan].offset.y + spans[startSpan].top; |
88 | - var y2 = y1 + spans[startSpan].height!; | 88 | + var y2 = y1 + spans[startSpan].height; |
89 | 89 | ||
90 | for (var n = startSpan + 1; n <= endSpan; n++) { | 90 | for (var n = startSpan + 1; n <= endSpan; n++) { |
91 | final ny1 = spans[n].offset.y + spans[n].top; | 91 | final ny1 = spans[n].offset.y + spans[n].top; |
92 | - final ny2 = ny1 + spans[n].height!; | 92 | + final ny2 = ny1 + spans[n].height; |
93 | y1 = math.min(y1, ny1); | 93 | y1 = math.min(y1, ny1); |
94 | y2 = math.max(y2, ny2); | 94 | y2 = math.max(y2, ny2); |
95 | } | 95 | } |
@@ -233,7 +233,7 @@ class _TextDecoration { | @@ -233,7 +233,7 @@ class _TextDecoration { | ||
233 | class _Word extends _Span { | 233 | class _Word extends _Span { |
234 | _Word( | 234 | _Word( |
235 | this.text, | 235 | this.text, |
236 | - TextStyle? style, | 236 | + TextStyle style, |
237 | this.metrics, | 237 | this.metrics, |
238 | ) : super(style); | 238 | ) : super(style); |
239 | 239 | ||
@@ -476,7 +476,7 @@ class _Line { | @@ -476,7 +476,7 @@ class _Line { | ||
476 | final int firstSpan; | 476 | final int firstSpan; |
477 | final int countSpan; | 477 | final int countSpan; |
478 | 478 | ||
479 | - TextAlign get textAlign => parent.textAlign; | 479 | + TextAlign get textAlign => parent._textAlign; |
480 | 480 | ||
481 | final double baseline; | 481 | final double baseline; |
482 | 482 | ||
@@ -518,7 +518,7 @@ class _Line { | @@ -518,7 +518,7 @@ class _Line { | ||
518 | if (textDirection == TextDirection.rtl) { | 518 | if (textDirection == TextDirection.rtl) { |
519 | for (var span in spans) { | 519 | for (var span in spans) { |
520 | span.offset = PdfPoint( | 520 | span.offset = PdfPoint( |
521 | - totalWidth - (span.offset.x + span.width!) - delta, | 521 | + totalWidth - (span.offset.x + span.width) - delta, |
522 | span.offset.y - baseline, | 522 | span.offset.y - baseline, |
523 | ); | 523 | ); |
524 | } | 524 | } |
@@ -537,34 +537,31 @@ class _Line { | @@ -537,34 +537,31 @@ class _Line { | ||
537 | class RichText extends Widget { | 537 | class RichText extends Widget { |
538 | RichText({ | 538 | RichText({ |
539 | required this.text, | 539 | required this.text, |
540 | - TextAlign? textAlign, | 540 | + this.textAlign, |
541 | this.textDirection, | 541 | this.textDirection, |
542 | - bool? softWrap, | 542 | + this.softWrap, |
543 | this.tightBounds = false, | 543 | this.tightBounds = false, |
544 | this.textScaleFactor = 1.0, | 544 | this.textScaleFactor = 1.0, |
545 | - int? maxLines, | ||
546 | - }) : _textAlign = textAlign, | ||
547 | - _softWrap = softWrap, | ||
548 | - _maxLines = maxLines; | 545 | + this.maxLines, |
546 | + }); | ||
549 | 547 | ||
550 | static bool debug = false; | 548 | static bool debug = false; |
551 | 549 | ||
552 | final InlineSpan text; | 550 | final InlineSpan text; |
553 | 551 | ||
554 | - TextAlign get textAlign => _textAlign!; | ||
555 | - TextAlign? _textAlign; | 552 | + final TextAlign? textAlign; |
553 | + | ||
554 | + late TextAlign _textAlign; | ||
556 | 555 | ||
557 | final TextDirection? textDirection; | 556 | final TextDirection? textDirection; |
558 | 557 | ||
559 | final double textScaleFactor; | 558 | final double textScaleFactor; |
560 | 559 | ||
561 | - bool get softWrap => _softWrap!; | ||
562 | - bool? _softWrap; | 560 | + final bool? softWrap; |
563 | 561 | ||
564 | final bool tightBounds; | 562 | final bool tightBounds; |
565 | 563 | ||
566 | - int? get maxLines => _maxLines; | ||
567 | - int? _maxLines; | 564 | + final int? maxLines; |
568 | 565 | ||
569 | final List<_Span> _spans = <_Span>[]; | 566 | final List<_Span> _spans = <_Span>[]; |
570 | 567 | ||
@@ -591,9 +588,9 @@ class RichText extends Widget { | @@ -591,9 +588,9 @@ class RichText extends Widget { | ||
591 | 588 | ||
592 | final theme = Theme.of(context); | 589 | final theme = Theme.of(context); |
593 | final defaultstyle = theme.defaultTextStyle; | 590 | final defaultstyle = theme.defaultTextStyle; |
594 | - _softWrap ??= theme.softWrap; | ||
595 | - _maxLines ??= theme.maxLines; | ||
596 | - _textAlign ??= theme.textAlign; | 591 | + final _softWrap = softWrap ?? theme.softWrap; |
592 | + final _maxLines = maxLines ?? theme.maxLines; | ||
593 | + _textAlign = textAlign ?? theme.textAlign; | ||
597 | final _textDirection = textDirection ?? Directionality.of(context); | 594 | final _textDirection = textDirection ?? Directionality.of(context); |
598 | 595 | ||
599 | final constraintWidth = constraints.hasBoundedWidth | 596 | final constraintWidth = constraints.hasBoundedWidth |
@@ -606,8 +603,8 @@ class RichText extends Widget { | @@ -606,8 +603,8 @@ class RichText extends Widget { | ||
606 | var offsetX = 0.0; | 603 | var offsetX = 0.0; |
607 | var offsetY = 0.0; | 604 | var offsetY = 0.0; |
608 | 605 | ||
609 | - double? top; | ||
610 | - double? bottom; | 606 | + var top = 0.0; |
607 | + var bottom = 0.0; | ||
611 | 608 | ||
612 | final lines = <_Line>[]; | 609 | final lines = <_Line>[]; |
613 | var spanCount = 0; | 610 | var spanCount = 0; |
@@ -635,7 +632,10 @@ class RichText extends Widget { | @@ -635,7 +632,10 @@ class RichText extends Widget { | ||
635 | .split('\n'); | 632 | .split('\n'); |
636 | 633 | ||
637 | for (var line = 0; line < spanLines.length; line++) { | 634 | for (var line = 0; line < spanLines.length; line++) { |
638 | - for (var word in spanLines[line].split(RegExp(r'\s'))) { | 635 | + final words = spanLines[line].split(RegExp(r'\s')); |
636 | + for (var index = 0; index < words.length; index++) { | ||
637 | + final word = words[index]; | ||
638 | + | ||
639 | if (word.isEmpty) { | 639 | if (word.isEmpty) { |
640 | offsetX += space.advanceWidth * style.wordSpacing! + | 640 | offsetX += space.advanceWidth * style.wordSpacing! + |
641 | style.letterSpacing!; | 641 | style.letterSpacing!; |
@@ -647,41 +647,54 @@ class RichText extends Widget { | @@ -647,41 +647,54 @@ class RichText extends Widget { | ||
647 | (style.fontSize! * textScaleFactor)) * | 647 | (style.fontSize! * textScaleFactor)) * |
648 | (style.fontSize! * textScaleFactor); | 648 | (style.fontSize! * textScaleFactor); |
649 | 649 | ||
650 | - if (offsetX + metrics.width > constraintWidth && spanCount > 0) { | ||
651 | - overflow = true; | ||
652 | - lines.add(_Line( | ||
653 | - this, | ||
654 | - spanStart, | ||
655 | - spanCount, | ||
656 | - bottom ?? 0, | ||
657 | - offsetX - | ||
658 | - space.advanceWidth * style.wordSpacing! - | ||
659 | - style.letterSpacing!, | ||
660 | - _textDirection, | ||
661 | - )); | ||
662 | - | ||
663 | - spanStart += spanCount; | ||
664 | - spanCount = 0; | ||
665 | - | ||
666 | - if (maxLines != null && lines.length >= maxLines!) { | ||
667 | - return false; | ||
668 | - } | ||
669 | - | ||
670 | - offsetX = 0.0; | ||
671 | - offsetY += bottom! - top! + style.lineSpacing!; | ||
672 | - top = null; | ||
673 | - bottom = null; | ||
674 | - | ||
675 | - if (offsetY > constraintHeight) { | ||
676 | - return false; | 650 | + if (offsetX + metrics.width > constraintWidth + 0.00001) { |
651 | + if (spanCount > 0 && metrics.width <= constraintWidth) { | ||
652 | + overflow = true; | ||
653 | + lines.add(_Line( | ||
654 | + this, | ||
655 | + spanStart, | ||
656 | + spanCount, | ||
657 | + bottom, | ||
658 | + offsetX - | ||
659 | + space.advanceWidth * style.wordSpacing! - | ||
660 | + style.letterSpacing!, | ||
661 | + _textDirection, | ||
662 | + )); | ||
663 | + | ||
664 | + spanStart += spanCount; | ||
665 | + spanCount = 0; | ||
666 | + | ||
667 | + if (_maxLines != null && lines.length >= _maxLines) { | ||
668 | + return false; | ||
669 | + } | ||
670 | + | ||
671 | + offsetX = 0.0; | ||
672 | + offsetY += bottom - top + style.lineSpacing!; | ||
673 | + | ||
674 | + top = 0; | ||
675 | + bottom = 0; | ||
676 | + | ||
677 | + if (offsetY > constraintHeight) { | ||
678 | + return false; | ||
679 | + } | ||
680 | + } else { | ||
681 | + // One word Overflow, try to split it. | ||
682 | + final pos = _splitWord(word, font, style, constraintWidth); | ||
683 | + | ||
684 | + words[index] = word.substring(0, pos); | ||
685 | + words.insert(index + 1, word.substring(pos)); | ||
686 | + | ||
687 | + // Try again | ||
688 | + index--; | ||
689 | + continue; | ||
677 | } | 690 | } |
678 | } | 691 | } |
679 | 692 | ||
680 | final baseline = span.baseline! * textScaleFactor; | 693 | final baseline = span.baseline! * textScaleFactor; |
681 | final mt = tightBounds ? metrics.top : metrics.descent; | 694 | final mt = tightBounds ? metrics.top : metrics.descent; |
682 | final mb = tightBounds ? metrics.bottom : metrics.ascent; | 695 | final mb = tightBounds ? metrics.bottom : metrics.ascent; |
683 | - top = math.min(top ?? mt + baseline, mt + baseline); | ||
684 | - bottom = math.max(bottom ?? mb + baseline, mb + baseline); | 696 | + top = math.min(top, mt + baseline); |
697 | + bottom = math.max(bottom, mb + baseline); | ||
685 | 698 | ||
686 | final wd = _Word( | 699 | final wd = _Word( |
687 | word, | 700 | word, |
@@ -707,12 +720,12 @@ class RichText extends Widget { | @@ -707,12 +720,12 @@ class RichText extends Widget { | ||
707 | style.letterSpacing!; | 720 | style.letterSpacing!; |
708 | } | 721 | } |
709 | 722 | ||
710 | - if (softWrap && line < spanLines.length - 1) { | 723 | + if (_softWrap && line < spanLines.length - 1) { |
711 | lines.add(_Line( | 724 | lines.add(_Line( |
712 | this, | 725 | this, |
713 | spanStart, | 726 | spanStart, |
714 | spanCount, | 727 | spanCount, |
715 | - bottom ?? 0, | 728 | + bottom, |
716 | offsetX - | 729 | offsetX - |
717 | space.advanceWidth * style.wordSpacing! - | 730 | space.advanceWidth * style.wordSpacing! - |
718 | style.letterSpacing!, | 731 | style.letterSpacing!, |
@@ -720,19 +733,19 @@ class RichText extends Widget { | @@ -720,19 +733,19 @@ class RichText extends Widget { | ||
720 | 733 | ||
721 | spanStart += spanCount; | 734 | spanStart += spanCount; |
722 | 735 | ||
723 | - if (maxLines != null && lines.length >= maxLines!) { | 736 | + if (_maxLines != null && lines.length >= _maxLines) { |
724 | spanCount = 0; | 737 | spanCount = 0; |
725 | return false; | 738 | return false; |
726 | } | 739 | } |
727 | 740 | ||
728 | offsetX = 0.0; | 741 | offsetX = 0.0; |
729 | if (spanCount > 0) { | 742 | if (spanCount > 0) { |
730 | - offsetY += bottom! - top! + style.lineSpacing!; | 743 | + offsetY += bottom - top + style.lineSpacing!; |
731 | } else { | 744 | } else { |
732 | offsetY += space.ascent + space.descent + style.lineSpacing!; | 745 | offsetY += space.ascent + space.descent + style.lineSpacing!; |
733 | } | 746 | } |
734 | - top = null; | ||
735 | - bottom = null; | 747 | + top = 0; |
748 | + bottom = 0; | ||
736 | spanCount = 0; | 749 | spanCount = 0; |
737 | 750 | ||
738 | if (offsetY > constraintHeight) { | 751 | if (offsetY > constraintHeight) { |
@@ -746,13 +759,13 @@ class RichText extends Widget { | @@ -746,13 +759,13 @@ class RichText extends Widget { | ||
746 | } else if (span is WidgetSpan) { | 759 | } else if (span is WidgetSpan) { |
747 | span.child.layout( | 760 | span.child.layout( |
748 | context, | 761 | context, |
749 | - BoxConstraints.tight(PdfPoint( | ||
750 | - double.infinity, | ||
751 | - style!.fontSize! * textScaleFactor, | ||
752 | - ))); | 762 | + BoxConstraints( |
763 | + maxWidth: constraintWidth, | ||
764 | + maxHeight: constraintHeight, | ||
765 | + )); | ||
753 | final ws = _WidgetSpan( | 766 | final ws = _WidgetSpan( |
754 | span.child, | 767 | span.child, |
755 | - style, | 768 | + style!, |
756 | ); | 769 | ); |
757 | 770 | ||
758 | if (offsetX + ws.width > constraintWidth && spanCount > 0) { | 771 | if (offsetX + ws.width > constraintWidth && spanCount > 0) { |
@@ -761,7 +774,7 @@ class RichText extends Widget { | @@ -761,7 +774,7 @@ class RichText extends Widget { | ||
761 | this, | 774 | this, |
762 | spanStart, | 775 | spanStart, |
763 | spanCount, | 776 | spanCount, |
764 | - bottom ?? 0, | 777 | + bottom, |
765 | offsetX, | 778 | offsetX, |
766 | _textDirection, | 779 | _textDirection, |
767 | )); | 780 | )); |
@@ -769,14 +782,14 @@ class RichText extends Widget { | @@ -769,14 +782,14 @@ class RichText extends Widget { | ||
769 | spanStart += spanCount; | 782 | spanStart += spanCount; |
770 | spanCount = 0; | 783 | spanCount = 0; |
771 | 784 | ||
772 | - if (maxLines != null && lines.length > maxLines!) { | 785 | + if (_maxLines != null && lines.length > _maxLines) { |
773 | return false; | 786 | return false; |
774 | } | 787 | } |
775 | 788 | ||
776 | offsetX = 0.0; | 789 | offsetX = 0.0; |
777 | - offsetY += bottom! - top! + style.lineSpacing!; | ||
778 | - top = null; | ||
779 | - bottom = null; | 790 | + offsetY += bottom - top + style.lineSpacing!; |
791 | + top = 0; | ||
792 | + bottom = 0; | ||
780 | 793 | ||
781 | if (offsetY > constraintHeight) { | 794 | if (offsetY > constraintHeight) { |
782 | return false; | 795 | return false; |
@@ -784,9 +797,9 @@ class RichText extends Widget { | @@ -784,9 +797,9 @@ class RichText extends Widget { | ||
784 | } | 797 | } |
785 | 798 | ||
786 | final baseline = span.baseline! * textScaleFactor; | 799 | final baseline = span.baseline! * textScaleFactor; |
787 | - top = math.min(top ?? baseline, baseline); | 800 | + top = math.min(top, baseline); |
788 | bottom = math.max( | 801 | bottom = math.max( |
789 | - bottom ?? ws.height + baseline, | 802 | + bottom, |
790 | ws.height + baseline, | 803 | ws.height + baseline, |
791 | ); | 804 | ); |
792 | 805 | ||
@@ -815,7 +828,7 @@ class RichText extends Widget { | @@ -815,7 +828,7 @@ class RichText extends Widget { | ||
815 | this, | 828 | this, |
816 | spanStart, | 829 | spanStart, |
817 | spanCount, | 830 | spanCount, |
818 | - bottom ?? 0, | 831 | + bottom, |
819 | offsetX, | 832 | offsetX, |
820 | _textDirection, | 833 | _textDirection, |
821 | )); | 834 | )); |
@@ -840,7 +853,7 @@ class RichText extends Widget { | @@ -840,7 +853,7 @@ class RichText extends Widget { | ||
840 | } | 853 | } |
841 | 854 | ||
842 | box = PdfRect(0, 0, constraints.constrainWidth(width), | 855 | box = PdfRect(0, 0, constraints.constrainWidth(width), |
843 | - constraints.constrainHeight(offsetY + (bottom ?? 0) - (top ?? 0))); | 856 | + constraints.constrainHeight(offsetY + bottom - top)); |
844 | } | 857 | } |
845 | 858 | ||
846 | @override | 859 | @override |
@@ -889,7 +902,7 @@ class RichText extends Widget { | @@ -889,7 +902,7 @@ class RichText extends Widget { | ||
889 | 902 | ||
890 | if (span.style != currentStyle) { | 903 | if (span.style != currentStyle) { |
891 | currentStyle = span.style; | 904 | currentStyle = span.style; |
892 | - if (currentStyle!.color != currentColor) { | 905 | + if (currentStyle.color != currentColor) { |
893 | currentColor = currentStyle.color; | 906 | currentColor = currentStyle.color; |
894 | context.canvas.setFillColor(currentColor); | 907 | context.canvas.setFillColor(currentColor); |
895 | } | 908 | } |
@@ -912,6 +925,29 @@ class RichText extends Widget { | @@ -912,6 +925,29 @@ class RichText extends Widget { | ||
912 | ); | 925 | ); |
913 | } | 926 | } |
914 | } | 927 | } |
928 | + | ||
929 | + int _splitWord(String word, PdfFont font, TextStyle style, double maxWidth) { | ||
930 | + var low = 0; | ||
931 | + var high = word.length; | ||
932 | + var pos = (low + high) ~/ 2; | ||
933 | + | ||
934 | + while (low + 1 < high) { | ||
935 | + final metrics = font.stringMetrics(word.substring(0, pos), | ||
936 | + letterSpacing: | ||
937 | + style.letterSpacing! / (style.fontSize! * textScaleFactor)) * | ||
938 | + (style.fontSize! * textScaleFactor); | ||
939 | + | ||
940 | + if (metrics.width > maxWidth) { | ||
941 | + high = pos; | ||
942 | + } else { | ||
943 | + low = pos; | ||
944 | + } | ||
945 | + | ||
946 | + pos = (low + high) ~/ 2; | ||
947 | + } | ||
948 | + | ||
949 | + return pos; | ||
950 | + } | ||
915 | } | 951 | } |
916 | 952 | ||
917 | class Text extends RichText { | 953 | class Text extends RichText { |
No preview for this file type
No preview for this file type
No preview for this file type
-
Please register or login to post a comment