Showing
14 changed files
with
437 additions
and
221 deletions
| @@ -161,6 +161,9 @@ See https://github.com/DavBfr/dart_pdf/wiki/Fonts-Management | @@ -161,6 +161,9 @@ See https://github.com/DavBfr/dart_pdf/wiki/Fonts-Management | ||
| 161 | /// Calculate the [PdfFontMetrics] for this glyph | 161 | /// Calculate the [PdfFontMetrics] for this glyph |
| 162 | PdfFontMetrics glyphMetrics(int charCode); | 162 | PdfFontMetrics glyphMetrics(int charCode); |
| 163 | 163 | ||
| 164 | + /// is this Rune supported by this font | ||
| 165 | + bool isRuneSupported(int charCode); | ||
| 166 | + | ||
| 164 | /// Calculate the [PdfFontMetrics] for this string | 167 | /// Calculate the [PdfFontMetrics] for this string |
| 165 | PdfFontMetrics stringMetrics(String s, {double letterSpacing = 0}) { | 168 | PdfFontMetrics stringMetrics(String s, {double letterSpacing = 0}) { |
| 166 | if (s.isEmpty) { | 169 | if (s.isEmpty) { |
| @@ -188,4 +188,9 @@ class PdfTtfFont extends PdfFont { | @@ -188,4 +188,9 @@ class PdfTtfFont extends PdfFont { | ||
| 188 | final metrics = bytes.map(glyphMetrics); | 188 | final metrics = bytes.map(glyphMetrics); |
| 189 | return PdfFontMetrics.append(metrics, letterSpacing: letterSpacing); | 189 | return PdfFontMetrics.append(metrics, letterSpacing: letterSpacing); |
| 190 | } | 190 | } |
| 191 | + | ||
| 192 | + @override | ||
| 193 | + bool isRuneSupported(int charCode) { | ||
| 194 | + return font.charToGlyphIndexMap.containsKey(charCode); | ||
| 195 | + } | ||
| 191 | } | 196 | } |
| @@ -64,6 +64,11 @@ class PdfType1Font extends PdfFont { | @@ -64,6 +64,11 @@ class PdfType1Font extends PdfFont { | ||
| 64 | 64 | ||
| 65 | @override | 65 | @override |
| 66 | PdfFontMetrics glyphMetrics(int charCode) { | 66 | PdfFontMetrics glyphMetrics(int charCode) { |
| 67 | + if (!isRuneSupported(charCode)) { | ||
| 68 | + throw Exception( | ||
| 69 | + 'Unable to display U+${charCode.toRadixString(16)} with $fontName'); | ||
| 70 | + } | ||
| 71 | + | ||
| 67 | return PdfFontMetrics( | 72 | return PdfFontMetrics( |
| 68 | left: 0, | 73 | left: 0, |
| 69 | top: descent, | 74 | top: descent, |
| @@ -72,4 +77,9 @@ class PdfType1Font extends PdfFont { | @@ -72,4 +77,9 @@ class PdfType1Font extends PdfFont { | ||
| 72 | : PdfFont.defaultGlyphWidth, | 77 | : PdfFont.defaultGlyphWidth, |
| 73 | bottom: ascent); | 78 | bottom: ascent); |
| 74 | } | 79 | } |
| 80 | + | ||
| 81 | + @override | ||
| 82 | + bool isRuneSupported(int charCode) { | ||
| 83 | + return charCode >= 0x00 && charCode <= 0xff; | ||
| 84 | + } | ||
| 75 | } | 85 | } |
| @@ -66,7 +66,7 @@ class SvgText extends SvgOperation { | @@ -66,7 +66,7 @@ class SvgText extends SvgOperation { | ||
| 66 | 66 | ||
| 67 | final font = painter.getFontCache( | 67 | final font = painter.getFontCache( |
| 68 | _brush.fontFamily!, _brush.fontStyle!, _brush.fontWeight!)!; | 68 | _brush.fontFamily!, _brush.fontStyle!, _brush.fontWeight!)!; |
| 69 | - final pdfFont = font.getFont(Context(document: painter.document))!; | 69 | + final pdfFont = font.getFont(Context(document: painter.document)); |
| 70 | final metrics = pdfFont.stringMetrics(text) * _brush.fontSize!.sizeValue; | 70 | final metrics = pdfFont.stringMetrics(text) * _brush.fontSize!.sizeValue; |
| 71 | offset = PdfPoint((x ?? offset.x) + dx, (y ?? offset.y) + dy); | 71 | offset = PdfPoint((x ?? offset.x) + dx, (y ?? offset.y) + dy); |
| 72 | 72 |
| @@ -392,7 +392,7 @@ class AnnotationTextField extends AnnotationBuilder { | @@ -392,7 +392,7 @@ class AnnotationTextField extends AnnotationBuilder { | ||
| 392 | fieldFlags: fieldFlags, | 392 | fieldFlags: fieldFlags, |
| 393 | value: value, | 393 | value: value, |
| 394 | defaultValue: defaultValue, | 394 | defaultValue: defaultValue, |
| 395 | - font: _textStyle.font!.getFont(context)!, | 395 | + font: _textStyle.font!.getFont(context), |
| 396 | fontSize: _textStyle.fontSize!, | 396 | fontSize: _textStyle.fontSize!, |
| 397 | textColor: _textStyle.color!, | 397 | textColor: _textStyle.color!, |
| 398 | ), | 398 | ), |
| @@ -94,7 +94,7 @@ class _BarcodeWidget extends Widget { | @@ -94,7 +94,7 @@ class _BarcodeWidget extends Widget { | ||
| 94 | final font = textStyle!.font!.getFont(context); | 94 | final font = textStyle!.font!.getFont(context); |
| 95 | 95 | ||
| 96 | for (final text in textList) { | 96 | for (final text in textList) { |
| 97 | - final metrics = font!.stringMetrics(text.text); | 97 | + final metrics = font.stringMetrics(text.text); |
| 98 | 98 | ||
| 99 | final top = box!.top - | 99 | final top = box!.top - |
| 100 | text.top - | 100 | text.top - |
| @@ -43,7 +43,7 @@ enum Type1Fonts { | @@ -43,7 +43,7 @@ enum Type1Fonts { | ||
| 43 | class Font { | 43 | class Font { |
| 44 | Font() : font = null; | 44 | Font() : font = null; |
| 45 | 45 | ||
| 46 | - Font.type1(Type1Fonts this.font); | 46 | + Font.type1(this.font); |
| 47 | 47 | ||
| 48 | factory Font.courier() => Font.type1(Type1Fonts.courier); | 48 | factory Font.courier() => Font.type1(Type1Fonts.courier); |
| 49 | factory Font.courierBold() => Font.type1(Type1Fonts.courierBold); | 49 | factory Font.courierBold() => Font.type1(Type1Fonts.courierBold); |
| @@ -127,13 +127,13 @@ class Font { | @@ -127,13 +127,13 @@ class Font { | ||
| 127 | 127 | ||
| 128 | PdfFont? _pdfFont; | 128 | PdfFont? _pdfFont; |
| 129 | 129 | ||
| 130 | - PdfFont? getFont(Context context) { | 130 | + PdfFont getFont(Context context) { |
| 131 | if (_pdfFont == null || _pdfFont!.pdfDocument != context.document) { | 131 | if (_pdfFont == null || _pdfFont!.pdfDocument != context.document) { |
| 132 | final pdfDocument = context.document; | 132 | final pdfDocument = context.document; |
| 133 | _pdfFont = buildFont(pdfDocument); | 133 | _pdfFont = buildFont(pdfDocument); |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | - return _pdfFont; | 136 | + return _pdfFont!; |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | @override | 139 | @override |
| @@ -278,7 +278,7 @@ class TextField extends StatelessWidget { | @@ -278,7 +278,7 @@ class TextField extends StatelessWidget { | ||
| 278 | fieldFlags: fieldFlags, | 278 | fieldFlags: fieldFlags, |
| 279 | value: value, | 279 | value: value, |
| 280 | defaultValue: defaultValue, | 280 | defaultValue: defaultValue, |
| 281 | - font: _textStyle.font!.getFont(context)!, | 281 | + font: _textStyle.font!.getFont(context), |
| 282 | fontSize: _textStyle.fontSize!, | 282 | fontSize: _textStyle.fontSize!, |
| 283 | textColor: _textStyle.color!, | 283 | textColor: _textStyle.color!, |
| 284 | ); | 284 | ); |
| @@ -21,9 +21,11 @@ import 'package:pdf/pdf.dart'; | @@ -21,9 +21,11 @@ import 'package:pdf/pdf.dart'; | ||
| 21 | import 'package:pdf/src/pdf/font/arabic.dart' as arabic; | 21 | import 'package:pdf/src/pdf/font/arabic.dart' as arabic; |
| 22 | 22 | ||
| 23 | import 'annotations.dart'; | 23 | import 'annotations.dart'; |
| 24 | +import 'basic.dart'; | ||
| 24 | import 'document.dart'; | 25 | import 'document.dart'; |
| 25 | import 'geometry.dart'; | 26 | import 'geometry.dart'; |
| 26 | import 'multi_page.dart'; | 27 | import 'multi_page.dart'; |
| 28 | +import 'placeholders.dart'; | ||
| 27 | import 'text_style.dart'; | 29 | import 'text_style.dart'; |
| 28 | import 'theme.dart'; | 30 | import 'theme.dart'; |
| 29 | import 'widget.dart'; | 31 | import 'widget.dart'; |
| @@ -170,7 +172,7 @@ class _TextDecoration { | @@ -170,7 +172,7 @@ class _TextDecoration { | ||
| 170 | 0.05); | 172 | 0.05); |
| 171 | 173 | ||
| 172 | if (style.decoration!.contains(TextDecoration.underline)) { | 174 | if (style.decoration!.contains(TextDecoration.underline)) { |
| 173 | - final base = -font!.descent * style.fontSize! * textScaleFactor / 2; | 175 | + final base = -font.descent * style.fontSize! * textScaleFactor / 2; |
| 174 | 176 | ||
| 175 | context.canvas.drawLine( | 177 | context.canvas.drawLine( |
| 176 | globalBox!.x + box!.left, | 178 | globalBox!.x + box!.left, |
| @@ -209,7 +211,7 @@ class _TextDecoration { | @@ -209,7 +211,7 @@ class _TextDecoration { | ||
| 209 | } | 211 | } |
| 210 | 212 | ||
| 211 | if (style.decoration!.contains(TextDecoration.lineThrough)) { | 213 | if (style.decoration!.contains(TextDecoration.lineThrough)) { |
| 212 | - final base = (1 - font!.descent) * style.fontSize! * textScaleFactor / 2; | 214 | + final base = (1 - font.descent) * style.fontSize! * textScaleFactor / 2; |
| 213 | context.canvas.drawLine( | 215 | context.canvas.drawLine( |
| 214 | globalBox!.x + box!.left, | 216 | globalBox!.x + box!.left, |
| 215 | globalBox.top + box.bottom + base, | 217 | globalBox.top + box.bottom + base, |
| @@ -281,7 +283,7 @@ class _Word extends _Span { | @@ -281,7 +283,7 @@ class _Word extends _Span { | ||
| 281 | PdfPoint point, | 283 | PdfPoint point, |
| 282 | ) { | 284 | ) { |
| 283 | context.canvas.drawString( | 285 | context.canvas.drawString( |
| 284 | - style.font!.getFont(context)!, | 286 | + style.font!.getFont(context), |
| 285 | style.fontSize! * textScaleFactor, | 287 | style.fontSize! * textScaleFactor, |
| 286 | text, | 288 | text, |
| 287 | point.x + offset.x, | 289 | point.x + offset.x, |
| @@ -316,10 +318,12 @@ class _Word extends _Span { | @@ -316,10 +318,12 @@ class _Word extends _Span { | ||
| 316 | } | 318 | } |
| 317 | 319 | ||
| 318 | class _WidgetSpan extends _Span { | 320 | class _WidgetSpan extends _Span { |
| 319 | - _WidgetSpan(this.widget, TextStyle style) : super(style); | 321 | + _WidgetSpan(this.widget, TextStyle style, this.baseline) : super(style); |
| 320 | 322 | ||
| 321 | final Widget widget; | 323 | final Widget widget; |
| 322 | 324 | ||
| 325 | + final double baseline; | ||
| 326 | + | ||
| 323 | @override | 327 | @override |
| 324 | double get left => 0; | 328 | double get left => 0; |
| 325 | 329 | ||
| @@ -365,11 +369,21 @@ class _WidgetSpan extends _Span { | @@ -365,11 +369,21 @@ class _WidgetSpan extends _Span { | ||
| 365 | double textScaleFactor, | 369 | double textScaleFactor, |
| 366 | PdfRect? globalBox, | 370 | PdfRect? globalBox, |
| 367 | ) { | 371 | ) { |
| 372 | + const deb = 5; | ||
| 373 | + | ||
| 368 | context.canvas | 374 | context.canvas |
| 369 | ..setLineWidth(.5) | 375 | ..setLineWidth(.5) |
| 370 | ..drawRect( | 376 | ..drawRect( |
| 371 | globalBox!.x + offset.x, globalBox.top + offset.y, width, height) | 377 | globalBox!.x + offset.x, globalBox.top + offset.y, width, height) |
| 372 | ..setStrokeColor(PdfColors.orange) | 378 | ..setStrokeColor(PdfColors.orange) |
| 379 | + ..strokePath() | ||
| 380 | + ..drawLine( | ||
| 381 | + globalBox.x + offset.x - deb, | ||
| 382 | + globalBox.top + offset.y - baseline, | ||
| 383 | + globalBox.x + offset.x + width + deb, | ||
| 384 | + globalBox.top + offset.y - baseline, | ||
| 385 | + ) | ||
| 386 | + ..setStrokeColor(PdfColors.deepPurple) | ||
| 373 | ..strokePath(); | 387 | ..strokePath(); |
| 374 | } | 388 | } |
| 375 | } | 389 | } |
| @@ -382,14 +396,24 @@ typedef _VisitorCallback = bool Function( | @@ -382,14 +396,24 @@ typedef _VisitorCallback = bool Function( | ||
| 382 | 396 | ||
| 383 | @immutable | 397 | @immutable |
| 384 | abstract class InlineSpan { | 398 | abstract class InlineSpan { |
| 385 | - const InlineSpan({this.style, this.baseline, this.annotation}); | 399 | + const InlineSpan({ |
| 400 | + this.style, | ||
| 401 | + required this.baseline, | ||
| 402 | + this.annotation, | ||
| 403 | + }); | ||
| 386 | 404 | ||
| 387 | final TextStyle? style; | 405 | final TextStyle? style; |
| 388 | 406 | ||
| 389 | - final double? baseline; | 407 | + final double baseline; |
| 390 | 408 | ||
| 391 | final AnnotationBuilder? annotation; | 409 | final AnnotationBuilder? annotation; |
| 392 | 410 | ||
| 411 | + InlineSpan copyWith({ | ||
| 412 | + TextStyle? style, | ||
| 413 | + double? baseline, | ||
| 414 | + AnnotationBuilder? annotation, | ||
| 415 | + }); | ||
| 416 | + | ||
| 393 | String toPlainText() { | 417 | String toPlainText() { |
| 394 | final buffer = StringBuffer(); | 418 | final buffer = StringBuffer(); |
| 395 | visitChildren(( | 419 | visitChildren(( |
| @@ -424,6 +448,19 @@ class WidgetSpan extends InlineSpan { | @@ -424,6 +448,19 @@ class WidgetSpan extends InlineSpan { | ||
| 424 | /// The widget to embed inline within text. | 448 | /// The widget to embed inline within text. |
| 425 | final Widget child; | 449 | final Widget child; |
| 426 | 450 | ||
| 451 | + @override | ||
| 452 | + InlineSpan copyWith({ | ||
| 453 | + TextStyle? style, | ||
| 454 | + double? baseline, | ||
| 455 | + AnnotationBuilder? annotation, | ||
| 456 | + }) => | ||
| 457 | + WidgetSpan( | ||
| 458 | + child: child, | ||
| 459 | + style: style ?? this.style, | ||
| 460 | + baseline: baseline ?? this.baseline, | ||
| 461 | + annotation: annotation ?? this.annotation, | ||
| 462 | + ); | ||
| 463 | + | ||
| 427 | /// Calls `visitor` on this [WidgetSpan]. There are no children spans to walk. | 464 | /// Calls `visitor` on this [WidgetSpan]. There are no children spans to walk. |
| 428 | @override | 465 | @override |
| 429 | bool visitChildren( | 466 | bool visitChildren( |
| @@ -452,6 +489,20 @@ class TextSpan extends InlineSpan { | @@ -452,6 +489,20 @@ class TextSpan extends InlineSpan { | ||
| 452 | final List<InlineSpan>? children; | 489 | final List<InlineSpan>? children; |
| 453 | 490 | ||
| 454 | @override | 491 | @override |
| 492 | + InlineSpan copyWith({ | ||
| 493 | + TextStyle? style, | ||
| 494 | + double? baseline, | ||
| 495 | + AnnotationBuilder? annotation, | ||
| 496 | + }) => | ||
| 497 | + TextSpan( | ||
| 498 | + style: style ?? this.style, | ||
| 499 | + text: text, | ||
| 500 | + baseline: baseline ?? this.baseline, | ||
| 501 | + children: children, | ||
| 502 | + annotation: annotation ?? this.annotation, | ||
| 503 | + ); | ||
| 504 | + | ||
| 505 | + @override | ||
| 455 | bool visitChildren( | 506 | bool visitChildren( |
| 456 | _VisitorCallback visitor, | 507 | _VisitorCallback visitor, |
| 457 | TextStyle? parentStyle, | 508 | TextStyle? parentStyle, |
| @@ -624,6 +675,8 @@ class RichText extends Widget with SpanningWidget { | @@ -624,6 +675,8 @@ class RichText extends Widget with SpanningWidget { | ||
| 624 | 675 | ||
| 625 | var _mustClip = false; | 676 | var _mustClip = false; |
| 626 | 677 | ||
| 678 | + List<InlineSpan>? _preprocessed; | ||
| 679 | + | ||
| 627 | void _appendDecoration(bool append, _TextDecoration td) { | 680 | void _appendDecoration(bool append, _TextDecoration td) { |
| 628 | if (append && _decorations.isNotEmpty) { | 681 | if (append && _decorations.isNotEmpty) { |
| 629 | final last = _decorations.last; | 682 | final last = _decorations.last; |
| @@ -637,6 +690,132 @@ class RichText extends Widget with SpanningWidget { | @@ -637,6 +690,132 @@ class RichText extends Widget with SpanningWidget { | ||
| 637 | _decorations.add(td); | 690 | _decorations.add(td); |
| 638 | } | 691 | } |
| 639 | 692 | ||
| 693 | + InlineSpan _addText({ | ||
| 694 | + required List<int> text, | ||
| 695 | + int start = 0, | ||
| 696 | + int? end, | ||
| 697 | + double baseline = 0, | ||
| 698 | + required TextStyle style, | ||
| 699 | + AnnotationBuilder? annotation, | ||
| 700 | + }) { | ||
| 701 | + return TextSpan( | ||
| 702 | + text: String.fromCharCodes(text, start, end), | ||
| 703 | + style: style, | ||
| 704 | + baseline: baseline, | ||
| 705 | + annotation: annotation, | ||
| 706 | + ); | ||
| 707 | + } | ||
| 708 | + | ||
| 709 | + InlineSpan _addPlaceholder({ | ||
| 710 | + double baseline = 0, | ||
| 711 | + required TextStyle style, | ||
| 712 | + AnnotationBuilder? annotation, | ||
| 713 | + }) { | ||
| 714 | + return WidgetSpan( | ||
| 715 | + child: SizedBox( | ||
| 716 | + height: style.fontSize, | ||
| 717 | + width: style.fontSize! / 2, | ||
| 718 | + child: Placeholder( | ||
| 719 | + color: style.color!, | ||
| 720 | + strokeWidth: 1, | ||
| 721 | + ), | ||
| 722 | + ), | ||
| 723 | + style: style, | ||
| 724 | + baseline: baseline, | ||
| 725 | + annotation: annotation, | ||
| 726 | + ); | ||
| 727 | + } | ||
| 728 | + | ||
| 729 | + /// Check available characters in the fonts | ||
| 730 | + /// use fallback if needed and replace emojis | ||
| 731 | + List<InlineSpan> _preprocessSpans(Context context) { | ||
| 732 | + final theme = Theme.of(context); | ||
| 733 | + final defaultstyle = theme.defaultTextStyle; | ||
| 734 | + final spans = <InlineSpan>[]; | ||
| 735 | + | ||
| 736 | + text.visitChildren(( | ||
| 737 | + InlineSpan span, | ||
| 738 | + TextStyle? style, | ||
| 739 | + AnnotationBuilder? annotation, | ||
| 740 | + ) { | ||
| 741 | + if (span is! TextSpan) { | ||
| 742 | + spans.add(span.copyWith(style: style, annotation: annotation)); | ||
| 743 | + return true; | ||
| 744 | + } | ||
| 745 | + if (span.text == null) { | ||
| 746 | + return true; | ||
| 747 | + } | ||
| 748 | + | ||
| 749 | + final font = style!.font!.getFont(context); | ||
| 750 | + | ||
| 751 | + var text = span.text!.runes.toList(); | ||
| 752 | + | ||
| 753 | + for (var index = 0; index < text.length; index++) { | ||
| 754 | + final rune = text[index]; | ||
| 755 | + if (rune == 0x0a) { | ||
| 756 | + continue; | ||
| 757 | + } | ||
| 758 | + | ||
| 759 | + if (!font.isRuneSupported(rune)) { | ||
| 760 | + if (index > 0) { | ||
| 761 | + spans.add(_addText( | ||
| 762 | + text: text, | ||
| 763 | + end: index, | ||
| 764 | + style: style, | ||
| 765 | + baseline: span.baseline, | ||
| 766 | + annotation: annotation, | ||
| 767 | + )); | ||
| 768 | + } | ||
| 769 | + var found = false; | ||
| 770 | + for (final fb in style.fontFallback) { | ||
| 771 | + final font = fb.getFont(context); | ||
| 772 | + if (font.isRuneSupported(rune)) { | ||
| 773 | + spans.add(_addText( | ||
| 774 | + text: [rune], | ||
| 775 | + style: style.copyWith( | ||
| 776 | + font: fb, | ||
| 777 | + fontNormal: fb, | ||
| 778 | + fontBold: fb, | ||
| 779 | + fontBoldItalic: fb, | ||
| 780 | + fontItalic: fb, | ||
| 781 | + ), | ||
| 782 | + baseline: span.baseline, | ||
| 783 | + annotation: annotation, | ||
| 784 | + )); | ||
| 785 | + found = true; | ||
| 786 | + break; | ||
| 787 | + } | ||
| 788 | + } | ||
| 789 | + if (!found) { | ||
| 790 | + spans.add(_addPlaceholder( | ||
| 791 | + style: style, | ||
| 792 | + baseline: span.baseline, | ||
| 793 | + annotation: annotation, | ||
| 794 | + )); | ||
| 795 | + assert(() { | ||
| 796 | + print( | ||
| 797 | + 'Unable to find a font to draw "${String.fromCharCode(rune)}" (U+${rune.toRadixString(16)}) try to provide a TextStyle.fontFallback'); | ||
| 798 | + return true; | ||
| 799 | + }()); | ||
| 800 | + } | ||
| 801 | + text = text.sublist(index + 1); | ||
| 802 | + index = -1; | ||
| 803 | + } | ||
| 804 | + } | ||
| 805 | + | ||
| 806 | + spans.add(_addText( | ||
| 807 | + text: text, | ||
| 808 | + style: style, | ||
| 809 | + baseline: span.baseline, | ||
| 810 | + annotation: annotation, | ||
| 811 | + )); | ||
| 812 | + | ||
| 813 | + return true; | ||
| 814 | + }, defaultstyle, null); | ||
| 815 | + | ||
| 816 | + return spans; | ||
| 817 | + } | ||
| 818 | + | ||
| 640 | @override | 819 | @override |
| 641 | void layout(Context context, BoxConstraints constraints, | 820 | void layout(Context context, BoxConstraints constraints, |
| 642 | {bool parentUsesSize = false}) { | 821 | {bool parentUsesSize = false}) { |
| @@ -644,7 +823,6 @@ class RichText extends Widget with SpanningWidget { | @@ -644,7 +823,6 @@ class RichText extends Widget with SpanningWidget { | ||
| 644 | _decorations.clear(); | 823 | _decorations.clear(); |
| 645 | 824 | ||
| 646 | final theme = Theme.of(context); | 825 | final theme = Theme.of(context); |
| 647 | - final defaultstyle = theme.defaultTextStyle; | ||
| 648 | final _softWrap = softWrap ?? theme.softWrap; | 826 | final _softWrap = softWrap ?? theme.softWrap; |
| 649 | final _maxLines = maxLines ?? theme.maxLines; | 827 | final _maxLines = maxLines ?? theme.maxLines; |
| 650 | _textAlign = textAlign ?? theme.textAlign; | 828 | _textAlign = textAlign ?? theme.textAlign; |
| @@ -669,228 +847,232 @@ class RichText extends Widget with SpanningWidget { | @@ -669,228 +847,232 @@ class RichText extends Widget with SpanningWidget { | ||
| 669 | var spanStart = 0; | 847 | var spanStart = 0; |
| 670 | var overflow = false; | 848 | var overflow = false; |
| 671 | 849 | ||
| 672 | - text.visitChildren(( | ||
| 673 | - InlineSpan span, | ||
| 674 | - TextStyle? style, | ||
| 675 | - AnnotationBuilder? annotation, | ||
| 676 | - ) { | ||
| 677 | - if (span is TextSpan) { | ||
| 678 | - if (span.text == null) { | ||
| 679 | - return true; | ||
| 680 | - } | 850 | + _preprocessed ??= _preprocessSpans(context); |
| 681 | 851 | ||
| 682 | - final font = style!.font!.getFont(context)!; | 852 | + void _buildLines() { |
| 853 | + for (final span in _preprocessed!) { | ||
| 854 | + final style = span.style; | ||
| 855 | + final annotation = span.annotation; | ||
| 683 | 856 | ||
| 684 | - final space = | ||
| 685 | - font.stringMetrics(' ') * (style.fontSize! * textScaleFactor); | 857 | + if (span is TextSpan) { |
| 858 | + if (span.text == null) { | ||
| 859 | + continue; | ||
| 860 | + } | ||
| 686 | 861 | ||
| 687 | - final spanLines = (_textDirection == TextDirection.rtl | ||
| 688 | - ? arabic.convert(span.text!) | ||
| 689 | - : span.text)! | ||
| 690 | - .split('\n'); | 862 | + final font = style!.font!.getFont(context); |
| 691 | 863 | ||
| 692 | - for (var line = 0; line < spanLines.length; line++) { | ||
| 693 | - final words = spanLines[line].split(RegExp(r'\s')); | ||
| 694 | - for (var index = 0; index < words.length; index++) { | ||
| 695 | - final word = words[index]; | 864 | + final space = |
| 865 | + font.stringMetrics(' ') * (style.fontSize! * textScaleFactor); | ||
| 696 | 866 | ||
| 697 | - if (word.isEmpty) { | ||
| 698 | - offsetX += space.advanceWidth * style.wordSpacing! + | ||
| 699 | - style.letterSpacing!; | ||
| 700 | - continue; | ||
| 701 | - } | 867 | + final spanLines = (_textDirection == TextDirection.rtl |
| 868 | + ? arabic.convert(span.text!) | ||
| 869 | + : span.text)! | ||
| 870 | + .split('\n'); | ||
| 702 | 871 | ||
| 703 | - final metrics = font.stringMetrics(word, | ||
| 704 | - letterSpacing: style.letterSpacing! / | ||
| 705 | - (style.fontSize! * textScaleFactor)) * | ||
| 706 | - (style.fontSize! * textScaleFactor); | ||
| 707 | - | ||
| 708 | - if (_softWrap && | ||
| 709 | - offsetX + metrics.width > constraintWidth + 0.00001) { | ||
| 710 | - if (spanCount > 0 && metrics.width <= constraintWidth) { | ||
| 711 | - overflow = true; | ||
| 712 | - lines.add(_Line( | ||
| 713 | - this, | ||
| 714 | - spanStart, | ||
| 715 | - spanCount, | ||
| 716 | - bottom, | ||
| 717 | - offsetX - | ||
| 718 | - space.advanceWidth * style.wordSpacing! - | ||
| 719 | - style.letterSpacing!, | ||
| 720 | - _textDirection, | ||
| 721 | - true, | ||
| 722 | - )); | ||
| 723 | - | ||
| 724 | - spanStart += spanCount; | ||
| 725 | - spanCount = 0; | ||
| 726 | - | ||
| 727 | - offsetX = 0.0; | ||
| 728 | - offsetY += bottom - top; | ||
| 729 | - top = 0; | ||
| 730 | - bottom = 0; | 872 | + for (var line = 0; line < spanLines.length; line++) { |
| 873 | + final words = spanLines[line].split(RegExp(r'\s')); | ||
| 874 | + for (var index = 0; index < words.length; index++) { | ||
| 875 | + final word = words[index]; | ||
| 731 | 876 | ||
| 732 | - if (_maxLines != null && lines.length >= _maxLines) { | ||
| 733 | - return false; | ||
| 734 | - } | 877 | + if (word.isEmpty) { |
| 878 | + offsetX += space.advanceWidth * style.wordSpacing! + | ||
| 879 | + style.letterSpacing!; | ||
| 880 | + continue; | ||
| 881 | + } | ||
| 735 | 882 | ||
| 736 | - if (offsetY > constraintHeight) { | ||
| 737 | - return false; | 883 | + final metrics = font.stringMetrics(word, |
| 884 | + letterSpacing: style.letterSpacing! / | ||
| 885 | + (style.fontSize! * textScaleFactor)) * | ||
| 886 | + (style.fontSize! * textScaleFactor); | ||
| 887 | + | ||
| 888 | + if (_softWrap && | ||
| 889 | + offsetX + metrics.width > constraintWidth + 0.00001) { | ||
| 890 | + if (spanCount > 0 && metrics.width <= constraintWidth) { | ||
| 891 | + overflow = true; | ||
| 892 | + lines.add(_Line( | ||
| 893 | + this, | ||
| 894 | + spanStart, | ||
| 895 | + spanCount, | ||
| 896 | + bottom, | ||
| 897 | + offsetX - | ||
| 898 | + space.advanceWidth * style.wordSpacing! - | ||
| 899 | + style.letterSpacing!, | ||
| 900 | + _textDirection, | ||
| 901 | + true, | ||
| 902 | + )); | ||
| 903 | + | ||
| 904 | + spanStart += spanCount; | ||
| 905 | + spanCount = 0; | ||
| 906 | + | ||
| 907 | + offsetX = 0.0; | ||
| 908 | + offsetY += bottom - top; | ||
| 909 | + top = 0; | ||
| 910 | + bottom = 0; | ||
| 911 | + | ||
| 912 | + if (_maxLines != null && lines.length >= _maxLines) { | ||
| 913 | + return; | ||
| 914 | + } | ||
| 915 | + | ||
| 916 | + if (offsetY > constraintHeight) { | ||
| 917 | + return; | ||
| 918 | + } | ||
| 919 | + | ||
| 920 | + offsetY += style.lineSpacing! * textScaleFactor; | ||
| 921 | + } else { | ||
| 922 | + // One word Overflow, try to split it. | ||
| 923 | + final pos = _splitWord(word, font, style, constraintWidth); | ||
| 924 | + | ||
| 925 | + if (pos < word.length) { | ||
| 926 | + words[index] = word.substring(0, pos); | ||
| 927 | + words.insert(index + 1, word.substring(pos)); | ||
| 928 | + | ||
| 929 | + // Try again | ||
| 930 | + index--; | ||
| 931 | + continue; | ||
| 932 | + } | ||
| 738 | } | 933 | } |
| 934 | + } | ||
| 739 | 935 | ||
| 740 | - offsetY += style.lineSpacing! * textScaleFactor; | ||
| 741 | - } else { | ||
| 742 | - // One word Overflow, try to split it. | ||
| 743 | - final pos = _splitWord(word, font, style, constraintWidth); | 936 | + final baseline = span.baseline * textScaleFactor; |
| 937 | + final mt = tightBounds ? metrics.top : metrics.descent; | ||
| 938 | + final mb = tightBounds ? metrics.bottom : metrics.ascent; | ||
| 939 | + top = math.min(top, mt + baseline); | ||
| 940 | + bottom = math.max(bottom, mb + baseline); | ||
| 744 | 941 | ||
| 745 | - if (pos < word.length) { | ||
| 746 | - words[index] = word.substring(0, pos); | ||
| 747 | - words.insert(index + 1, word.substring(pos)); | 942 | + final wd = _Word( |
| 943 | + word, | ||
| 944 | + style, | ||
| 945 | + metrics, | ||
| 946 | + ); | ||
| 947 | + wd.offset = PdfPoint(offsetX, -offsetY + baseline); | ||
| 948 | + _spans.add(wd); | ||
| 949 | + spanCount++; | ||
| 950 | + | ||
| 951 | + _appendDecoration( | ||
| 952 | + spanCount > 1, | ||
| 953 | + _TextDecoration( | ||
| 954 | + style, | ||
| 955 | + annotation, | ||
| 956 | + _spans.length - 1, | ||
| 957 | + _spans.length - 1, | ||
| 958 | + ), | ||
| 959 | + ); | ||
| 960 | + | ||
| 961 | + offsetX += metrics.advanceWidth + | ||
| 962 | + space.advanceWidth * style.wordSpacing! + | ||
| 963 | + style.letterSpacing!; | ||
| 964 | + } | ||
| 748 | 965 | ||
| 749 | - // Try again | ||
| 750 | - index--; | ||
| 751 | - continue; | ||
| 752 | - } | 966 | + if (line < spanLines.length - 1) { |
| 967 | + lines.add(_Line( | ||
| 968 | + this, | ||
| 969 | + spanStart, | ||
| 970 | + spanCount, | ||
| 971 | + bottom, | ||
| 972 | + offsetX - | ||
| 973 | + space.advanceWidth * style.wordSpacing! - | ||
| 974 | + style.letterSpacing!, | ||
| 975 | + _textDirection, | ||
| 976 | + false, | ||
| 977 | + )); | ||
| 978 | + | ||
| 979 | + spanStart += spanCount; | ||
| 980 | + | ||
| 981 | + offsetX = 0.0; | ||
| 982 | + if (spanCount > 0) { | ||
| 983 | + offsetY += bottom - top; | ||
| 984 | + } else { | ||
| 985 | + offsetY += space.ascent + space.descent; | ||
| 753 | } | 986 | } |
| 754 | - } | 987 | + top = 0; |
| 988 | + bottom = 0; | ||
| 989 | + spanCount = 0; | ||
| 755 | 990 | ||
| 756 | - final baseline = span.baseline! * textScaleFactor; | ||
| 757 | - final mt = tightBounds ? metrics.top : metrics.descent; | ||
| 758 | - final mb = tightBounds ? metrics.bottom : metrics.ascent; | ||
| 759 | - top = math.min(top, mt + baseline); | ||
| 760 | - bottom = math.max(bottom, mb + baseline); | 991 | + if (_maxLines != null && lines.length >= _maxLines) { |
| 992 | + return; | ||
| 993 | + } | ||
| 761 | 994 | ||
| 762 | - final wd = _Word( | ||
| 763 | - word, | ||
| 764 | - style, | ||
| 765 | - metrics, | ||
| 766 | - ); | ||
| 767 | - wd.offset = PdfPoint(offsetX, -offsetY + baseline); | ||
| 768 | - _spans.add(wd); | ||
| 769 | - spanCount++; | ||
| 770 | - | ||
| 771 | - _appendDecoration( | ||
| 772 | - spanCount > 1, | ||
| 773 | - _TextDecoration( | ||
| 774 | - style, | ||
| 775 | - annotation, | ||
| 776 | - _spans.length - 1, | ||
| 777 | - _spans.length - 1, | ||
| 778 | - ), | ||
| 779 | - ); | ||
| 780 | - | ||
| 781 | - offsetX += metrics.advanceWidth + | ||
| 782 | - space.advanceWidth * style.wordSpacing! + | ||
| 783 | - style.letterSpacing!; | 995 | + if (offsetY > constraintHeight) { |
| 996 | + return; | ||
| 997 | + } | ||
| 998 | + | ||
| 999 | + offsetY += style.lineSpacing! * textScaleFactor; | ||
| 1000 | + } | ||
| 784 | } | 1001 | } |
| 785 | 1002 | ||
| 786 | - if (line < spanLines.length - 1) { | 1003 | + offsetX -= |
| 1004 | + space.advanceWidth * style.wordSpacing! - style.letterSpacing!; | ||
| 1005 | + } else if (span is WidgetSpan) { | ||
| 1006 | + span.child.layout( | ||
| 1007 | + context, | ||
| 1008 | + BoxConstraints( | ||
| 1009 | + maxWidth: constraintWidth, | ||
| 1010 | + maxHeight: constraintHeight, | ||
| 1011 | + )); | ||
| 1012 | + final ws = _WidgetSpan( | ||
| 1013 | + span.child, | ||
| 1014 | + style!, | ||
| 1015 | + span.baseline, | ||
| 1016 | + ); | ||
| 1017 | + | ||
| 1018 | + if (offsetX + ws.width > constraintWidth && spanCount > 0) { | ||
| 1019 | + overflow = true; | ||
| 787 | lines.add(_Line( | 1020 | lines.add(_Line( |
| 788 | this, | 1021 | this, |
| 789 | spanStart, | 1022 | spanStart, |
| 790 | spanCount, | 1023 | spanCount, |
| 791 | bottom, | 1024 | bottom, |
| 792 | - offsetX - | ||
| 793 | - space.advanceWidth * style.wordSpacing! - | ||
| 794 | - style.letterSpacing!, | 1025 | + offsetX, |
| 795 | _textDirection, | 1026 | _textDirection, |
| 796 | - false, | 1027 | + true, |
| 797 | )); | 1028 | )); |
| 798 | 1029 | ||
| 799 | spanStart += spanCount; | 1030 | spanStart += spanCount; |
| 1031 | + spanCount = 0; | ||
| 800 | 1032 | ||
| 801 | - offsetX = 0.0; | ||
| 802 | - if (spanCount > 0) { | ||
| 803 | - offsetY += bottom - top; | ||
| 804 | - } else { | ||
| 805 | - offsetY += space.ascent + space.descent; | 1033 | + if (_maxLines != null && lines.length > _maxLines) { |
| 1034 | + return; | ||
| 806 | } | 1035 | } |
| 1036 | + | ||
| 1037 | + offsetX = 0.0; | ||
| 1038 | + offsetY += bottom - top; | ||
| 807 | top = 0; | 1039 | top = 0; |
| 808 | bottom = 0; | 1040 | bottom = 0; |
| 809 | - spanCount = 0; | ||
| 810 | - | ||
| 811 | - if (_maxLines != null && lines.length >= _maxLines) { | ||
| 812 | - return false; | ||
| 813 | - } | ||
| 814 | 1041 | ||
| 815 | if (offsetY > constraintHeight) { | 1042 | if (offsetY > constraintHeight) { |
| 816 | - return false; | 1043 | + return; |
| 817 | } | 1044 | } |
| 818 | 1045 | ||
| 819 | offsetY += style.lineSpacing! * textScaleFactor; | 1046 | offsetY += style.lineSpacing! * textScaleFactor; |
| 820 | } | 1047 | } |
| 821 | - } | ||
| 822 | - | ||
| 823 | - offsetX -= | ||
| 824 | - space.advanceWidth * style.wordSpacing! - style.letterSpacing!; | ||
| 825 | - } else if (span is WidgetSpan) { | ||
| 826 | - span.child.layout( | ||
| 827 | - context, | ||
| 828 | - BoxConstraints( | ||
| 829 | - maxWidth: constraintWidth, | ||
| 830 | - maxHeight: constraintHeight, | ||
| 831 | - )); | ||
| 832 | - final ws = _WidgetSpan( | ||
| 833 | - span.child, | ||
| 834 | - style!, | ||
| 835 | - ); | ||
| 836 | 1048 | ||
| 837 | - if (offsetX + ws.width > constraintWidth && spanCount > 0) { | ||
| 838 | - overflow = true; | ||
| 839 | - lines.add(_Line( | ||
| 840 | - this, | ||
| 841 | - spanStart, | ||
| 842 | - spanCount, | 1049 | + final baseline = span.baseline * textScaleFactor; |
| 1050 | + top = math.min(top, baseline); | ||
| 1051 | + bottom = math.max( | ||
| 843 | bottom, | 1052 | bottom, |
| 844 | - offsetX, | ||
| 845 | - _textDirection, | ||
| 846 | - true, | ||
| 847 | - )); | ||
| 848 | - | ||
| 849 | - spanStart += spanCount; | ||
| 850 | - spanCount = 0; | ||
| 851 | - | ||
| 852 | - if (_maxLines != null && lines.length > _maxLines) { | ||
| 853 | - return false; | ||
| 854 | - } | 1053 | + ws.height + baseline, |
| 1054 | + ); | ||
| 855 | 1055 | ||
| 856 | - offsetX = 0.0; | ||
| 857 | - offsetY += bottom - top; | ||
| 858 | - top = 0; | ||
| 859 | - bottom = 0; | 1056 | + ws.offset = PdfPoint(offsetX, -offsetY + baseline); |
| 1057 | + _spans.add(ws); | ||
| 1058 | + spanCount++; | ||
| 860 | 1059 | ||
| 861 | - if (offsetY > constraintHeight) { | ||
| 862 | - return false; | ||
| 863 | - } | 1060 | + _appendDecoration( |
| 1061 | + spanCount > 1, | ||
| 1062 | + _TextDecoration( | ||
| 1063 | + style, | ||
| 1064 | + annotation, | ||
| 1065 | + _spans.length - 1, | ||
| 1066 | + _spans.length - 1, | ||
| 1067 | + ), | ||
| 1068 | + ); | ||
| 864 | 1069 | ||
| 865 | - offsetY += style.lineSpacing! * textScaleFactor; | 1070 | + offsetX += ws.left + ws.width; |
| 866 | } | 1071 | } |
| 867 | - | ||
| 868 | - final baseline = span.baseline! * textScaleFactor; | ||
| 869 | - top = math.min(top, baseline); | ||
| 870 | - bottom = math.max( | ||
| 871 | - bottom, | ||
| 872 | - ws.height + baseline, | ||
| 873 | - ); | ||
| 874 | - | ||
| 875 | - ws.offset = PdfPoint(offsetX, -offsetY + baseline); | ||
| 876 | - _spans.add(ws); | ||
| 877 | - spanCount++; | ||
| 878 | - | ||
| 879 | - _appendDecoration( | ||
| 880 | - spanCount > 1, | ||
| 881 | - _TextDecoration( | ||
| 882 | - style, | ||
| 883 | - annotation, | ||
| 884 | - _spans.length - 1, | ||
| 885 | - _spans.length - 1, | ||
| 886 | - ), | ||
| 887 | - ); | ||
| 888 | - | ||
| 889 | - offsetX += ws.left + ws.width; | ||
| 890 | } | 1072 | } |
| 1073 | + } | ||
| 891 | 1074 | ||
| 892 | - return true; | ||
| 893 | - }, defaultstyle, null); | 1075 | + _buildLines(); |
| 894 | 1076 | ||
| 895 | if (spanCount > 0) { | 1077 | if (spanCount > 0) { |
| 896 | lines.add(_Line( | 1078 | lines.add(_Line( |
| @@ -111,6 +111,7 @@ class TextStyle { | @@ -111,6 +111,7 @@ class TextStyle { | ||
| 111 | Font? fontBold, | 111 | Font? fontBold, |
| 112 | Font? fontItalic, | 112 | Font? fontItalic, |
| 113 | Font? fontBoldItalic, | 113 | Font? fontBoldItalic, |
| 114 | + this.fontFallback = const [], | ||
| 114 | this.fontSize, | 115 | this.fontSize, |
| 115 | this.fontWeight, | 116 | this.fontWeight, |
| 116 | this.fontStyle, | 117 | this.fontStyle, |
| @@ -165,6 +166,7 @@ class TextStyle { | @@ -165,6 +166,7 @@ class TextStyle { | ||
| 165 | fontBold: Font.helveticaBold(), | 166 | fontBold: Font.helveticaBold(), |
| 166 | fontItalic: Font.helveticaOblique(), | 167 | fontItalic: Font.helveticaOblique(), |
| 167 | fontBoldItalic: Font.helveticaBoldOblique(), | 168 | fontBoldItalic: Font.helveticaBoldOblique(), |
| 169 | + fontFallback: const [], | ||
| 168 | fontSize: _defaultFontSize, | 170 | fontSize: _defaultFontSize, |
| 169 | fontWeight: FontWeight.normal, | 171 | fontWeight: FontWeight.normal, |
| 170 | fontStyle: FontStyle.normal, | 172 | fontStyle: FontStyle.normal, |
| @@ -192,7 +194,10 @@ class TextStyle { | @@ -192,7 +194,10 @@ class TextStyle { | ||
| 192 | 194 | ||
| 193 | final Font? fontBoldItalic; | 195 | final Font? fontBoldItalic; |
| 194 | 196 | ||
| 195 | - // font height, in pdf unit | 197 | + /// The ordered list of font to fall back on when a glyph cannot be found in a higher priority font. |
| 198 | + final List<Font> fontFallback; | ||
| 199 | + | ||
| 200 | + /// font height, in pdf unit | ||
| 196 | final double? fontSize; | 201 | final double? fontSize; |
| 197 | 202 | ||
| 198 | /// The typeface thickness to use when painting the text (e.g., bold). | 203 | /// The typeface thickness to use when painting the text (e.g., bold). |
| @@ -233,6 +238,7 @@ class TextStyle { | @@ -233,6 +238,7 @@ class TextStyle { | ||
| 233 | Font? fontBold, | 238 | Font? fontBold, |
| 234 | Font? fontItalic, | 239 | Font? fontItalic, |
| 235 | Font? fontBoldItalic, | 240 | Font? fontBoldItalic, |
| 241 | + List<Font>? fontFallback, | ||
| 236 | double? fontSize, | 242 | double? fontSize, |
| 237 | FontWeight? fontWeight, | 243 | FontWeight? fontWeight, |
| 238 | FontStyle? fontStyle, | 244 | FontStyle? fontStyle, |
| @@ -255,6 +261,7 @@ class TextStyle { | @@ -255,6 +261,7 @@ class TextStyle { | ||
| 255 | fontBold: fontBold ?? this.fontBold, | 261 | fontBold: fontBold ?? this.fontBold, |
| 256 | fontItalic: fontItalic ?? this.fontItalic, | 262 | fontItalic: fontItalic ?? this.fontItalic, |
| 257 | fontBoldItalic: fontBoldItalic ?? this.fontBoldItalic, | 263 | fontBoldItalic: fontBoldItalic ?? this.fontBoldItalic, |
| 264 | + fontFallback: fontFallback ?? this.fontFallback, | ||
| 258 | fontSize: fontSize ?? this.fontSize, | 265 | fontSize: fontSize ?? this.fontSize, |
| 259 | fontWeight: fontWeight ?? this.fontWeight, | 266 | fontWeight: fontWeight ?? this.fontWeight, |
| 260 | fontStyle: fontStyle ?? this.fontStyle, | 267 | fontStyle: fontStyle ?? this.fontStyle, |
| @@ -339,6 +346,7 @@ class TextStyle { | @@ -339,6 +346,7 @@ class TextStyle { | ||
| 339 | fontBold: other.fontBold, | 346 | fontBold: other.fontBold, |
| 340 | fontItalic: other.fontItalic, | 347 | fontItalic: other.fontItalic, |
| 341 | fontBoldItalic: other.fontBoldItalic, | 348 | fontBoldItalic: other.fontBoldItalic, |
| 349 | + fontFallback: [...other.fontFallback, ...fontFallback], | ||
| 342 | fontSize: other.fontSize, | 350 | fontSize: other.fontSize, |
| 343 | fontWeight: other.fontWeight, | 351 | fontWeight: other.fontWeight, |
| 344 | fontStyle: other.fontStyle, | 352 | fontStyle: other.fontStyle, |
| @@ -23,6 +23,8 @@ import 'text.dart'; | @@ -23,6 +23,8 @@ import 'text.dart'; | ||
| 23 | import 'text_style.dart'; | 23 | import 'text_style.dart'; |
| 24 | import 'widget.dart'; | 24 | import 'widget.dart'; |
| 25 | 25 | ||
| 26 | +typedef DefaultThemeDataBuilder = ThemeData Function(); | ||
| 27 | + | ||
| 26 | @immutable | 28 | @immutable |
| 27 | class ThemeData extends Inherited { | 29 | class ThemeData extends Inherited { |
| 28 | factory ThemeData({ | 30 | factory ThemeData({ |
| @@ -100,6 +102,7 @@ class ThemeData extends Inherited { | @@ -100,6 +102,7 @@ class ThemeData extends Inherited { | ||
| 100 | Font? italic, | 102 | Font? italic, |
| 101 | Font? boldItalic, | 103 | Font? boldItalic, |
| 102 | Font? icons, | 104 | Font? icons, |
| 105 | + List<Font>? fontFallback, | ||
| 103 | }) { | 106 | }) { |
| 104 | final defaultStyle = TextStyle.defaultStyle().copyWith( | 107 | final defaultStyle = TextStyle.defaultStyle().copyWith( |
| 105 | font: base, | 108 | font: base, |
| @@ -107,6 +110,7 @@ class ThemeData extends Inherited { | @@ -107,6 +110,7 @@ class ThemeData extends Inherited { | ||
| 107 | fontBold: bold, | 110 | fontBold: bold, |
| 108 | fontItalic: italic, | 111 | fontItalic: italic, |
| 109 | fontBoldItalic: boldItalic, | 112 | fontBoldItalic: boldItalic, |
| 113 | + fontFallback: fontFallback, | ||
| 110 | ); | 114 | ); |
| 111 | final fontSize = defaultStyle.fontSize!; | 115 | final fontSize = defaultStyle.fontSize!; |
| 112 | 116 | ||
| @@ -130,7 +134,34 @@ class ThemeData extends Inherited { | @@ -130,7 +134,34 @@ class ThemeData extends Inherited { | ||
| 130 | ); | 134 | ); |
| 131 | } | 135 | } |
| 132 | 136 | ||
| 133 | - factory ThemeData.base() => ThemeData.withFont(); | 137 | + factory ThemeData.base() => |
| 138 | + buildThemeData == null ? ThemeData.withFont() : buildThemeData!(); | ||
| 139 | + | ||
| 140 | + static DefaultThemeDataBuilder? buildThemeData; | ||
| 141 | + | ||
| 142 | + final TextStyle defaultTextStyle; | ||
| 143 | + | ||
| 144 | + final TextStyle paragraphStyle; | ||
| 145 | + | ||
| 146 | + final TextStyle header0; | ||
| 147 | + final TextStyle header1; | ||
| 148 | + final TextStyle header2; | ||
| 149 | + final TextStyle header3; | ||
| 150 | + final TextStyle header4; | ||
| 151 | + final TextStyle header5; | ||
| 152 | + | ||
| 153 | + final TextStyle bulletStyle; | ||
| 154 | + | ||
| 155 | + final TextStyle tableHeader; | ||
| 156 | + | ||
| 157 | + final TextStyle tableCell; | ||
| 158 | + | ||
| 159 | + final TextAlign textAlign; | ||
| 160 | + final bool softWrap; | ||
| 161 | + final int? maxLines; | ||
| 162 | + final TextOverflow overflow; | ||
| 163 | + | ||
| 164 | + final IconThemeData iconTheme; | ||
| 134 | 165 | ||
| 135 | ThemeData copyWith({ | 166 | ThemeData copyWith({ |
| 136 | TextStyle? defaultTextStyle, | 167 | TextStyle? defaultTextStyle, |
| @@ -168,30 +199,6 @@ class ThemeData extends Inherited { | @@ -168,30 +199,6 @@ class ThemeData extends Inherited { | ||
| 168 | maxLines: maxLines ?? this.maxLines, | 199 | maxLines: maxLines ?? this.maxLines, |
| 169 | iconTheme: iconTheme ?? this.iconTheme, | 200 | iconTheme: iconTheme ?? this.iconTheme, |
| 170 | ); | 201 | ); |
| 171 | - | ||
| 172 | - final TextStyle defaultTextStyle; | ||
| 173 | - | ||
| 174 | - final TextStyle paragraphStyle; | ||
| 175 | - | ||
| 176 | - final TextStyle header0; | ||
| 177 | - final TextStyle header1; | ||
| 178 | - final TextStyle header2; | ||
| 179 | - final TextStyle header3; | ||
| 180 | - final TextStyle header4; | ||
| 181 | - final TextStyle header5; | ||
| 182 | - | ||
| 183 | - final TextStyle bulletStyle; | ||
| 184 | - | ||
| 185 | - final TextStyle tableHeader; | ||
| 186 | - | ||
| 187 | - final TextStyle tableCell; | ||
| 188 | - | ||
| 189 | - final TextAlign textAlign; | ||
| 190 | - final bool softWrap; | ||
| 191 | - final int? maxLines; | ||
| 192 | - final TextOverflow overflow; | ||
| 193 | - | ||
| 194 | - final IconThemeData iconTheme; | ||
| 195 | } | 202 | } |
| 196 | 203 | ||
| 197 | class Theme extends StatelessWidget { | 204 | class Theme extends StatelessWidget { |
| @@ -24,9 +24,9 @@ import 'package:test/test.dart'; | @@ -24,9 +24,9 @@ import 'package:test/test.dart'; | ||
| 24 | import 'utils.dart'; | 24 | import 'utils.dart'; |
| 25 | 25 | ||
| 26 | late Document pdf; | 26 | late Document pdf; |
| 27 | -Font? ttf; | ||
| 28 | -Font? ttfBold; | ||
| 29 | -Font? asian; | 27 | +late Font ttf; |
| 28 | +late Font ttfBold; | ||
| 29 | +late Font asian; | ||
| 30 | 30 | ||
| 31 | Iterable<TextDecoration> permute( | 31 | Iterable<TextDecoration> permute( |
| 32 | List<TextDecoration> prefix, List<TextDecoration> remaining) sync* { | 32 | List<TextDecoration> prefix, List<TextDecoration> remaining) sync* { |
No preview for this file type
-
Please register or login to post a comment