Showing
5 changed files
with
96 additions
and
59 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,13 +647,14 @@ class RichText extends Widget { | @@ -647,13 +647,14 @@ 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) { | 650 | + if (offsetX + metrics.width > constraintWidth + 0.00001) { |
| 651 | + if (spanCount > 0 && metrics.width <= constraintWidth) { | ||
| 651 | overflow = true; | 652 | overflow = true; |
| 652 | lines.add(_Line( | 653 | lines.add(_Line( |
| 653 | this, | 654 | this, |
| 654 | spanStart, | 655 | spanStart, |
| 655 | spanCount, | 656 | spanCount, |
| 656 | - bottom ?? 0, | 657 | + bottom, |
| 657 | offsetX - | 658 | offsetX - |
| 658 | space.advanceWidth * style.wordSpacing! - | 659 | space.advanceWidth * style.wordSpacing! - |
| 659 | style.letterSpacing!, | 660 | style.letterSpacing!, |
| @@ -663,25 +664,37 @@ class RichText extends Widget { | @@ -663,25 +664,37 @@ class RichText extends Widget { | ||
| 663 | spanStart += spanCount; | 664 | spanStart += spanCount; |
| 664 | spanCount = 0; | 665 | spanCount = 0; |
| 665 | 666 | ||
| 666 | - if (maxLines != null && lines.length >= maxLines!) { | 667 | + if (_maxLines != null && lines.length >= _maxLines) { |
| 667 | return false; | 668 | return false; |
| 668 | } | 669 | } |
| 669 | 670 | ||
| 670 | offsetX = 0.0; | 671 | offsetX = 0.0; |
| 671 | - offsetY += bottom! - top! + style.lineSpacing!; | ||
| 672 | - top = null; | ||
| 673 | - bottom = null; | 672 | + offsetY += bottom - top + style.lineSpacing!; |
| 673 | + | ||
| 674 | + top = 0; | ||
| 675 | + bottom = 0; | ||
| 674 | 676 | ||
| 675 | if (offsetY > constraintHeight) { | 677 | if (offsetY > constraintHeight) { |
| 676 | return false; | 678 | return false; |
| 677 | } | 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; | ||
| 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