diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index a6e3a6f30a1a3..dbaf3e671b18b 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -4032,6 +4032,7 @@ ORIGIN: ../../../flutter/third_party/txt/src/txt/platform.h + ../../../flutter/L ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_android.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_fuchsia.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_linux.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_mac.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_mac.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/third_party/txt/src/txt/platform_windows.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/third_party/web_test_fonts/lib/web_test_fonts.dart + ../../../flutter/LICENSE @@ -6819,6 +6820,7 @@ FILE: ../../../flutter/third_party/txt/src/txt/platform.h FILE: ../../../flutter/third_party/txt/src/txt/platform_android.cc FILE: ../../../flutter/third_party/txt/src/txt/platform_fuchsia.cc FILE: ../../../flutter/third_party/txt/src/txt/platform_linux.cc +FILE: ../../../flutter/third_party/txt/src/txt/platform_mac.h FILE: ../../../flutter/third_party/txt/src/txt/platform_mac.mm FILE: ../../../flutter/third_party/txt/src/txt/platform_windows.cc FILE: ../../../flutter/third_party/web_test_fonts/lib/web_test_fonts.dart diff --git a/lib/ui/text/font_collection.cc b/lib/ui/text/font_collection.cc index 5a4afd334fc12..86225d4ad8df9 100644 --- a/lib/ui/text/font_collection.cc +++ b/lib/ui/text/font_collection.cc @@ -22,6 +22,9 @@ #include "third_party/tonic/typed_data/typed_list.h" #include "txt/asset_font_manager.h" #include "txt/test_font_manager.h" +#if FML_OS_MACOSX || FML_OS_IOS +#include "txt/platform_mac.h" +#endif namespace flutter { @@ -63,6 +66,9 @@ void FontCollection::SetupDefaultFontManager( // Structure described in https://docs.flutter.dev/cookbook/design/fonts void FontCollection::RegisterFonts( const std::shared_ptr& asset_manager) { +#if FML_OS_MACOSX || FML_OS_IOS + RegisterSystemFonts(*dynamic_font_manager_); +#endif std::unique_ptr manifest_mapping = asset_manager->GetAsMapping("FontManifest.json"); if (manifest_mapping == nullptr) { diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index 7402e6958cd9a..4c0f0713c2ddf 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -69,6 +69,8 @@ 686383152AC2175100E27AAD /* ../../Flutter.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4522E3B61000073EBF /* ../../Flutter.xcframework */; }; 686383162AC2175100E27AAD /* ../../Flutter.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 246B4E4522E3B61000073EBF /* ../../Flutter.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */; }; + 68C9D8012AD9B0EF00DF9D79 /* DarwinSystemFontTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68C9D8002AD9B0EF00DF9D79 /* DarwinSystemFontTests.m */; }; + 68C9D8092AD9B6C800DF9D79 /* golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 68C9D8082AD9B6C800DF9D79 /* golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png */; }; 68D4017D2564859300ECD91A /* ContinuousTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 68D4017C2564859300ECD91A /* ContinuousTexture.m */; }; 68D93AEE2A46097E0054AB6D /* golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 68D93AED2A46097E0054AB6D /* golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png */; }; F26F15B8268B6B5600EC54D3 /* iPadGestureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */; }; @@ -242,6 +244,8 @@ 686383052AC2024200E27AAD /* FlutterAppExtensionTestHost.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FlutterAppExtensionTestHost.xcodeproj; path = ../FlutterAppExtensionTestHost/FlutterAppExtensionTestHost.xcodeproj; sourceTree = ""; }; 686383122AC202B700E27AAD /* AppExtensionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppExtensionTests.m; sourceTree = ""; }; 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGestureRecognizerTests.m; sourceTree = ""; }; + 68C9D8002AD9B0EF00DF9D79 /* DarwinSystemFontTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DarwinSystemFontTests.m; sourceTree = ""; }; + 68C9D8082AD9B6C800DF9D79 /* golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; 68D4017B2564859300ECD91A /* ContinuousTexture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContinuousTexture.h; sourceTree = ""; }; 68D4017C2564859300ECD91A /* ContinuousTexture.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContinuousTexture.m; sourceTree = ""; }; 68D93AED2A46097E0054AB6D /* golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; @@ -366,6 +370,7 @@ 0DDEBC88258830B40065D0E8 /* SpawnEngineTest.m */, F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */, 686383122AC202B700E27AAD /* AppExtensionTests.m */, + 68C9D8002AD9B0EF00DF9D79 /* DarwinSystemFontTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -401,6 +406,7 @@ F7B464DC2759D02B00079189 /* Goldens */ = { isa = PBXGroup; children = ( + 68C9D8082AD9B6C800DF9D79 /* golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png */, 3BFD971E2A990CF40094F51B /* golden_bogus_font_text_impeller_iPhone SE (3rd generation)_16.2_simulator.png */, 3BFD971F2A990CF40094F51B /* golden_spawn_engine_works_impeller_iPhone SE (3rd generation)_16.2_simulator.png */, 68D93AED2A46097E0054AB6D /* golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png */, @@ -617,6 +623,7 @@ 684FFF8729F9C10700281002 /* golden_platform_view_multiple_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 68D93AEE2A46097E0054AB6D /* golden_platform_view_with_negative_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 3BFD97202A990CF50094F51B /* golden_bogus_font_text_impeller_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, + 68C9D8092AD9B6C800DF9D79 /* golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8D29F9C10700281002 /* golden_platform_view_large_cliprrect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8329F9C10700281002 /* golden_platform_view_transform_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8B29F9C10700281002 /* golden_platform_view_clippath_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, @@ -666,6 +673,7 @@ files = ( 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */, 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */, + 68C9D8012AD9B0EF00DF9D79 /* DarwinSystemFontTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 0A02E8F724EFAD27002D54E5 /* BogusFontTextTest.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 9aa3a0043063d..042fb8c6c49ad 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -90,6 +90,7 @@ - (BOOL)application:(UIApplication*)application @"--two-platform-view-clip-rect" : @"two_platform_view_clip_rect", @"--two-platform-view-clip-rrect" : @"two_platform_view_clip_rrect", @"--two-platform-view-clip-path" : @"two_platform_view_clip_path", + @"--darwin-system-font" : @"darwin_system_font", }; __block NSString* flutterViewControllerTestName = nil; [launchArgsMap diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/DarwinSystemFontTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/DarwinSystemFontTests.m new file mode 100644 index 0000000000000..29fec251557ed --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/DarwinSystemFontTests.m @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#import "GoldenTestManager.h" + +@interface DarwinSystemFontTests : XCTestCase + +@end + +@implementation DarwinSystemFontTests + +- (void)testFontRendering { + self.continueAfterFailure = NO; + + XCUIApplication* application = [[XCUIApplication alloc] init]; + application.launchArguments = @[ @"--darwin-system-font" ]; + [application launch]; + + XCUIElement* addTextField = application.textFields[@"ready"]; + XCTAssertTrue([addTextField waitForExistenceWithTimeout:30]); + + GoldenTestManager* manager = + [[GoldenTestManager alloc] initWithLaunchArg:@"--darwin-system-font"]; + [manager checkGoldenForTest:self rmesThreshold:kDefaultRmseThreshold]; +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m index 29915a4cf57b2..ac55da74642bb 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m @@ -54,6 +54,7 @@ - (instancetype)initWithLaunchArg:(NSString*)launchArg { @"--two-platform-view-clip-rrect" : @"two_platform_view_clip_rrect", @"--two-platform-view-clip-path" : @"two_platform_view_clip_path", @"--app-extension" : @"app_extension", + @"--darwin-system-font" : @"darwin_system_font", }; }); _identifier = launchArgsMap[launchArg]; diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png new file mode 100644 index 0000000000000..eba78ac2a07e9 Binary files /dev/null and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_darwin_system_font_iPhone SE (3rd generation)_16.2_simulator.png differ diff --git a/testing/scenario_app/lib/src/darwin_system_font.dart b/testing/scenario_app/lib/src/darwin_system_font.dart new file mode 100644 index 0000000000000..92bc1357625b7 --- /dev/null +++ b/testing/scenario_app/lib/src/darwin_system_font.dart @@ -0,0 +1,62 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'channel_util.dart'; +import 'scenario.dart'; + +/// Tries to draw darwin system font: CupertinoSystemDisplay, CupertinoSystemText +class DarwinSystemFont extends Scenario { + /// Creates the DarwinSystemFont scenario. + DarwinSystemFont(super.view); + + // Semi-arbitrary. + final double _screenWidth = 700; + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + + final ParagraphBuilder paragraphBuilderDisplay = + ParagraphBuilder(ParagraphStyle(fontFamily: 'CupertinoSystemDisplay')) + ..pushStyle(TextStyle(fontSize: 50)) + ..addText('Cupertino System Display\n') + ..pop(); + final ParagraphBuilder paragraphBuilderText = + ParagraphBuilder(ParagraphStyle(fontFamily: 'CupertinoSystemText')) + ..pushStyle(TextStyle(fontSize: 50)) + ..addText('Cupertino System Text\n') + ..pop(); + + final Paragraph paragraphPro = paragraphBuilderDisplay.build(); + paragraphPro.layout(ParagraphConstraints(width: _screenWidth)); + canvas.drawParagraph(paragraphPro, const Offset(50, 80)); + + final Paragraph paragraphText = paragraphBuilderText.build(); + paragraphText.layout(ParagraphConstraints(width: _screenWidth)); + canvas.drawParagraph(paragraphText, const Offset(50, 200)); + + final Picture picture = recorder.endRecording(); + + builder.addPicture( + Offset.zero, + picture, + willChangeHint: true, + ); + final Scene scene = builder.build(); + view.render(scene); + scene.dispose(); + + sendJsonMessage( + dispatcher: view.platformDispatcher, + channel: 'display_data', + json: { + 'data': 'ready', + }, + ); + } +} diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index 8e3b15a985fa1..87f1f6cbba952 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -7,6 +7,7 @@ import 'dart:ui'; import 'animated_color_square.dart'; import 'bogus_font_text.dart'; import 'darwin_app_extension_scenario.dart'; +import 'darwin_system_font.dart'; import 'get_bitmap_scenario.dart'; import 'initial_route_reply.dart'; import 'locale_initialization.dart'; @@ -68,6 +69,7 @@ Map _scenarios = { 'display_texture': (FlutterView view) => DisplayTexture(view), 'get_bitmap': (FlutterView view) => GetBitmapScenario(view), 'app_extension': (FlutterView view) => DarwinAppExtensionScenario(view), + 'darwin_system_font': (FlutterView view) => DarwinSystemFont(view), }; Map _currentScenarioParams = {}; diff --git a/third_party/txt/BUILD.gn b/third_party/txt/BUILD.gn index a61438b72c5eb..da4d0615d7b9a 100644 --- a/third_party/txt/BUILD.gn +++ b/third_party/txt/BUILD.gn @@ -98,7 +98,10 @@ source_set("txt") { } if (is_mac || is_ios) { - sources += [ "src/txt/platform_mac.mm" ] + sources += [ + "src/txt/platform_mac.h", + "src/txt/platform_mac.mm", + ] deps += [ "//flutter/fml" ] } else if (is_android) { sources += [ "src/txt/platform_android.cc" ] @@ -148,6 +151,10 @@ if (enable_unittests) { "tests/txt_run_all_unittests.cc", ] + if (is_mac || is_ios) { + sources += [ "tests/platform_mac_tests.cc" ] + } + public_configs = [ ":txt_config" ] configs += [ ":allow_posix_names" ] diff --git a/third_party/txt/src/txt/asset_font_manager.h b/third_party/txt/src/txt/asset_font_manager.h index cc1c92711e793..4c1e34c17fdc1 100644 --- a/third_party/txt/src/txt/asset_font_manager.h +++ b/third_party/txt/src/txt/asset_font_manager.h @@ -89,7 +89,7 @@ class DynamicFontManager : public AssetFontManager { DynamicFontManager() : AssetFontManager(std::make_unique()) {} - TypefaceFontAssetProvider& font_provider() { + TypefaceFontAssetProvider& font_provider() const { return static_cast(*font_provider_); } }; diff --git a/third_party/txt/src/txt/platform_mac.h b/third_party/txt/src/txt/platform_mac.h new file mode 100644 index 0000000000000..7ff2e0d391ded --- /dev/null +++ b/third_party/txt/src/txt/platform_mac.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TXT_PLATFORM_MAC_H_ +#define TXT_PLATFORM_MAC_H_ + +#include "txt/asset_font_manager.h" + +namespace txt { + +// Register additional system font for MacOS and iOS. +void RegisterSystemFonts(const DynamicFontManager& dynamic_font_manager); + +} // namespace txt + +#endif // TXT_PLATFORM_MAC_H_ diff --git a/third_party/txt/src/txt/platform_mac.mm b/third_party/txt/src/txt/platform_mac.mm index 4e116418c5974..3ab772ffb23e1 100644 --- a/third_party/txt/src/txt/platform_mac.mm +++ b/third_party/txt/src/txt/platform_mac.mm @@ -5,7 +5,9 @@ #include #include "flutter/fml/platform/darwin/platform_version.h" +#include "third_party/skia/include/ports/SkTypeface_mac.h" #include "txt/platform.h" +#include "txt/platform_mac.h" #if TARGET_OS_EMBEDDED || TARGET_OS_SIMULATOR #include @@ -15,6 +17,15 @@ #define FONT_CLASS NSFont #endif // TARGET_OS_EMBEDDED +// Apple system font larger than size 29 returns SFProDisplay typeface. +static const CGFloat kSFProDisplayBreakPoint = 29; +// Apple system font smaller than size 16 returns SFProText typeface. +static const CGFloat kSFProTextBreakPoint = 16; +// Font name represents the "SF Pro Display" system font on Apple platforms. +static const std::string kSFProDisplayName = "CupertinoSystemDisplay"; +// Font name represents the "SF Pro Text" system font on Apple platforms. +static const std::string kSFProTextName = "CupertinoSystemText"; + namespace txt { std::vector GetDefaultFontFamilies() { @@ -29,4 +40,34 @@ return SkFontMgr::RefDefault(); } +void RegisterSystemFonts(const DynamicFontManager& dynamic_font_manager) { + // iOS loads different system fonts when size is greater than 28 or lower + // than 17. The "familyName" property returned from CoreText stays the same + // despite the typeface is different. + // + // Below code manually loads and registers them as two different fonts + // so Flutter app can access them on macOS and iOS. + // + // Darwin system fonts from 17 to 28 also have dynamic spacing based on sizes. + // These two fonts do not match the spacings when sizes are from 17 to 28. + // The spacing should be handled by the app or the framework. + // + // See https://www.wwdcnotes.com/notes/wwdc20/10175/ for Apple's document on + // this topic. + sk_sp large_system_font = SkMakeTypefaceFromCTFont( + (CTFontRef)CFAutorelease(CTFontCreateUIFontForLanguage( + kCTFontUIFontSystem, kSFProDisplayBreakPoint, NULL))); + if (large_system_font) { + dynamic_font_manager.font_provider().RegisterTypeface(large_system_font, + kSFProDisplayName); + } + sk_sp regular_system_font = SkMakeTypefaceFromCTFont( + (CTFontRef)CFAutorelease(CTFontCreateUIFontForLanguage( + kCTFontUIFontSystem, kSFProTextBreakPoint, NULL))); + if (regular_system_font) { + dynamic_font_manager.font_provider().RegisterTypeface(regular_system_font, + kSFProTextName); + } +} + } // namespace txt diff --git a/third_party/txt/tests/platform_mac_tests.cc b/third_party/txt/tests/platform_mac_tests.cc new file mode 100644 index 0000000000000..54cea1c1324a1 --- /dev/null +++ b/third_party/txt/tests/platform_mac_tests.cc @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gtest/gtest.h" + +#include + +#include "txt/platform_mac.h" + +namespace txt { +namespace testing { + +class PlatformMacTests : public ::testing::Test { + public: + PlatformMacTests() {} + + void SetUp() override {} +}; + +TEST_F(PlatformMacTests, RegisterSystemFonts) { + DynamicFontManager dynamic_font_manager; + RegisterSystemFonts(dynamic_font_manager); + ASSERT_EQ(dynamic_font_manager.font_provider().GetFamilyCount(), 2ul); + ASSERT_NE(dynamic_font_manager.font_provider().MatchFamily( + "CupertinoSystemDisplay"), + nullptr); + ASSERT_NE( + dynamic_font_manager.font_provider().MatchFamily("CupertinoSystemText"), + nullptr); +} + +} // namespace testing +} // namespace txt