mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
fix infinite loop in findMinimumFontsForCodeunits (flutter/engine#24441)
This commit is contained in:
parent
adbe5b3cd7
commit
04067ddd57
@ -12,7 +12,7 @@ bool _registeredSymbolsAndEmoji = false;
|
||||
|
||||
final Set<int> codeUnitsWithNoKnownFont = <int>{};
|
||||
|
||||
Future<void> _findFontsForMissingCodeunits(List<int> codeunits) async {
|
||||
Future<void> findFontsForMissingCodeunits(List<int> codeunits) async {
|
||||
_ensureNotoFontTreeCreated();
|
||||
|
||||
// If all of the code units are known to have no Noto Font which covers them,
|
||||
@ -20,11 +20,11 @@ Future<void> _findFontsForMissingCodeunits(List<int> codeunits) async {
|
||||
if (codeunits.every((u) => codeUnitsWithNoKnownFont.contains(u))) {
|
||||
return;
|
||||
}
|
||||
Set<_NotoFont> fonts = <_NotoFont>{};
|
||||
Set<NotoFont> fonts = <NotoFont>{};
|
||||
Set<int> coveredCodeUnits = <int>{};
|
||||
Set<int> missingCodeUnits = <int>{};
|
||||
for (int codeunit in codeunits) {
|
||||
List<_NotoFont> fontsForUnit = _notoTree!.intersections(codeunit);
|
||||
List<NotoFont> fontsForUnit = _notoTree!.intersections(codeunit);
|
||||
fonts.addAll(fontsForUnit);
|
||||
if (fontsForUnit.isNotEmpty) {
|
||||
coveredCodeUnits.add(codeunit);
|
||||
@ -33,15 +33,15 @@ Future<void> _findFontsForMissingCodeunits(List<int> codeunits) async {
|
||||
}
|
||||
}
|
||||
|
||||
fonts = _findMinimumFontsForCodeunits(coveredCodeUnits, fonts);
|
||||
fonts = findMinimumFontsForCodeunits(coveredCodeUnits, fonts);
|
||||
|
||||
for (_NotoFont font in fonts) {
|
||||
for (NotoFont font in fonts) {
|
||||
await font.ensureResolved();
|
||||
}
|
||||
|
||||
Set<_ResolvedNotoSubset> resolvedFonts = <_ResolvedNotoSubset>{};
|
||||
for (int codeunit in coveredCodeUnits) {
|
||||
for (_NotoFont font in fonts) {
|
||||
for (NotoFont font in fonts) {
|
||||
if (font.resolvedFont == null) {
|
||||
// We failed to resolve the font earlier.
|
||||
continue;
|
||||
@ -232,18 +232,20 @@ Future<void> _registerSymbolsAndEmoji() async {
|
||||
/// which finds the font which covers the most codeunits. If multiple CJK
|
||||
/// fonts match the same number of codeunits, we choose one based on the user's
|
||||
/// locale.
|
||||
Set<_NotoFont> _findMinimumFontsForCodeunits(
|
||||
Iterable<int> codeunits, Set<_NotoFont> fonts) {
|
||||
Set<NotoFont> findMinimumFontsForCodeunits(
|
||||
Iterable<int> codeunits, Set<NotoFont> fonts) {
|
||||
assert(fonts.isNotEmpty || codeunits.isEmpty);
|
||||
List<int> unmatchedCodeunits = List<int>.from(codeunits);
|
||||
Set<_NotoFont> minimumFonts = <_NotoFont>{};
|
||||
List<_NotoFont> bestFonts = <_NotoFont>[];
|
||||
int maxCodeunitsCovered = 0;
|
||||
Set<NotoFont> minimumFonts = <NotoFont>{};
|
||||
List<NotoFont> bestFonts = <NotoFont>[];
|
||||
|
||||
String language = html.window.navigator.language;
|
||||
|
||||
// This is guaranteed to terminate because [codeunits] is a list of fonts
|
||||
// which we've already determined are covered by [fonts].
|
||||
while (unmatchedCodeunits.isNotEmpty) {
|
||||
int maxCodeunitsCovered = 0;
|
||||
bestFonts.clear();
|
||||
for (var font in fonts) {
|
||||
int codeunitsCovered = 0;
|
||||
for (int codeunit in unmatchedCodeunits) {
|
||||
@ -259,10 +261,13 @@ Set<_NotoFont> _findMinimumFontsForCodeunits(
|
||||
bestFonts.add(font);
|
||||
}
|
||||
}
|
||||
assert(bestFonts.isNotEmpty);
|
||||
assert(
|
||||
bestFonts.isNotEmpty,
|
||||
'Did not find any fonts that cover code units: ${unmatchedCodeunits.join(', ')}',
|
||||
);
|
||||
// If the list of best fonts are all CJK fonts, choose the best one based
|
||||
// on locale. Otherwise just choose the first font.
|
||||
_NotoFont bestFont = bestFonts.first;
|
||||
NotoFont bestFont = bestFonts.first;
|
||||
if (bestFonts.length > 1) {
|
||||
if (bestFonts.every((font) => _cjkFonts.contains(font))) {
|
||||
if (language == 'zh-Hans' ||
|
||||
@ -301,19 +306,19 @@ void _ensureNotoFontTreeCreated() {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<_NotoFont, List<CodeunitRange>> ranges =
|
||||
<_NotoFont, List<CodeunitRange>>{};
|
||||
Map<NotoFont, List<CodeunitRange>> ranges =
|
||||
<NotoFont, List<CodeunitRange>>{};
|
||||
|
||||
for (_NotoFont font in _notoFonts) {
|
||||
for (NotoFont font in _notoFonts) {
|
||||
for (CodeunitRange range in font.unicodeRanges) {
|
||||
ranges.putIfAbsent(font, () => <CodeunitRange>[]).add(range);
|
||||
}
|
||||
}
|
||||
|
||||
_notoTree = IntervalTree<_NotoFont>.createFromRanges(ranges);
|
||||
_notoTree = IntervalTree<NotoFont>.createFromRanges(ranges);
|
||||
}
|
||||
|
||||
class _NotoFont {
|
||||
class NotoFont {
|
||||
final String name;
|
||||
final List<CodeunitRange> unicodeRanges;
|
||||
|
||||
@ -321,7 +326,7 @@ class _NotoFont {
|
||||
|
||||
_ResolvedNotoFont? resolvedFont;
|
||||
|
||||
_NotoFont(this.name, this.unicodeRanges);
|
||||
NotoFont(this.name, this.unicodeRanges);
|
||||
|
||||
bool matchesCodeunit(int codeunit) {
|
||||
for (CodeunitRange range in unicodeRanges) {
|
||||
@ -397,7 +402,7 @@ class _ResolvedNotoSubset {
|
||||
String toString() => '_ResolvedNotoSubset($family, $url)';
|
||||
}
|
||||
|
||||
_NotoFont _notoSansSC = _NotoFont('Noto Sans SC', <CodeunitRange>[
|
||||
NotoFont _notoSansSC = NotoFont('Noto Sans SC', <CodeunitRange>[
|
||||
CodeunitRange(12288, 12591),
|
||||
CodeunitRange(12800, 13311),
|
||||
CodeunitRange(19968, 40959),
|
||||
@ -405,37 +410,37 @@ _NotoFont _notoSansSC = _NotoFont('Noto Sans SC', <CodeunitRange>[
|
||||
CodeunitRange(65280, 65519),
|
||||
]);
|
||||
|
||||
_NotoFont _notoSansTC = _NotoFont('Noto Sans TC', <CodeunitRange>[
|
||||
NotoFont _notoSansTC = NotoFont('Noto Sans TC', <CodeunitRange>[
|
||||
CodeunitRange(12288, 12351),
|
||||
CodeunitRange(12549, 12585),
|
||||
CodeunitRange(19968, 40959),
|
||||
]);
|
||||
|
||||
_NotoFont _notoSansHK = _NotoFont('Noto Sans HK', <CodeunitRange>[
|
||||
NotoFont _notoSansHK = NotoFont('Noto Sans HK', <CodeunitRange>[
|
||||
CodeunitRange(12288, 12351),
|
||||
CodeunitRange(12549, 12585),
|
||||
CodeunitRange(19968, 40959),
|
||||
]);
|
||||
|
||||
_NotoFont _notoSansJP = _NotoFont('Noto Sans JP', <CodeunitRange>[
|
||||
NotoFont _notoSansJP = NotoFont('Noto Sans JP', <CodeunitRange>[
|
||||
CodeunitRange(12288, 12543),
|
||||
CodeunitRange(19968, 40959),
|
||||
CodeunitRange(65280, 65519),
|
||||
]);
|
||||
|
||||
List<_NotoFont> _cjkFonts = <_NotoFont>[
|
||||
List<NotoFont> _cjkFonts = <NotoFont>[
|
||||
_notoSansSC,
|
||||
_notoSansTC,
|
||||
_notoSansHK,
|
||||
_notoSansJP,
|
||||
];
|
||||
|
||||
List<_NotoFont> _notoFonts = <_NotoFont>[
|
||||
List<NotoFont> _notoFonts = <NotoFont>[
|
||||
_notoSansSC,
|
||||
_notoSansTC,
|
||||
_notoSansHK,
|
||||
_notoSansJP,
|
||||
_NotoFont('Noto Naskh Arabic UI', <CodeunitRange>[
|
||||
NotoFont('Noto Naskh Arabic UI', <CodeunitRange>[
|
||||
CodeunitRange(1536, 1791),
|
||||
CodeunitRange(8204, 8206),
|
||||
CodeunitRange(8208, 8209),
|
||||
@ -444,36 +449,36 @@ List<_NotoFont> _notoFonts = <_NotoFont>[
|
||||
CodeunitRange(64336, 65023),
|
||||
CodeunitRange(65132, 65276),
|
||||
]),
|
||||
_NotoFont('Noto Sans Armenian', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Armenian', <CodeunitRange>[
|
||||
CodeunitRange(1328, 1424),
|
||||
CodeunitRange(64275, 64279),
|
||||
]),
|
||||
_NotoFont('Noto Sans Bengali UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Bengali UI', <CodeunitRange>[
|
||||
CodeunitRange(2404, 2405),
|
||||
CodeunitRange(2433, 2555),
|
||||
CodeunitRange(8204, 8205),
|
||||
CodeunitRange(8377, 8377),
|
||||
CodeunitRange(9676, 9676),
|
||||
]),
|
||||
_NotoFont('Noto Sans Myanmar UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Myanmar UI', <CodeunitRange>[
|
||||
CodeunitRange(4096, 4255),
|
||||
CodeunitRange(8204, 8205),
|
||||
CodeunitRange(9676, 9676),
|
||||
]),
|
||||
_NotoFont('Noto Sans Egyptian Hieroglyphs', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Egyptian Hieroglyphs', <CodeunitRange>[
|
||||
CodeunitRange(77824, 78894),
|
||||
]),
|
||||
_NotoFont('Noto Sans Ethiopic', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Ethiopic', <CodeunitRange>[
|
||||
CodeunitRange(4608, 5017),
|
||||
CodeunitRange(11648, 11742),
|
||||
CodeunitRange(43777, 43822),
|
||||
]),
|
||||
_NotoFont('Noto Sans Georgian', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Georgian', <CodeunitRange>[
|
||||
CodeunitRange(1417, 1417),
|
||||
CodeunitRange(4256, 4351),
|
||||
CodeunitRange(11520, 11567),
|
||||
]),
|
||||
_NotoFont('Noto Sans Gujarati UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Gujarati UI', <CodeunitRange>[
|
||||
CodeunitRange(2404, 2405),
|
||||
CodeunitRange(2688, 2815),
|
||||
CodeunitRange(8204, 8205),
|
||||
@ -481,7 +486,7 @@ List<_NotoFont> _notoFonts = <_NotoFont>[
|
||||
CodeunitRange(9676, 9676),
|
||||
CodeunitRange(43056, 43065),
|
||||
]),
|
||||
_NotoFont('Noto Sans Gurmukhi UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Gurmukhi UI', <CodeunitRange>[
|
||||
CodeunitRange(2404, 2405),
|
||||
CodeunitRange(2561, 2677),
|
||||
CodeunitRange(8204, 8205),
|
||||
@ -490,13 +495,13 @@ List<_NotoFont> _notoFonts = <_NotoFont>[
|
||||
CodeunitRange(9772, 9772),
|
||||
CodeunitRange(43056, 43065),
|
||||
]),
|
||||
_NotoFont('Noto Sans Hebrew', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Hebrew', <CodeunitRange>[
|
||||
CodeunitRange(1424, 1535),
|
||||
CodeunitRange(8362, 8362),
|
||||
CodeunitRange(9676, 9676),
|
||||
CodeunitRange(64285, 64335),
|
||||
]),
|
||||
_NotoFont('Noto Sans Devanagari UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Devanagari UI', <CodeunitRange>[
|
||||
CodeunitRange(2304, 2431),
|
||||
CodeunitRange(7376, 7414),
|
||||
CodeunitRange(7416, 7417),
|
||||
@ -507,29 +512,29 @@ List<_NotoFont> _notoFonts = <_NotoFont>[
|
||||
CodeunitRange(43056, 43065),
|
||||
CodeunitRange(43232, 43259),
|
||||
]),
|
||||
_NotoFont('Noto Sans Kannada UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Kannada UI', <CodeunitRange>[
|
||||
CodeunitRange(2404, 2405),
|
||||
CodeunitRange(3202, 3314),
|
||||
CodeunitRange(8204, 8205),
|
||||
CodeunitRange(8377, 8377),
|
||||
CodeunitRange(9676, 9676),
|
||||
]),
|
||||
_NotoFont('Noto Sans Khmer UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Khmer UI', <CodeunitRange>[
|
||||
CodeunitRange(6016, 6143),
|
||||
CodeunitRange(8204, 8204),
|
||||
CodeunitRange(9676, 9676),
|
||||
]),
|
||||
_NotoFont('Noto Sans KR', <CodeunitRange>[
|
||||
NotoFont('Noto Sans KR', <CodeunitRange>[
|
||||
CodeunitRange(12593, 12686),
|
||||
CodeunitRange(12800, 12828),
|
||||
CodeunitRange(12896, 12923),
|
||||
CodeunitRange(44032, 55215),
|
||||
]),
|
||||
_NotoFont('Noto Sans Lao UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Lao UI', <CodeunitRange>[
|
||||
CodeunitRange(3713, 3807),
|
||||
CodeunitRange(9676, 9676),
|
||||
]),
|
||||
_NotoFont('Noto Sans Malayalam UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Malayalam UI', <CodeunitRange>[
|
||||
CodeunitRange(775, 775),
|
||||
CodeunitRange(803, 803),
|
||||
CodeunitRange(2404, 2405),
|
||||
@ -538,20 +543,20 @@ List<_NotoFont> _notoFonts = <_NotoFont>[
|
||||
CodeunitRange(8377, 8377),
|
||||
CodeunitRange(9676, 9676),
|
||||
]),
|
||||
_NotoFont('Noto Sans Sinhala', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Sinhala', <CodeunitRange>[
|
||||
CodeunitRange(2404, 2405),
|
||||
CodeunitRange(3458, 3572),
|
||||
CodeunitRange(8204, 8205),
|
||||
CodeunitRange(9676, 9676),
|
||||
]),
|
||||
_NotoFont('Noto Sans Tamil UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Tamil UI', <CodeunitRange>[
|
||||
CodeunitRange(2404, 2405),
|
||||
CodeunitRange(2946, 3066),
|
||||
CodeunitRange(8204, 8205),
|
||||
CodeunitRange(8377, 8377),
|
||||
CodeunitRange(9676, 9676),
|
||||
]),
|
||||
_NotoFont('Noto Sans Telugu UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Telugu UI', <CodeunitRange>[
|
||||
CodeunitRange(2385, 2386),
|
||||
CodeunitRange(2404, 2405),
|
||||
CodeunitRange(3072, 3199),
|
||||
@ -559,12 +564,12 @@ List<_NotoFont> _notoFonts = <_NotoFont>[
|
||||
CodeunitRange(8204, 8205),
|
||||
CodeunitRange(9676, 9676),
|
||||
]),
|
||||
_NotoFont('Noto Sans Thai UI', <CodeunitRange>[
|
||||
NotoFont('Noto Sans Thai UI', <CodeunitRange>[
|
||||
CodeunitRange(3585, 3675),
|
||||
CodeunitRange(8204, 8205),
|
||||
CodeunitRange(9676, 9676),
|
||||
]),
|
||||
_NotoFont('Noto Sans', <CodeunitRange>[
|
||||
NotoFont('Noto Sans', <CodeunitRange>[
|
||||
CodeunitRange(0, 255),
|
||||
CodeunitRange(305, 305),
|
||||
CodeunitRange(338, 339),
|
||||
@ -639,7 +644,7 @@ class FallbackFontDownloadQueue {
|
||||
downloads.add(Future<void>(() async {
|
||||
ByteBuffer buffer;
|
||||
try {
|
||||
buffer = await downloader.downloadAsBytes(subset.url);
|
||||
buffer = await downloader.downloadAsBytes(subset.url, debugDescription: subset.family);
|
||||
} catch (e) {
|
||||
html.window.console
|
||||
.warn('Failed to load font ${subset.family} at ${subset.url}');
|
||||
@ -695,7 +700,7 @@ class NotoDownloader {
|
||||
/// Downloads the [url] and returns it as a [ByteBuffer].
|
||||
///
|
||||
/// Override this for testing.
|
||||
Future<ByteBuffer> downloadAsBytes(String url) {
|
||||
Future<ByteBuffer> downloadAsBytes(String url, {String? debugDescription}) {
|
||||
if (assertionsEnabled) {
|
||||
_debugActiveDownloadCount += 1;
|
||||
}
|
||||
@ -714,7 +719,7 @@ class NotoDownloader {
|
||||
/// Downloads the [url] and returns is as a [String].
|
||||
///
|
||||
/// Override this for testing.
|
||||
Future<String> downloadAsString(String url) {
|
||||
Future<String> downloadAsString(String url, {String? debugDescription}) {
|
||||
if (assertionsEnabled) {
|
||||
_debugActiveDownloadCount += 1;
|
||||
}
|
||||
@ -731,6 +736,12 @@ class NotoDownloader {
|
||||
}
|
||||
|
||||
/// The Noto font interval tree.
|
||||
IntervalTree<_NotoFont>? _notoTree;
|
||||
IntervalTree<NotoFont>? _notoTree;
|
||||
|
||||
/// Returns the tree of Noto fonts for tests.
|
||||
IntervalTree<NotoFont> get debugNotoTree {
|
||||
_ensureNotoFontTreeCreated();
|
||||
return _notoTree!;
|
||||
}
|
||||
|
||||
FallbackFontDownloadQueue notoDownloadQueue = FallbackFontDownloadQueue();
|
||||
|
||||
@ -18,7 +18,7 @@ class IntervalTree<T> {
|
||||
/// have a range which contains the point.
|
||||
factory IntervalTree.createFromRanges(Map<T, List<CodeunitRange>> rangesMap) {
|
||||
// Get a list of all the ranges ordered by start index.
|
||||
List<IntervalTreeNode<T>> intervals = <IntervalTreeNode<T>>[];
|
||||
final List<IntervalTreeNode<T>> intervals = <IntervalTreeNode<T>>[];
|
||||
rangesMap.forEach((T key, List<CodeunitRange> rangeList) {
|
||||
for (CodeunitRange range in rangeList) {
|
||||
intervals.add(IntervalTreeNode<T>(key, range.start, range.end));
|
||||
@ -93,6 +93,16 @@ class IntervalTreeNode<T> {
|
||||
|
||||
IntervalTreeNode(this.value, this.low, this.high) : computedHigh = high;
|
||||
|
||||
Iterable<T> enumerateAllElements() sync* {
|
||||
if (left != null) {
|
||||
yield* left!.enumerateAllElements();
|
||||
}
|
||||
yield value;
|
||||
if (right != null) {
|
||||
yield* right!.enumerateAllElements();
|
||||
}
|
||||
}
|
||||
|
||||
bool contains(int x) {
|
||||
return low <= x && x <= high;
|
||||
}
|
||||
|
||||
@ -714,7 +714,7 @@ class CkParagraphBuilder implements ui.ParagraphBuilder {
|
||||
missingCodeUnits.add(codeUnits[i]);
|
||||
}
|
||||
}
|
||||
_findFontsForMissingCodeunits(missingCodeUnits);
|
||||
findFontsForMissingCodeunits(missingCodeUnits);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
// @dart = 2.12
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
@ -206,6 +207,116 @@ void testMain() {
|
||||
expect(notoDownloadQueue.isPending, isFalse);
|
||||
expect(skiaFontCollection.globalFontFallbacks, ['Roboto']);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/75836
|
||||
// When we had this bug our font fallback resolution logic would end up in an
|
||||
// infinite loop and this test would freeze and time out.
|
||||
test('Can find fonts for two adjacent unmatched code units from different fonts', () async {
|
||||
final LoggingDownloader loggingDownloader = LoggingDownloader(NotoDownloader());
|
||||
notoDownloadQueue.downloader = loggingDownloader;
|
||||
// Try rendering text that requires fallback fonts, initially before the fonts are loaded.
|
||||
|
||||
CkParagraphBuilder(CkParagraphStyle()).addText('ヽಠ');
|
||||
await notoDownloadQueue.downloader.debugWhenIdle();
|
||||
expect(
|
||||
loggingDownloader.log,
|
||||
<String>[
|
||||
'https://fonts.googleapis.com/css2?family=Noto+Sans+SC',
|
||||
'https://fonts.googleapis.com/css2?family=Noto+Sans+Kannada+UI',
|
||||
'Noto Sans SC',
|
||||
'Noto Sans Kannada UI',
|
||||
],
|
||||
);
|
||||
|
||||
// Do the same thing but this time with loaded fonts.
|
||||
loggingDownloader.log.clear();
|
||||
CkParagraphBuilder(CkParagraphStyle()).addText('ヽಠ');
|
||||
await notoDownloadQueue.downloader.debugWhenIdle();
|
||||
expect(loggingDownloader.log, isEmpty);
|
||||
});
|
||||
|
||||
test('findMinimumFontsForCodeunits for all supported code units', () async {
|
||||
final LoggingDownloader loggingDownloader = LoggingDownloader(NotoDownloader());
|
||||
notoDownloadQueue.downloader = loggingDownloader;
|
||||
|
||||
// Collect all supported code units from all fallback fonts in the Noto
|
||||
// font tree.
|
||||
final Set<String> testedFonts = <String>{};
|
||||
final Set<int> supportedUniqueCodeUnits = <int>{};
|
||||
for (NotoFont font in debugNotoTree.root.enumerateAllElements()) {
|
||||
testedFonts.add(font.name);
|
||||
for (CodeunitRange range in font.unicodeRanges) {
|
||||
for (int codeUnit = range.start; codeUnit < range.end; codeUnit += 1) {
|
||||
supportedUniqueCodeUnits.add(codeUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect(supportedUniqueCodeUnits.length, greaterThan(10000)); // sanity check
|
||||
expect(testedFonts, unorderedEquals(<String>{
|
||||
'Noto Sans',
|
||||
'Noto Sans Malayalam UI',
|
||||
'Noto Sans Armenian',
|
||||
'Noto Sans Georgian',
|
||||
'Noto Sans Hebrew',
|
||||
'Noto Naskh Arabic UI',
|
||||
'Noto Sans Devanagari UI',
|
||||
'Noto Sans Telugu UI',
|
||||
'Noto Sans Tamil UI',
|
||||
'Noto Sans Kannada UI',
|
||||
'Noto Sans Sinhala',
|
||||
'Noto Sans Gurmukhi UI',
|
||||
'Noto Sans Gujarati UI',
|
||||
'Noto Sans Bengali UI',
|
||||
'Noto Sans Thai UI',
|
||||
'Noto Sans Lao UI',
|
||||
'Noto Sans Myanmar UI',
|
||||
'Noto Sans Ethiopic',
|
||||
'Noto Sans Khmer UI',
|
||||
'Noto Sans SC',
|
||||
'Noto Sans JP',
|
||||
'Noto Sans TC',
|
||||
'Noto Sans HK',
|
||||
'Noto Sans KR',
|
||||
'Noto Sans Egyptian Hieroglyphs',
|
||||
}));
|
||||
|
||||
// Construct random paragraphs out of supported code units.
|
||||
final math.Random random = math.Random(0);
|
||||
final List<int> supportedCodeUnits = supportedUniqueCodeUnits.toList()..shuffle(random);
|
||||
const int paragraphLength = 3;
|
||||
|
||||
for (int batchStart = 0; batchStart < supportedCodeUnits.length; batchStart += paragraphLength) {
|
||||
final int batchEnd = math.min(batchStart + paragraphLength, supportedCodeUnits.length);
|
||||
final List<int> codeUnits = <int>[];
|
||||
for (int i = batchStart; i < batchEnd; i += 1) {
|
||||
codeUnits.add(supportedCodeUnits[i]);
|
||||
}
|
||||
final Set<NotoFont> fonts = <NotoFont>{};
|
||||
for (int codeunit in codeUnits) {
|
||||
List<NotoFont> fontsForUnit = debugNotoTree.intersections(codeunit);
|
||||
|
||||
// All code units are extracted from the same tree, so there must
|
||||
// be at least one font supporting each code unit
|
||||
expect(fontsForUnit, isNotEmpty);
|
||||
|
||||
// Make sure that every returned font indeed covers the code unit.
|
||||
expect(fontsForUnit.every((font) => font.matchesCodeunit(codeunit)), isTrue);
|
||||
fonts.addAll(fontsForUnit);
|
||||
}
|
||||
|
||||
try {
|
||||
findMinimumFontsForCodeunits(codeUnits, fonts);
|
||||
} catch (e) {
|
||||
print(
|
||||
'findMinimumFontsForCodeunits failed:\n'
|
||||
' Code units: ${codeUnits.join(', ')}\n'
|
||||
' Fonts: ${fonts.map((f) => f.name).join(', ')}',
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
});
|
||||
// TODO: https://github.com/flutter/flutter/issues/60040
|
||||
}, skip: isIosSafari);
|
||||
}
|
||||
@ -213,7 +324,7 @@ void testMain() {
|
||||
class TestDownloader extends NotoDownloader {
|
||||
static final Map<String, String> mockDownloads = <String, String>{};
|
||||
@override
|
||||
Future<String> downloadAsString(String url) async {
|
||||
Future<String> downloadAsString(String url, {String? debugDescription}) async {
|
||||
if (mockDownloads.containsKey(url)) {
|
||||
return mockDownloads[url]!;
|
||||
} else {
|
||||
@ -221,3 +332,28 @@ class TestDownloader extends NotoDownloader {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LoggingDownloader implements NotoDownloader {
|
||||
final List<String> log = <String>[];
|
||||
|
||||
LoggingDownloader(this.delegate);
|
||||
|
||||
final NotoDownloader delegate;
|
||||
|
||||
@override
|
||||
Future<void> debugWhenIdle() {
|
||||
return delegate.debugWhenIdle();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ByteBuffer> downloadAsBytes(String url, {String? debugDescription}) {
|
||||
log.add(debugDescription ?? url);
|
||||
return delegate.downloadAsBytes(url);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> downloadAsString(String url, {String? debugDescription}) {
|
||||
log.add(debugDescription ?? url);
|
||||
return delegate.downloadAsString(url);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user