Showing
8 changed files
with
220 additions
and
17 deletions
@@ -18,7 +18,7 @@ DART_BIN=$(FLUTTER)/bin/dart | @@ -18,7 +18,7 @@ DART_BIN=$(FLUTTER)/bin/dart | ||
18 | DART_SRC=$(shell find . -name '*.dart') | 18 | DART_SRC=$(shell find . -name '*.dart') |
19 | CLNG_SRC=$(shell find printing/ios printing/macos printing/windows printing/linux printing/android -name '*.cpp' -o -name '*.cc' -o -name '*.m' -o -name '*.h' -o -name '*.java') | 19 | CLNG_SRC=$(shell find printing/ios printing/macos printing/windows printing/linux printing/android -name '*.cpp' -o -name '*.cc' -o -name '*.m' -o -name '*.h' -o -name '*.java') |
20 | SWFT_SRC=$(shell find printing/ios printing/macos -name '*.swift') | 20 | SWFT_SRC=$(shell find printing/ios printing/macos -name '*.swift') |
21 | -FONTS=pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf pdf/hacen-tunisia.ttf pdf/material.ttf | 21 | +FONTS=pdf/open-sans.ttf pdf/open-sans-bold.ttf pdf/roboto.ttf pdf/noto-sans.ttf pdf/genyomintw.ttf pdf/hacen-tunisia.ttf pdf/material.ttf pdf/emoji.ttf |
22 | COV_PORT=9292 | 22 | COV_PORT=9292 |
23 | SVG=blend_and_mask blend_mode_devil clip_path clip_path_2 clip_path_2 clip_path_3 clip_path_3 dash_path ellipse empty_defs equation fill-rule-inherit group_composite_opacity group_fill_opacity group_mask group_opacity group_opacity_transform hidden href-fill image image_def implicit_fill_with_opacity linear_gradient linear_gradient_2 linear_gradient_absolute_user_space_translate linear_gradient_percentage_bounding_translate linear_gradient_percentage_user_space_translate linear_gradient_xlink male mask mask_with_gradient mask_with_use mask_with_use2 nested_group opacity_on_path radial_gradient radial_gradient_absolute_user_space_translate radial_gradient_focal radial_gradient_percentage_bounding_translate radial_gradient_percentage_user_space_translate radial_gradient_xlink radial_ref_linear_gradient rect_rrect rect_rrect_no_ry stroke_inherit_circles style_attr text text_2 text_3 use_circles use_circles_def use_emc2 use_fill use_opacity_grid width_height_viewbox flutter_logo emoji_u1f600 text_transform dart new-pause-button new-send-circle new-gif new-camera new-image numeric_25 new-mention new-gif-button new-action-expander new-play-button aa alphachannel Ghostscript_Tiger Firefox_Logo_2017 chess_knight Flag_of_the_United_States | 23 | SVG=blend_and_mask blend_mode_devil clip_path clip_path_2 clip_path_2 clip_path_3 clip_path_3 dash_path ellipse empty_defs equation fill-rule-inherit group_composite_opacity group_fill_opacity group_mask group_opacity group_opacity_transform hidden href-fill image image_def implicit_fill_with_opacity linear_gradient linear_gradient_2 linear_gradient_absolute_user_space_translate linear_gradient_percentage_bounding_translate linear_gradient_percentage_user_space_translate linear_gradient_xlink male mask mask_with_gradient mask_with_use mask_with_use2 nested_group opacity_on_path radial_gradient radial_gradient_absolute_user_space_translate radial_gradient_focal radial_gradient_percentage_bounding_translate radial_gradient_percentage_user_space_translate radial_gradient_xlink radial_ref_linear_gradient rect_rrect rect_rrect_no_ry stroke_inherit_circles style_attr text text_2 text_3 use_circles use_circles_def use_emc2 use_fill use_opacity_grid width_height_viewbox flutter_logo emoji_u1f600 text_transform dart new-pause-button new-send-circle new-gif new-camera new-image numeric_25 new-mention new-gif-button new-action-expander new-play-button aa alphachannel Ghostscript_Tiger Firefox_Logo_2017 chess_knight Flag_of_the_United_States |
24 | 24 | ||
@@ -42,6 +42,9 @@ pdf/genyomintw.ttf: | @@ -42,6 +42,9 @@ pdf/genyomintw.ttf: | ||
42 | pdf/material.ttf: | 42 | pdf/material.ttf: |
43 | curl -L "https://github.com/google/material-design-icons/raw/master/font/MaterialIcons-Regular.ttf" > $@ | 43 | curl -L "https://github.com/google/material-design-icons/raw/master/font/MaterialIcons-Regular.ttf" > $@ |
44 | 44 | ||
45 | +pdf/emoji.ttf: | ||
46 | + curl -L https://github.com/googlefonts/noto-emoji/raw/main/fonts/NotoColorEmoji.ttf > $@ | ||
47 | + | ||
45 | demo/assets/logo.svg: | 48 | demo/assets/logo.svg: |
46 | curl -L "http://pigment.github.io/fake-logos/logos/vector/color/auto-speed.svg" > $@ | 49 | curl -L "http://pigment.github.io/fake-logos/logos/vector/color/auto-speed.svg" > $@ |
47 | 50 |
@@ -68,6 +68,52 @@ class TtfGlyphInfo { | @@ -68,6 +68,52 @@ class TtfGlyphInfo { | ||
68 | String toString() => 'Glyph $index $compounds'; | 68 | String toString() => 'Glyph $index $compounds'; |
69 | } | 69 | } |
70 | 70 | ||
71 | +class TtfBitmapInfo { | ||
72 | + const TtfBitmapInfo( | ||
73 | + this.data, | ||
74 | + this.height, | ||
75 | + this.width, | ||
76 | + this.horiBearingX, | ||
77 | + this.horiBearingY, | ||
78 | + this.horiAdvance, | ||
79 | + this.vertBearingX, | ||
80 | + this.vertBearingY, | ||
81 | + this.vertAdvance, | ||
82 | + this.ascent, | ||
83 | + this.descent, | ||
84 | + ); | ||
85 | + | ||
86 | + final Uint8List data; | ||
87 | + final int height; | ||
88 | + final int width; | ||
89 | + final int horiBearingX; | ||
90 | + final int horiBearingY; | ||
91 | + final int horiAdvance; | ||
92 | + final int vertBearingX; | ||
93 | + final int vertBearingY; | ||
94 | + final int vertAdvance; | ||
95 | + final int ascent; | ||
96 | + final int descent; | ||
97 | + | ||
98 | + PdfFontMetrics get metrics { | ||
99 | + final coef = 1.0 / height; | ||
100 | + return PdfFontMetrics( | ||
101 | + bottom: horiBearingY * coef, | ||
102 | + left: horiBearingX * coef, | ||
103 | + top: horiBearingY * coef - height * coef, | ||
104 | + right: horiAdvance * coef, | ||
105 | + ascent: ascent * coef, | ||
106 | + descent: horiBearingY * coef, | ||
107 | + advanceWidth: horiAdvance * coef, | ||
108 | + leftBearing: horiBearingX * coef, | ||
109 | + ); | ||
110 | + } | ||
111 | + | ||
112 | + @override | ||
113 | + String toString() => | ||
114 | + 'Bitmap Glyph ${width}x$height horiBearingX:$horiBearingX horiBearingY:$horiBearingY horiAdvance:$horiAdvance ascender:$ascent descender:$descent'; | ||
115 | +} | ||
116 | + | ||
71 | class TtfParser { | 117 | class TtfParser { |
72 | TtfParser(ByteData bytes) : bytes = UnmodifiableByteDataView(bytes) { | 118 | TtfParser(ByteData bytes) : bytes = UnmodifiableByteDataView(bytes) { |
73 | final numTables = bytes.getUint16(4); | 119 | final numTables = bytes.getUint16(4); |
@@ -92,14 +138,17 @@ class TtfParser { | @@ -92,14 +138,17 @@ class TtfParser { | ||
92 | 'Unable to find the `cmap` table. This file is not a supported TTF font'); | 138 | 'Unable to find the `cmap` table. This file is not a supported TTF font'); |
93 | assert(tableOffsets.containsKey(maxp_table), | 139 | assert(tableOffsets.containsKey(maxp_table), |
94 | 'Unable to find the `maxp` table. This file is not a supported TTF font'); | 140 | 'Unable to find the `maxp` table. This file is not a supported TTF font'); |
95 | - assert(tableOffsets.containsKey(loca_table), | ||
96 | - 'Unable to find the `loca` table. This file is not a supported TTF font'); | ||
97 | - assert(tableOffsets.containsKey(glyf_table), | ||
98 | - 'Unable to find the `glyf` table. This file is not a supported TTF font'); | ||
99 | 141 | ||
100 | _parseCMap(); | 142 | _parseCMap(); |
101 | - _parseIndexes(); | ||
102 | - _parseGlyphs(); | 143 | + if (tableOffsets.containsKey(loca_table) && |
144 | + tableOffsets.containsKey(glyf_table)) { | ||
145 | + _parseIndexes(); | ||
146 | + _parseGlyphs(); | ||
147 | + } | ||
148 | + if (tableOffsets.containsKey(cblc_table) && | ||
149 | + tableOffsets.containsKey(cbdt_table)) { | ||
150 | + _parseBitmaps(); | ||
151 | + } | ||
103 | } | 152 | } |
104 | 153 | ||
105 | static const String head_table = 'head'; | 154 | static const String head_table = 'head'; |
@@ -110,14 +159,17 @@ class TtfParser { | @@ -110,14 +159,17 @@ class TtfParser { | ||
110 | static const String maxp_table = 'maxp'; | 159 | static const String maxp_table = 'maxp'; |
111 | static const String loca_table = 'loca'; | 160 | static const String loca_table = 'loca'; |
112 | static const String glyf_table = 'glyf'; | 161 | static const String glyf_table = 'glyf'; |
162 | + static const String cblc_table = 'CBLC'; | ||
163 | + static const String cbdt_table = 'CBDT'; | ||
113 | 164 | ||
114 | final UnmodifiableByteDataView bytes; | 165 | final UnmodifiableByteDataView bytes; |
115 | - final Map<String, int> tableOffsets = <String, int>{}; | ||
116 | - final Map<String, int> tableSize = <String, int>{}; | 166 | + final tableOffsets = <String, int>{}; |
167 | + final tableSize = <String, int>{}; | ||
117 | 168 | ||
118 | - final Map<int, int> charToGlyphIndexMap = <int, int>{}; | ||
119 | - final List<int> glyphOffsets = <int>[]; | ||
120 | - final Map<int, PdfFontMetrics> glyphInfoMap = <int, PdfFontMetrics>{}; | 169 | + final charToGlyphIndexMap = <int, int>{}; |
170 | + final glyphOffsets = <int>[]; | ||
171 | + final glyphInfoMap = <int, PdfFontMetrics>{}; | ||
172 | + final bitmapOffsets = <int, TtfBitmapInfo>{}; | ||
121 | 173 | ||
122 | int get unitsPerEm => bytes.getUint16(tableOffsets[head_table]! + 18); | 174 | int get unitsPerEm => bytes.getUint16(tableOffsets[head_table]! + 18); |
123 | 175 | ||
@@ -145,9 +197,14 @@ class TtfParser { | @@ -145,9 +197,14 @@ class TtfParser { | ||
145 | 197 | ||
146 | bool get unicode => bytes.getUint32(0) == 0x10000; | 198 | bool get unicode => bytes.getUint32(0) == 0x10000; |
147 | 199 | ||
200 | + bool get isBitmap => bitmapOffsets.isNotEmpty && glyphOffsets.isEmpty; | ||
201 | + | ||
148 | // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html | 202 | // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html |
149 | String? getNameID(TtfParserName fontNameID) { | 203 | String? getNameID(TtfParserName fontNameID) { |
150 | - final basePosition = tableOffsets[name_table]!; | 204 | + final basePosition = tableOffsets[name_table]; |
205 | + if (basePosition == null) { | ||
206 | + return null; | ||
207 | + } | ||
151 | // final format = bytes.getUint16(basePosition); | 208 | // final format = bytes.getUint16(basePosition); |
152 | final count = bytes.getUint16(basePosition + 2); | 209 | final count = bytes.getUint16(basePosition + 2); |
153 | final stringOffset = bytes.getUint16(basePosition + 4); | 210 | final stringOffset = bytes.getUint16(basePosition + 4); |
@@ -288,15 +345,15 @@ class TtfParser { | @@ -288,15 +345,15 @@ class TtfParser { | ||
288 | } | 345 | } |
289 | 346 | ||
290 | void _parseIndexes() { | 347 | void _parseIndexes() { |
291 | - final basePosition = tableOffsets[loca_table]; | 348 | + final basePosition = tableOffsets[loca_table]!; |
292 | final numGlyphs = this.numGlyphs; | 349 | final numGlyphs = this.numGlyphs; |
293 | if (indexToLocFormat == 0) { | 350 | if (indexToLocFormat == 0) { |
294 | for (var i = 0; i < numGlyphs; i++) { | 351 | for (var i = 0; i < numGlyphs; i++) { |
295 | - glyphOffsets.add(bytes.getUint16(basePosition! + i * 2) * 2); | 352 | + glyphOffsets.add(bytes.getUint16(basePosition + i * 2) * 2); |
296 | } | 353 | } |
297 | } else { | 354 | } else { |
298 | for (var i = 0; i < numGlyphs; i++) { | 355 | for (var i = 0; i < numGlyphs; i++) { |
299 | - glyphOffsets.add(bytes.getUint32(basePosition! + i * 4)); | 356 | + glyphOffsets.add(bytes.getUint32(basePosition + i * 4)); |
300 | } | 357 | } |
301 | } | 358 | } |
302 | } | 359 | } |
@@ -465,4 +522,92 @@ class TtfParser { | @@ -465,4 +522,92 @@ class TtfParser { | ||
465 | } | 522 | } |
466 | return String.fromCharCodes(charCodes); | 523 | return String.fromCharCodes(charCodes); |
467 | } | 524 | } |
525 | + | ||
526 | + // https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt | ||
527 | + void _parseBitmaps() { | ||
528 | + final baseOffset = tableOffsets[cblc_table]!; | ||
529 | + final pngOffset = tableOffsets[cbdt_table]!; | ||
530 | + | ||
531 | + // CBLC Header | ||
532 | + final numSizes = bytes.getUint32(baseOffset + 4); | ||
533 | + var bitmapSize = baseOffset + 8; | ||
534 | + | ||
535 | + for (var bitmapSizeIndex = 0; | ||
536 | + bitmapSizeIndex < numSizes; | ||
537 | + bitmapSizeIndex++) { | ||
538 | + // BitmapSize Record | ||
539 | + final indexSubTableArrayOffset = baseOffset + bytes.getUint32(bitmapSize); | ||
540 | + // final indexTablesSize = bytes.getUint32(bitmapSize + 4); | ||
541 | + final numberOfIndexSubTables = bytes.getUint32(bitmapSize + 8); | ||
542 | + | ||
543 | + final ascender = bytes.getInt8(bitmapSize + 12); | ||
544 | + final descender = bytes.getInt8(bitmapSize + 13); | ||
545 | + | ||
546 | + // final startGlyphIndex = bytes.getUint16(bitmapSize + 16 + 12 * 2); | ||
547 | + // final endGlyphIndex = bytes.getUint16(bitmapSize + 16 + 12 * 2 + 2); | ||
548 | + // final ppemX = bytes.getUint8(bitmapSize + 16 + 12 * 2 + 4); | ||
549 | + // final ppemY = bytes.getUint8(bitmapSize + 16 + 12 * 2 + 5); | ||
550 | + // final bitDepth = bytes.getUint8(bitmapSize + 16 + 12 * 2 + 6); | ||
551 | + // final flags = bytes.getUint8(bitmapSize + 16 + 12 * 2 + 7); | ||
552 | + | ||
553 | + var subTableArrayOffset = indexSubTableArrayOffset; | ||
554 | + for (var indexSubTable = 0; | ||
555 | + indexSubTable < numberOfIndexSubTables; | ||
556 | + indexSubTable++) { | ||
557 | + // IndexSubTableArray | ||
558 | + final firstGlyphIndex = bytes.getUint16(subTableArrayOffset); | ||
559 | + final lastGlyphIndex = bytes.getUint16(subTableArrayOffset + 2); | ||
560 | + final additionalOffsetToIndexSubtable = | ||
561 | + indexSubTableArrayOffset + bytes.getUint32(subTableArrayOffset + 4); | ||
562 | + | ||
563 | + // IndexSubHeader | ||
564 | + final indexFormat = bytes.getUint16(additionalOffsetToIndexSubtable); | ||
565 | + final imageFormat = | ||
566 | + bytes.getUint16(additionalOffsetToIndexSubtable + 2); | ||
567 | + final imageDataOffset = | ||
568 | + pngOffset + bytes.getUint32(additionalOffsetToIndexSubtable + 4); | ||
569 | + | ||
570 | + if (indexFormat == 1) { | ||
571 | + // IndexSubTable1 | ||
572 | + | ||
573 | + for (var glyph = firstGlyphIndex; glyph <= lastGlyphIndex; glyph++) { | ||
574 | + final sbitOffset = imageDataOffset + | ||
575 | + bytes.getUint32(additionalOffsetToIndexSubtable + | ||
576 | + (glyph - firstGlyphIndex + 2) * 4); | ||
577 | + | ||
578 | + if (imageFormat == 17) { | ||
579 | + final height = bytes.getUint8(sbitOffset); | ||
580 | + final width = bytes.getUint8(sbitOffset + 1); | ||
581 | + final bearingX = bytes.getInt8(sbitOffset + 2); | ||
582 | + final bearingY = bytes.getInt8(sbitOffset + 3); | ||
583 | + final advance = bytes.getUint8(sbitOffset + 4); | ||
584 | + final dataLen = bytes.getUint32(sbitOffset + 5); | ||
585 | + | ||
586 | + bitmapOffsets[glyph] = TtfBitmapInfo( | ||
587 | + bytes.buffer.asUint8List( | ||
588 | + bytes.offsetInBytes + sbitOffset + 9, | ||
589 | + dataLen, | ||
590 | + ), | ||
591 | + height, | ||
592 | + width, | ||
593 | + bearingX, | ||
594 | + bearingY, | ||
595 | + advance, | ||
596 | + 0, | ||
597 | + 0, | ||
598 | + 0, | ||
599 | + ascender, | ||
600 | + descender); | ||
601 | + } | ||
602 | + } | ||
603 | + } | ||
604 | + | ||
605 | + subTableArrayOffset += 8; | ||
606 | + } | ||
607 | + bitmapSize += 16 + 12 * 2 + 8; | ||
608 | + } | ||
609 | + } | ||
610 | + | ||
611 | + TtfBitmapInfo? getBitmap(int charcode) => | ||
612 | + bitmapOffsets[charToGlyphIndexMap[charcode]]; | ||
468 | } | 613 | } |
@@ -23,7 +23,7 @@ class PdfUnicodeCmap extends PdfObjectStream { | @@ -23,7 +23,7 @@ class PdfUnicodeCmap extends PdfObjectStream { | ||
23 | PdfUnicodeCmap(PdfDocument pdfDocument, this.protect) : super(pdfDocument); | 23 | PdfUnicodeCmap(PdfDocument pdfDocument, this.protect) : super(pdfDocument); |
24 | 24 | ||
25 | /// List of characters | 25 | /// List of characters |
26 | - final List<int> cmap = <int>[0]; | 26 | + final cmap = <int>[]; |
27 | 27 | ||
28 | /// Protects the text from being "seen" by the PDF reader. | 28 | /// Protects the text from being "seen" by the PDF reader. |
29 | final bool protect; | 29 | final bool protect; |
@@ -24,6 +24,8 @@ import 'annotations.dart'; | @@ -24,6 +24,8 @@ import 'annotations.dart'; | ||
24 | import 'basic.dart'; | 24 | import 'basic.dart'; |
25 | import 'document.dart'; | 25 | import 'document.dart'; |
26 | import 'geometry.dart'; | 26 | import 'geometry.dart'; |
27 | +import 'image.dart'; | ||
28 | +import 'image_provider.dart'; | ||
27 | import 'multi_page.dart'; | 29 | import 'multi_page.dart'; |
28 | import 'placeholders.dart'; | 30 | import 'placeholders.dart'; |
29 | import 'text_style.dart'; | 31 | import 'text_style.dart'; |
@@ -690,6 +692,25 @@ class RichText extends Widget with SpanningWidget { | @@ -690,6 +692,25 @@ class RichText extends Widget with SpanningWidget { | ||
690 | _decorations.add(td); | 692 | _decorations.add(td); |
691 | } | 693 | } |
692 | 694 | ||
695 | + InlineSpan _addEmoji({ | ||
696 | + required TtfBitmapInfo bitmap, | ||
697 | + double baseline = 0, | ||
698 | + required TextStyle style, | ||
699 | + AnnotationBuilder? annotation, | ||
700 | + }) { | ||
701 | + final metrics = bitmap.metrics * style.fontSize!; | ||
702 | + | ||
703 | + return WidgetSpan( | ||
704 | + child: SizedBox( | ||
705 | + height: style.fontSize, | ||
706 | + child: Image(MemoryImage(bitmap.data)), | ||
707 | + ), | ||
708 | + style: style, | ||
709 | + baseline: baseline + metrics.ascent + metrics.descent - metrics.height, | ||
710 | + annotation: annotation, | ||
711 | + ); | ||
712 | + } | ||
713 | + | ||
693 | InlineSpan _addText({ | 714 | InlineSpan _addText({ |
694 | required List<int> text, | 715 | required List<int> text, |
695 | int start = 0, | 716 | int start = 0, |
@@ -770,6 +791,19 @@ class RichText extends Widget with SpanningWidget { | @@ -770,6 +791,19 @@ class RichText extends Widget with SpanningWidget { | ||
770 | for (final fb in style.fontFallback) { | 791 | for (final fb in style.fontFallback) { |
771 | final font = fb.getFont(context); | 792 | final font = fb.getFont(context); |
772 | if (font.isRuneSupported(rune)) { | 793 | if (font.isRuneSupported(rune)) { |
794 | + if (font is PdfTtfFont) { | ||
795 | + final bitmap = font.font.getBitmap(rune); | ||
796 | + if (bitmap != null) { | ||
797 | + spans.add(_addEmoji( | ||
798 | + bitmap: bitmap, | ||
799 | + style: style, | ||
800 | + baseline: span.baseline, | ||
801 | + annotation: annotation, | ||
802 | + )); | ||
803 | + found = true; | ||
804 | + break; | ||
805 | + } | ||
806 | + } | ||
773 | spans.add(_addText( | 807 | spans.add(_addText( |
774 | text: [rune], | 808 | text: [rune], |
775 | style: style.copyWith( | 809 | style: style.copyWith( |
@@ -27,6 +27,7 @@ late Document pdf; | @@ -27,6 +27,7 @@ late Document pdf; | ||
27 | late Font ttf; | 27 | late Font ttf; |
28 | late Font ttfBold; | 28 | late Font ttfBold; |
29 | late Font asian; | 29 | late Font asian; |
30 | +late Font emoji; | ||
30 | 31 | ||
31 | Iterable<TextDecoration> permute( | 32 | Iterable<TextDecoration> permute( |
32 | List<TextDecoration> prefix, List<TextDecoration> remaining) sync* { | 33 | List<TextDecoration> prefix, List<TextDecoration> remaining) sync* { |
@@ -48,6 +49,7 @@ void main() { | @@ -48,6 +49,7 @@ void main() { | ||
48 | ttf = loadFont('open-sans.ttf'); | 49 | ttf = loadFont('open-sans.ttf'); |
49 | ttfBold = loadFont('open-sans-bold.ttf'); | 50 | ttfBold = loadFont('open-sans-bold.ttf'); |
50 | asian = loadFont('genyomintw.ttf'); | 51 | asian = loadFont('genyomintw.ttf'); |
52 | + emoji = loadFont('emoji.ttf'); | ||
51 | pdf = Document(); | 53 | pdf = Document(); |
52 | }); | 54 | }); |
53 | 55 | ||
@@ -357,6 +359,20 @@ void main() { | @@ -357,6 +359,20 @@ void main() { | ||
357 | ); | 359 | ); |
358 | }); | 360 | }); |
359 | 361 | ||
362 | + test('Text Widgets Emojis', () { | ||
363 | + pdf.addPage( | ||
364 | + Page( | ||
365 | + build: (Context context) => Text( | ||
366 | + 'Hello 🐈! Dancing 💃🏃', | ||
367 | + style: TextStyle( | ||
368 | + fontSize: 30, | ||
369 | + fontFallback: [emoji], | ||
370 | + ), | ||
371 | + ), | ||
372 | + ), | ||
373 | + ); | ||
374 | + }); | ||
375 | + | ||
360 | tearDownAll(() async { | 376 | tearDownAll(() async { |
361 | final file = File('widgets-text.pdf'); | 377 | final file = File('widgets-text.pdf'); |
362 | await file.writeAsBytes(await pdf.save()); | 378 | await file.writeAsBytes(await pdf.save()); |
@@ -128,8 +128,12 @@ void main(List<String> args) async { | @@ -128,8 +128,12 @@ void main(List<String> args) async { | ||
128 | } | 128 | } |
129 | 129 | ||
130 | for (final entry in <String, String>{ | 130 | for (final entry in <String, String>{ |
131 | + 'CupertinoIcons': | ||
132 | + 'https://github.com/flutter/packages/blob/master/third_party/packages/cupertino_icons/assets/CupertinoIcons.ttf', | ||
131 | 'MaterialIcons': | 133 | 'MaterialIcons': |
132 | 'https://fonts.gstatic.com/s/materialicons/v98/flUhRq6tzZclQEJ-Vdg-IuiaDsNZ.ttf', | 134 | 'https://fonts.gstatic.com/s/materialicons/v98/flUhRq6tzZclQEJ-Vdg-IuiaDsNZ.ttf', |
135 | + 'NotoColorEmoji': | ||
136 | + 'https://github.com/googlefonts/noto-emoji/raw/main/fonts/NotoColorEmoji.ttf', | ||
133 | }.entries) { | 137 | }.entries) { |
134 | output.writeln(''); | 138 | output.writeln(''); |
135 | output.writeln('/// ${entry.key}'); | 139 | output.writeln('/// ${entry.key}'); |
No preview for this file type
-
Please register or login to post a comment