Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@mdebbar
Copy link
Contributor

@mdebbar mdebbar commented Nov 5, 2024

By splitting all large fallback fonts (1MB+) into smaller parts, we get faster downloads and fast decoding.

Some fonts are split into 100+ parts, and that's causing main.dart.js's size to grow by ~47KB (Brotli-compressed). The increase in size is due to the extra data we have to store about all the parts of these fonts.

The PR also makes changes to ensure we don't download the same license file 100 times (once for each part of the split font).

Fixes flutter/flutter#138288
Part of flutter/flutter#153974

@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!).

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group.

@github-actions github-actions bot added the platform-web Code specifically for the web engine label Nov 5, 2024
@mdebbar mdebbar marked this pull request as draft November 5, 2024 21:34
@flutter-dashboard
Copy link

This pull request has been changed to a draft. The currently pending flutter-gold status will not be able to resolve until a new commit is pushed or the change is marked ready for review again.

Copy link
Contributor

@harryterkelsen harryterkelsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@mdebbar mdebbar marked this pull request as ready for review November 5, 2024 21:46
@mdebbar
Copy link
Contributor Author

mdebbar commented Nov 5, 2024

I still need to do something about these.

@harryterkelsen
Copy link
Contributor

I still need to do something about these.

I think instead of comparing the font to the (single) Noto Sans SC, TC, etc you can basically replace the checks for "if font == _notoSansSC" with "if font.name == 'Noto Sans SC'"

@mdebbar
Copy link
Contributor Author

mdebbar commented Nov 5, 2024

I still need to do something about these.

I think instead of comparing the font to the (single) Noto Sans SC, TC, etc you can basically replace the checks for "if font == _notoSansSC" with "if font.name == 'Noto Sans SC'"

Yep, that and there are also some list.contains(_notoSansSC) that need to change.

Copy link
Member

@ditman ditman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a small suggestion to improve error reporting a little bit.

@mdebbar
Copy link
Contributor Author

mdebbar commented Nov 7, 2024

@harryterkelsen @ditman would you mind taking another look? I fixed the logic for picking a font based on user preferred language. I want to make sure that part is reviewed too.

Copy link
Member

@ditman ditman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anything major, it seems the font selection stays the same?

Comment on lines +48 to +54
@visibleForTesting
String get debugUserPreferredLanguage => _language;

@visibleForTesting
set debugUserPreferredLanguage(String value) {
_language = value;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@visibleForTesting
String get debugUserPreferredLanguage => _language;
@visibleForTesting
set debugUserPreferredLanguage(String value) {
_language = value;
}
@visibleForTesting
String debugUserPreferredLanguage = _language;

Same effect, less code? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. With your suggestion, debugUserPreferredLanguage will get out of sync with _language, and the code below won't pick up changes to debugUserPreferredLanguage.

_isNotoSansHK(font) ||
_isNotoSansJP(font) ||
_isNotoSansKR(font))) {
if (_language == 'zh-Hans' ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can _language ever be just 'zh'?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. Revising the logic wasn't a goal for this PR. I was just trying to make the logic work the same way after the font split.

That said, I think you're right in the other comment that the zh case is handled later by the catch-all.

Comment on lines +270 to 272
// If the list of best fonts are all CJK fonts, choose the best one based
// on user preferred language. Otherwise just choose the first font.
if (bestFonts.every((NotoFont font) =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this to check that all bestFonts are CJK, or should this kick in when there's "more than one"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question, and we may want to revise some of the logic in this file. But, again, this is a non-goal for this PR 🙂

} else if (bestFonts.contains(_notoSansSC)) {
bestFont = _notoSansSC;
} else {
final notoSansSC = bestFonts.firstWhereOrNull(_isNotoSansSC);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this final catch-all solves all the possible corner cases of CJK fonts, by defaulting to simplified chinese :P

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm adding a test for this (i.e. lang="zh")

Comment on lines 313 to 317
final commonCJKCodePoints = <int>[
0x56de, // CJK Unified Ideographs
0x700b, // CJK Unified Ideographs
0x9c57, // CJK Unified Ideographs
];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think testing in more than one codepoint adds much to the test? This is testing the language selection mostly, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original idea was to find code points that would naturally default to different fonts when no language is specified (e.g. one code point that defaults to JP and another that would default to KR). But I couldn't find any unfortunately.

I think I'll use just one code point and get rid of the for loop.

Comment on lines 322 to 336
for (final lang in <String>['zh-Hans', 'zh-CN', 'zh-SG', 'zh-MY', 'en-US']) {
// Simplified Chinese is also prioritized when preferred language is en-US.
test('prioritizes Noto Sans SC for code-point=$codePointHex lang=$lang', () async {
await checkDownloadedFamilyForCharCode(codePoint, 'Noto Sans SC', userPreferredLanguage: lang);
});
}

// Traditional Chinese
for (final lang in <String>['zh-Hant', 'zh-TW', 'zh-MO']) {
test('prioritizes Noto Sans TC for code-point=$codePointHex lang=$lang', () async {
await checkDownloadedFamilyForCharCode(codePoint, 'Noto Sans TC', userPreferredLanguage: lang);
});
}

// Hong Kong
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could simplify all these tests with:

const fontFamilyToPreferredLanguage = Map<String, List<String>> {
  'Noto Sans SC': <String>['zh-Hans', 'zh-CN', 'zh-SG'...],
  'Noto Sans TC': <String>['zh-Hant', 'zh-TW', ...],
  ...
}

And iterating over its entries to generate all the possible tests without duplicating any code? :)

(It might make the actual test harder to find when it fails if we try to search by the error message or line number though)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was actually leaning the opposite direction. I prefer better readability/searchability. I was thinking of removing all for loops and doing:

test('prioritizes Noto Sans TC for code-point=0x700b lang=zh-Hant', () async {
  await checkDownloadedFamilyForCharCode(codePoint, 'Noto Sans TC', userPreferredLanguage: 'zh-Hant');
});
test('prioritizes Noto Sans TC for code-point=0x700b lang=zh-TW', () async {
  await checkDownloadedFamilyForCharCode(codePoint, 'Noto Sans TC', userPreferredLanguage: 'zh-TW');
});
test('prioritizes Noto Sans TC for code-point=0x700b lang=zh-MO', () async {
  await checkDownloadedFamilyForCharCode(codePoint, 'Noto Sans TC', userPreferredLanguage: 'zh-MO');
});

The code duplication isn't bad honestly. All the logic lives in checkDownloadedFamilyForCharCode.

Now that you suggested testing only one code point, I have an even stronger feeling to remove all the for loops 🙂

Copy link
Contributor

@harryterkelsen harryterkelsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mdebbar mdebbar added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 8, 2024
@flutter-dashboard
Copy link

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

Changes reported for pull request #56388 at sha 6706ced

@auto-submit auto-submit bot merged commit cfed62b into flutter:main Nov 8, 2024
38 checks passed
@mdebbar mdebbar deleted the split_cjk branch November 8, 2024 20:48
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 8, 2024
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Nov 8, 2024
@mdebbar mdebbar added the revert Label used to revert changes in a closed and merged pull request. label Nov 20, 2024
@auto-submit
Copy link
Contributor

auto-submit bot commented Nov 20, 2024

Time to revert pull request flutter/engine/56388 has elapsed.
You need to open the revert manually and process as a regular pull request.

@auto-submit auto-submit bot removed the revert Label used to revert changes in a closed and merged pull request. label Nov 20, 2024
mdebbar added a commit that referenced this pull request Nov 20, 2024
nick9822 pushed a commit to nick9822/flutter that referenced this pull request Dec 18, 2024
…6388)

By splitting all large fallback fonts (1MB+) into smaller parts, we get faster downloads and fast decoding.

Some fonts are split into 100+ parts, and that's causing `main.dart.js`'s size to grow by ~47KB (Brotli-compressed). The increase in size is due to the extra data we have to store about all the parts of these fonts.

The PR also makes  changes to ensure we don't download the same license file 100 times (once for each part of the split font).

Fixes flutter#138288
Part of flutter#153974
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

autosubmit Merge PR when tree becomes green via auto submit App platform-web Code specifically for the web engine will affect goldens

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flutter web's TextField displays gibberish for Unicode inputs, such as Korean characters (CJK)

3 participants