From 98c7b4f139e93ab5ed7ad31a77ab833062c93060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Sun, 1 Mar 2020 03:36:38 -0800 Subject: [PATCH] [ios, macos] Allow specifying multiple fonts or font families for local font rendering Removed MGLIdeographicFontFamilyName validation from MGLRendererConfiguration, since mbgl::LocalGlyphRasterizer performs more robust validation. Removed C++ from MGLRendererConfiguration interface, implementation, and tests for simplicity. Pass all the specified font and font family names into mbgl so that mbgl::LocalGlyphRasterizer can form a full font cascade list. --- platform/darwin/src/MGLMapSnapshotter.mm | 3 +- .../darwin/src/MGLRendererConfiguration.h | 8 +- .../darwin/src/MGLRendererConfiguration.m | 66 ++++++ .../darwin/src/MGLRendererConfiguration.mm | 119 ----------- .../test/MGLRendererConfigurationTests.m | 162 ++++++++++++++ .../test/MGLRendererConfigurationTests.mm | 197 ------------------ platform/ios/CHANGELOG.md | 6 +- platform/ios/docs/guides/Info.plist Keys.md | 10 +- platform/ios/ios.xcodeproj/project.pbxproj | 20 +- platform/ios/src/MGLMapView.mm | 3 +- platform/macos/CHANGELOG.md | 8 +- platform/macos/docs/guides/Info.plist Keys.md | 10 +- .../macos/macos.xcodeproj/project.pbxproj | 16 +- platform/macos/src/MGLMapView.mm | 3 +- vendor/mapbox-gl-native | 2 +- 15 files changed, 277 insertions(+), 356 deletions(-) create mode 100644 platform/darwin/src/MGLRendererConfiguration.m delete mode 100644 platform/darwin/src/MGLRendererConfiguration.mm create mode 100644 platform/darwin/test/MGLRendererConfigurationTests.m delete mode 100644 platform/darwin/test/MGLRendererConfigurationTests.mm diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm index 7c2385a5ff..29a8ce005e 100644 --- a/platform/darwin/src/MGLMapSnapshotter.mm +++ b/platform/darwin/src/MGLMapSnapshotter.mm @@ -771,8 +771,9 @@ - (void)setOptions:(MGLMapSnapshotOptions *)options .withAssetPath([NSBundle mainBundle].resourceURL.path.UTF8String); // Create the snapshotter + auto localFontFamilyName = config.localFontFamilyName ? std::string(config.localFontFamilyName.UTF8String) : nullptr; _mbglMapSnapshotter = std::make_unique( - style, size, pixelRatio, cameraOptions, coordinateBounds, config.localFontFamilyName, resourceOptions); + style, size, pixelRatio, cameraOptions, coordinateBounds, localFontFamilyName, resourceOptions); } @end diff --git a/platform/darwin/src/MGLRendererConfiguration.h b/platform/darwin/src/MGLRendererConfiguration.h index 0c539f86f1..f3a917c7e9 100644 --- a/platform/darwin/src/MGLRendererConfiguration.h +++ b/platform/darwin/src/MGLRendererConfiguration.h @@ -1,8 +1,6 @@ #import "MGLFoundation.h" #import -#include - NS_ASSUME_NONNULL_BEGIN /** @@ -34,7 +32,9 @@ MGL_EXPORT - A boolean value NO to disable client-side rendering of CJK glyphs — remote fonts specified in your style will be used instead. */ -@property (nonatomic, readonly) mbgl::optional localFontFamilyName; +@property (nonatomic, readonly, nullable) NSString *localFontFamilyName; + +- (nullable NSString *)localFontFamilyNameWithInfoDictionaryObject:(nullable id)infoDictionaryObject; /** A Boolean value indicating whether symbol layers may enable per-source symbol @@ -49,6 +49,8 @@ MGL_EXPORT */ @property (nonatomic, readonly) BOOL perSourceCollisions; +- (BOOL)perSourceCollisionsWithInfoDictionaryObject:(nullable id)infoDictionaryObject; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLRendererConfiguration.m b/platform/darwin/src/MGLRendererConfiguration.m new file mode 100644 index 0000000000..1cc47e3224 --- /dev/null +++ b/platform/darwin/src/MGLRendererConfiguration.m @@ -0,0 +1,66 @@ +#import "MGLRendererConfiguration.h" + +#if TARGET_OS_IPHONE +#import +#else +#import +#endif + +static NSString * const MGLCollisionBehaviorPre4_0Key = @"MGLCollisionBehaviorPre4_0"; +static NSString * const MGLIdeographicFontFamilyNameKey = @"MGLIdeographicFontFamilyName"; + +@implementation MGLRendererConfiguration + ++ (instancetype)currentConfiguration { + return [[self alloc] init]; +} + +- (const float)scaleFactor { +#if TARGET_OS_IPHONE + return [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; +#else + return [NSScreen mainScreen].backingScaleFactor; +#endif +} + +- (nullable NSString *)localFontFamilyName { + id infoDictionaryObject = [NSBundle.mainBundle objectForInfoDictionaryKey:MGLIdeographicFontFamilyNameKey]; + return [self localFontFamilyNameWithInfoDictionaryObject:infoDictionaryObject]; +} + +- (nullable NSString *)localFontFamilyNameWithInfoDictionaryObject:(nullable id)infoDictionaryObject { + if ([infoDictionaryObject isKindOfClass:[NSNumber class]] && ![infoDictionaryObject boolValue]) { + // NO means don’t use local fonts. + return nil; + } else if ([infoDictionaryObject isKindOfClass:[NSString class]]) { + return infoDictionaryObject; + } else if ([infoDictionaryObject isKindOfClass:[NSArray class]]) { + // mbgl::LocalGlyphRasterizer::Impl accepts only a single string, but form a cascade list with one font on each line. + return [infoDictionaryObject componentsJoinedByString:@"\n"]; + } + +#if TARGET_OS_IPHONE + return [UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName; +#else + return [NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName; +#endif +} + +- (BOOL)perSourceCollisions { + id infoDictionaryObject = [NSBundle.mainBundle objectForInfoDictionaryKey:MGLCollisionBehaviorPre4_0Key]; + return [self perSourceCollisionsWithInfoDictionaryObject:infoDictionaryObject]; +} + +- (BOOL)perSourceCollisionsWithInfoDictionaryObject:(nullable id)infoDictionaryObject { + // Set the collision behaviour. A value set in `NSUserDefaults.standardUserDefaults` + // should override anything in the application's info.plist + if ([NSUserDefaults.standardUserDefaults objectForKey:MGLCollisionBehaviorPre4_0Key]) { + return [NSUserDefaults.standardUserDefaults boolForKey:MGLCollisionBehaviorPre4_0Key]; + } else if ([infoDictionaryObject isKindOfClass:[NSNumber class]] || [infoDictionaryObject isKindOfClass:[NSString class]]) { + // Also support NSString to correspond with the behavior of `-[NSUserDefaults boolForKey:]` + return [infoDictionaryObject boolValue]; + } + return NO; +} + +@end diff --git a/platform/darwin/src/MGLRendererConfiguration.mm b/platform/darwin/src/MGLRendererConfiguration.mm deleted file mode 100644 index 136dc929a6..0000000000 --- a/platform/darwin/src/MGLRendererConfiguration.mm +++ /dev/null @@ -1,119 +0,0 @@ -#import "MGLRendererConfiguration.h" -#import "MGLOfflineStorage_Private.h" -#import "MGLFoundation_Private.h" - -#if TARGET_OS_IPHONE -#import -#else -#import -#endif - -static NSString * const MGLCollisionBehaviorPre4_0Key = @"MGLCollisionBehaviorPre4_0"; -static NSString * const MGLIdeographicFontFamilyNameKey = @"MGLIdeographicFontFamilyName"; - -@interface MGLRendererConfiguration () -@property (nonatomic, readwrite) BOOL perSourceCollisions; -@end - - -@implementation MGLRendererConfiguration - -+ (instancetype)currentConfiguration { - return [[self alloc] init]; -} - -- (instancetype)init { - return [self initWithPropertyDictionary:[[NSBundle mainBundle] infoDictionary]]; -} - -- (instancetype)initWithPropertyDictionary:(nonnull NSDictionary *)properties { - self = [super init]; - - if (self) { - // Set the collision behaviour. A value set in `NSUserDefaults.standardUserDefaults` - // should override anything in the application's info.plist - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - - if ([defaults objectForKey:MGLCollisionBehaviorPre4_0Key]) { - _perSourceCollisions = [defaults boolForKey:MGLCollisionBehaviorPre4_0Key]; - } - else { - id collisionBehaviourValue = properties[MGLCollisionBehaviorPre4_0Key]; - - NSNumber *collisionBehaviourNumber = MGL_OBJC_DYNAMIC_CAST(collisionBehaviourValue, NSNumber); - - if (collisionBehaviourNumber) { - _perSourceCollisions = collisionBehaviourNumber.boolValue; - } else { - // Also support NSString to correspond with the behavior of `-[NSUserDefaults boolForKey:]` - NSString *collisionBehaviourString = MGL_OBJC_DYNAMIC_CAST(collisionBehaviourValue, NSString); - - if (collisionBehaviourString) { - _perSourceCollisions = collisionBehaviourString.boolValue; - } - } - } - } - - return self; -} - -- (const float)scaleFactor { -#if TARGET_OS_IPHONE - return [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; -#else - return [NSScreen mainScreen].backingScaleFactor; -#endif -} - -- (mbgl::optional)localFontFamilyName { - return [self _localFontFamilyNameWithPropertyDictionary:[[NSBundle mainBundle] infoDictionary]]; -} - -- (mbgl::optional)_localFontFamilyNameWithPropertyDictionary:(nonnull NSDictionary *)properties { - - std::string systemFontFamilyName; -#if TARGET_OS_IPHONE - systemFontFamilyName = std::string([[UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName UTF8String]); -#else - systemFontFamilyName = std::string([[NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName UTF8String]); -#endif - - id fontFamilyName = properties[MGLIdeographicFontFamilyNameKey]; - - if([fontFamilyName isKindOfClass:[NSNumber class]] && ![fontFamilyName boolValue]) - { - return mbgl::optional(); - } - else if([fontFamilyName isKindOfClass:[NSString class]]) - { - BOOL isValidFont = NO; -#if TARGET_OS_IPHONE - if([[UIFont familyNames] containsObject:fontFamilyName]){ - isValidFont = YES; - } -#else - if([[[NSFontManager sharedFontManager] availableFontFamilies] containsObject:fontFamilyName]){ - isValidFont = YES; - } -#endif - return (fontFamilyName && isValidFont) ? std::string([fontFamilyName UTF8String]) : systemFontFamilyName; - } - // Ability to specify an array of fonts for fallbacks for `localIdeographicFontFamily` - else if ([fontFamilyName isKindOfClass:[NSArray class]]){ - for(NSString *name in fontFamilyName){ -#if TARGET_OS_IPHONE - if([[UIFont familyNames] containsObject:name]){ - return std::string([name UTF8String]); - } -#else - if([[[NSFontManager sharedFontManager] availableFontFamilies] containsObject:name]){ - return std::string([name UTF8String]); - } -#endif - } - } - return systemFontFamilyName; -} - -@end diff --git a/platform/darwin/test/MGLRendererConfigurationTests.m b/platform/darwin/test/MGLRendererConfigurationTests.m new file mode 100644 index 0000000000..6cc16c56af --- /dev/null +++ b/platform/darwin/test/MGLRendererConfigurationTests.m @@ -0,0 +1,162 @@ +#import +#import +#import "MGLRendererConfiguration.h" + +static NSString * const MGLRendererConfigurationTests_collisionBehaviorKey = @"MGLCollisionBehaviorPre4_0"; + +@interface MGLRendererConfigurationTests : XCTestCase +@end + +@implementation MGLRendererConfigurationTests +- (void)setUp { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:MGLRendererConfigurationTests_collisionBehaviorKey]; +} + +- (void)tearDown { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:MGLRendererConfigurationTests_collisionBehaviorKey]; +} + +// Emulate what would happen with an Info.plist. +- (void)testSettingMGLCollisionBehaviorPre40 +{ + MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] init]; + XCTAssertFalse([config perSourceCollisionsWithInfoDictionaryObject:nil]); + XCTAssertFalse([config perSourceCollisionsWithInfoDictionaryObject:@(NO)]); + XCTAssertTrue([config perSourceCollisionsWithInfoDictionaryObject:@(YES)]); + XCTAssertFalse([config perSourceCollisionsWithInfoDictionaryObject:@"NO"]); + XCTAssertTrue([config perSourceCollisionsWithInfoDictionaryObject:@"YES"]); +} + +- (void)testSettingMGLCollisionBehaviorPre40InNSUserDefaults { + { + XCTAssertNil([[NSUserDefaults standardUserDefaults] objectForKey:MGLRendererConfigurationTests_collisionBehaviorKey]); + MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; + XCTAssertFalse(config.perSourceCollisions); + XCTAssertFalse([config perSourceCollisionsWithInfoDictionaryObject:nil]); + } + + [[NSUserDefaults standardUserDefaults] setObject:@(NO) forKey:MGLRendererConfigurationTests_collisionBehaviorKey]; + { + XCTAssertNotNil([[NSUserDefaults standardUserDefaults] objectForKey:MGLRendererConfigurationTests_collisionBehaviorKey]); + MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; + XCTAssertFalse(config.perSourceCollisions); + XCTAssertFalse([config perSourceCollisionsWithInfoDictionaryObject:@(NO)]); + XCTAssertFalse([config perSourceCollisionsWithInfoDictionaryObject:@(YES)]); + } + + [[NSUserDefaults standardUserDefaults] setObject:@(YES) forKey:MGLRendererConfigurationTests_collisionBehaviorKey]; + { + XCTAssertNotNil([[NSUserDefaults standardUserDefaults] objectForKey:MGLRendererConfigurationTests_collisionBehaviorKey]); + MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; + XCTAssert(config.perSourceCollisions); + XCTAssertTrue([config perSourceCollisionsWithInfoDictionaryObject:@(NO)]); + XCTAssertTrue([config perSourceCollisionsWithInfoDictionaryObject:@(YES)]); + } +} + +- (void)testOverridingMGLCollisionBehaviorPre40 { + // Dictionary = NO, NSUserDefaults = YES + { + [[NSUserDefaults standardUserDefaults] setObject:@(YES) forKey:MGLRendererConfigurationTests_collisionBehaviorKey]; + MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] init]; + XCTAssert([config perSourceCollisionsWithInfoDictionaryObject:@(NO)]); + } + // Dictionary = YES, NSUserDefaults = NO + { + [[NSUserDefaults standardUserDefaults] setObject:@(NO) forKey:MGLRendererConfigurationTests_collisionBehaviorKey]; + MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] init]; + XCTAssertFalse([config perSourceCollisionsWithInfoDictionaryObject:@(YES)]); + } +} + +- (void)testDefaultLocalFontFamilyName { + + MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] init]; + NSString *localFontFamilyName = config.localFontFamilyName; + + NSString *systemFontFamilyName; +#if TARGET_OS_IPHONE + systemFontFamilyName = [UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName; +#else + systemFontFamilyName = [NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName; +#endif + + XCTAssertEqualObjects(localFontFamilyName, systemFontFamilyName, @"Default local font family name should match default system font"); +} + +- (void)testSettingMGLIdeographicFontFamilyNameWithPlistValue { + + MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] init]; + + // `MGLIdeographicFontFamilyName` set to bool value `YES` + { + NSString *localFontFamilyName = [config localFontFamilyNameWithInfoDictionaryObject:@(YES)]; + + NSString *systemFontFamilyName; +#if TARGET_OS_IPHONE + systemFontFamilyName = [UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName; +#else + systemFontFamilyName = [NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName; +#endif + XCTAssertEqualObjects(localFontFamilyName, systemFontFamilyName, @"Local font family name should match default system font name when setting `YES`"); + } + + // `MGLIdeographicFontFamilyName` set to bool value `NO` + { + NSString *localFontFamilyName = [config localFontFamilyNameWithInfoDictionaryObject:@(NO)]; + XCTAssertNil(localFontFamilyName, @"Client rendering font should use remote font when setting `NO`"); + } + + // `MGLIdeographicFontFamilyName` set to a valid font string value + { + NSString *localFontFamilyName = [config localFontFamilyNameWithInfoDictionaryObject:@"PingFang TC"]; + XCTAssertEqualObjects(localFontFamilyName, @"PingFang TC", @"Local font family name should match a custom valid font name"); + } + + // `MGLIdeographicFontFamilyName` set to an invalid font string value + { + NSString *localFontFamilyName = [config localFontFamilyNameWithInfoDictionaryObject:@"test font"]; + + NSString *systemFontFamilyName; +#if TARGET_OS_IPHONE + systemFontFamilyName = [UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName; +#else + systemFontFamilyName = [NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName; +#endif + XCTAssertNotEqualObjects(localFontFamilyName, systemFontFamilyName, @"Local font family name should not be validated by MGLRenderConfiguration"); + } + + // `MGLIdeographicFontFamilyName` set to a valid font family names array value + { + NSString *localFontFamilyName = [config localFontFamilyNameWithInfoDictionaryObject:@[@"test font 1", @"PingFang TC", @"test font 2"]]; + XCTAssertEqualObjects(localFontFamilyName, @"test font 1\nPingFang TC\ntest font 2"); + } + + // `MGLIdeographicFontFamilyName` set to an invalid font family names array value + { + NSString *localFontFamilyName = [config localFontFamilyNameWithInfoDictionaryObject:@[@"test font 1", @"test font 2", @"test font 3"]]; + + NSString *systemFontFamilyName; +#if TARGET_OS_IPHONE + systemFontFamilyName = [UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName; +#else + systemFontFamilyName = [NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName; +#endif + XCTAssertEqualObjects(localFontFamilyName, @"test font 1\ntest font 2\ntest font 3", @"Local font family name should not be validated by MGLRendererConfiguration"); + } + + // `MGLIdeographicFontFamilyName` set to an invalid value type: NSDictionary, NSNumber, NSData, etc. + { + NSString *localFontFamilyName = [config localFontFamilyNameWithInfoDictionaryObject:[@"test font 1" dataUsingEncoding:NSUTF8StringEncoding]]; + + NSString *systemFontFamilyName; +#if TARGET_OS_IPHONE + systemFontFamilyName = [UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName; +#else + systemFontFamilyName = [NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName; +#endif + XCTAssertEqualObjects(localFontFamilyName, systemFontFamilyName, @"Local font family name should match default system font name when setting an invalid value type"); + } +} + +@end diff --git a/platform/darwin/test/MGLRendererConfigurationTests.mm b/platform/darwin/test/MGLRendererConfigurationTests.mm deleted file mode 100644 index 6483aa3587..0000000000 --- a/platform/darwin/test/MGLRendererConfigurationTests.mm +++ /dev/null @@ -1,197 +0,0 @@ -#import -#import -#import "MGLRendererConfiguration.h" - -static NSString * const MGLRendererConfigurationTests_collisionBehaviorKey = @"MGLCollisionBehaviorPre4_0"; - -@interface MGLRendererConfiguration (Tests) -- (instancetype)initWithPropertyDictionary:(nonnull NSDictionary*)bundle; -- (mbgl::optional)_localFontFamilyNameWithPropertyDictionary:(nonnull NSDictionary *)properties; -@end - - -@interface MGLRendererConfigurationTests : XCTestCase -@end - -@implementation MGLRendererConfigurationTests -- (void)setUp { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:MGLRendererConfigurationTests_collisionBehaviorKey]; -} - -- (void)tearDown { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:MGLRendererConfigurationTests_collisionBehaviorKey]; -} - -// Emulate what would happen with an Info.plist. -- (void)testSettingMGLCollisionBehaviorPre40WithEmptyDictionary -{ - MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] initWithPropertyDictionary:@{}]; - XCTAssertFalse(config.perSourceCollisions); -} - -- (void)testSettingMGLCollisionBehaviorPre40WithYESDictionary -{ - MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] initWithPropertyDictionary:@{MGLRendererConfigurationTests_collisionBehaviorKey:@(NO)}]; - XCTAssertFalse(config.perSourceCollisions); -} - -- (void)testSettingMGLCollisionBehaviorPre40WithNODictionary -{ - MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] initWithPropertyDictionary:@{MGLRendererConfigurationTests_collisionBehaviorKey:@(YES)}]; - XCTAssert(config.perSourceCollisions); -} - -- (void)testSettingMGLCollisionBehaviorPre40InNSUserDefaults { - { - XCTAssertNil([[NSUserDefaults standardUserDefaults] objectForKey:MGLRendererConfigurationTests_collisionBehaviorKey]); - MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; - XCTAssertFalse(config.perSourceCollisions); - } - - [[NSUserDefaults standardUserDefaults] setObject:@(NO) forKey:MGLRendererConfigurationTests_collisionBehaviorKey]; - { - XCTAssertNotNil([[NSUserDefaults standardUserDefaults] objectForKey:MGLRendererConfigurationTests_collisionBehaviorKey]); - MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; - XCTAssertFalse(config.perSourceCollisions); - } - - [[NSUserDefaults standardUserDefaults] setObject:@(YES) forKey:MGLRendererConfigurationTests_collisionBehaviorKey]; - { - XCTAssertNotNil([[NSUserDefaults standardUserDefaults] objectForKey:MGLRendererConfigurationTests_collisionBehaviorKey]); - MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; - XCTAssert(config.perSourceCollisions); - } -} - -- (void)testSettingMGLCollisionBehaviorPre40PListValueUsingString { - // Dictionary = "NO" - { - MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] initWithPropertyDictionary:@{MGLRendererConfigurationTests_collisionBehaviorKey:@"NO"}]; - XCTAssertFalse(config.perSourceCollisions); - } - - // Dictionary = "YES" - { - MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] initWithPropertyDictionary:@{MGLRendererConfigurationTests_collisionBehaviorKey:@"YES"}]; - XCTAssert(config.perSourceCollisions); - } -} - -- (void)testOverridingMGLCollisionBehaviorPre40 { - // Dictionary = NO, NSUserDefaults = YES - { - [[NSUserDefaults standardUserDefaults] setObject:@(YES) forKey:MGLRendererConfigurationTests_collisionBehaviorKey]; - MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] initWithPropertyDictionary:@{MGLRendererConfigurationTests_collisionBehaviorKey:@(NO)}]; - XCTAssert(config.perSourceCollisions); - } - // Dictionary = YES, NSUserDefaults = NO - { - [[NSUserDefaults standardUserDefaults] setObject:@(NO) forKey:MGLRendererConfigurationTests_collisionBehaviorKey]; - MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] initWithPropertyDictionary:@{MGLRendererConfigurationTests_collisionBehaviorKey:@(YES)}]; - XCTAssertFalse(config.perSourceCollisions); - } -} - -- (void)testDefaultLocalFontFamilyName { - - MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] init]; - std::string localFontFamilyName = config.localFontFamilyName.value(); - - std::string systemFontFamilyName; -#if TARGET_OS_IPHONE - systemFontFamilyName = std::string([[UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName UTF8String]); -#else - systemFontFamilyName = std::string([[NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName UTF8String]); -#endif - - XCTAssertEqual(localFontFamilyName, systemFontFamilyName, @"Default local font family name should match default system font"); -} - -- (void)testSettingMGLIdeographicFontFamilyNameWithPlistValue { - - MGLRendererConfiguration *config = [[MGLRendererConfiguration alloc] init]; - NSDictionary *dic; - - // `MGLIdeographicFontFamilyName` set to bool value `YES` - { - dic = @{@"MGLIdeographicFontFamilyName": @(YES)}; - std::string localFontFamilyName = ([config _localFontFamilyNameWithPropertyDictionary:dic]).value(); - - std::string systemFontFamilyName; -#if TARGET_OS_IPHONE - systemFontFamilyName = std::string([[UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName UTF8String]); -#else - systemFontFamilyName = std::string([[NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName UTF8String]); -#endif - XCTAssertEqual(localFontFamilyName, systemFontFamilyName, @"Local font family name should match default system font name when setting `YES`"); - } - - // `MGLIdeographicFontFamilyName` set to bool value `NO` - { - dic = @{@"MGLIdeographicFontFamilyName": @(NO)}; - mbgl::optional localFontFamilyName = [config _localFontFamilyNameWithPropertyDictionary:dic]; - XCTAssertFalse(localFontFamilyName.has_value(), @"Client rendering font should use remote font when setting `NO`"); - } - - // `MGLIdeographicFontFamilyName` set to a valid font string value - { - dic = @{@"MGLIdeographicFontFamilyName": @"PingFang TC"}; - std::string localFontFamilyName = ([config _localFontFamilyNameWithPropertyDictionary:dic]).value(); - std::string targetFontFamilyName = std::string([@"PingFang TC" UTF8String]); - XCTAssertEqual(localFontFamilyName, targetFontFamilyName, @"Local font family name should match a custom valid font name"); - } - - // `MGLIdeographicFontFamilyName` set to an invalid font string value - { - dic = @{@"MGLIdeographicFontFamilyName": @"test font"}; - std::string localFontFamilyName = ([config _localFontFamilyNameWithPropertyDictionary:dic]).value(); - - std::string systemFontFamilyName; -#if TARGET_OS_IPHONE - systemFontFamilyName = std::string([[UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName UTF8String]); -#else - systemFontFamilyName = std::string([[NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName UTF8String]); -#endif - XCTAssertEqual(localFontFamilyName, systemFontFamilyName, @"Local font family name should match default system font name when setting an invalid font string"); - } - - // `MGLIdeographicFontFamilyName` set to a valid font family names array value - { - dic = @{@"MGLIdeographicFontFamilyName": @[@"test font 1", @"PingFang TC", @"test font 2"]}; - std::string localFontFamilyName = ([config _localFontFamilyNameWithPropertyDictionary:dic]).value(); - std::string targetFontFamilyName = std::string([@"PingFang TC" UTF8String]); - XCTAssertEqual(localFontFamilyName, targetFontFamilyName, @"Local font family name should match a custom valid font name in a font family names array"); - } - - // `MGLIdeographicFontFamilyName` set to an invalid font family names array value - { - dic = @{@"MGLIdeographicFontFamilyName": @[@"test font 1", @"test font 2", @"test font 3"]}; - std::string localFontFamilyName = ([config _localFontFamilyNameWithPropertyDictionary:dic]).value(); - - std::string systemFontFamilyName; -#if TARGET_OS_IPHONE - systemFontFamilyName = std::string([[UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName UTF8String]); -#else - systemFontFamilyName = std::string([[NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName UTF8String]); -#endif - XCTAssertEqual(localFontFamilyName, systemFontFamilyName, @"Local font family name should match default system font name when setting an invalid font family names array"); - } - - // `MGLIdeographicFontFamilyName` set to an invalid value type: NSDictionary, NSNumber, NSData, etc. - { - dic = @{@"MGLIdeographicFontFamilyName": [@"test font 1" dataUsingEncoding:NSUTF8StringEncoding]}; - std::string localFontFamilyName = ([config _localFontFamilyNameWithPropertyDictionary:dic]).value(); - - std::string systemFontFamilyName; -#if TARGET_OS_IPHONE - systemFontFamilyName = std::string([[UIFont systemFontOfSize:0 weight:UIFontWeightRegular].familyName UTF8String]); -#else - systemFontFamilyName = std::string([[NSFont systemFontOfSize:0 weight:NSFontWeightRegular].familyName UTF8String]); -#endif - XCTAssertEqual(localFontFamilyName, systemFontFamilyName, @"Local font family name should match default system font name when setting an invalid value type"); - } -} - - - -@end diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 177ab7fd08..4b86dc1a14 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -9,10 +9,14 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Added the `in` expression function for testing whether a value is included in an array expression or whether a string is a substring of another string. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. ([#16162](https://github.com/mapbox/mapbox-gl-native/pull/16162)) * Added the `within` expression function for testing whether the evaluated feature lies within the given GeoJSON object. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. ([#16157](https://github.com/mapbox/mapbox-gl-native/pull/16157)) * Added the `MGLLineStyleLayer.lineSortKey` and `MGLFillStyleLayer.fillSortKey` properties. ([#179](https://github.com/mapbox/mapbox-gl-native-ios/pull/179), [#16194](https://github.com/mapbox/mapbox-gl-native/pull/16194), [#16220](https://github.com/mapbox/mapbox-gl-native/pull/16220)) -* The `-[MGLStyle localizeLabelsIntoLocale:]` and `-[NSExpression mgl_expressionLocalizedIntoLocale:]` methods can now localize text into Traditional Chinese and Vietnamese. ([#173](https://github.com/mapbox/mapbox-gl-native-ios/pull/173)) * Fixed an issue where an `MGLSymbolStyleLayer.lineDashPattern` value of `{1, 0}` resulted in hairline gaps. ([#16202](https://github.com/mapbox/mapbox-gl-native/pull/16202)) * Improved the performance of loading a style that has many style images. ([#16187](https://github.com/mapbox/mapbox-gl-native/pull/16187)) +### Localization and internationalization + +* The `MGLIdeographicFontFamilyName` Info.plist key can now be set to the display names or PostScript names of individual fonts. ([#189](https://github.com/mapbox/mapbox-gl-native-ios/pull/189)) +* The `-[MGLStyle localizeLabelsIntoLocale:]` and `-[NSExpression mgl_expressionLocalizedIntoLocale:]` methods can now localize text into Traditional Chinese and Vietnamese. ([#173](https://github.com/mapbox/mapbox-gl-native-ios/pull/173)) + ### Other changes * Fixed issues where an offline pack would stop downloading before completion. ([#16230](https://github.com/mapbox/mapbox-gl-native/pull/16230), [#16240](https://github.com/mapbox/mapbox-gl-native/pull/16240)) diff --git a/platform/ios/docs/guides/Info.plist Keys.md b/platform/ios/docs/guides/Info.plist Keys.md index 6322b42dc1..23179a51ea 100644 --- a/platform/ios/docs/guides/Info.plist Keys.md +++ b/platform/ios/docs/guides/Info.plist Keys.md @@ -22,15 +22,13 @@ If you have implemented custom opt-out of Mapbox Telemetry within the user inter ## MGLIdeographicFontFamilyName -The name of the font family to use for client-side text rendering of CJK ideographs. +This key configures client-side text rendering of Chinese hanzi, Japanese kana, and Korean hangul characters (CJK) that appear in text labels. Client-side text rendering uses less bandwidth than server-side text rendering, especially when viewing regions of the map that feature a wide variety of CJK characters. -Set `MGLIdeographicFontFamilyName` in your containing application's Info.plist to font family name(s) that will be available at run time, such as “PingFang TC” or “Marker Felt”. This plist key accepts: +By default, the map renders CJK characters using locally installed fonts as specified by the system’s font cascade list, which may vary from device to device, and ignores the `MGLSymbolStyleLayer.textFont` property for these characters. To customize the displayed font, set this key to a string containing a font family name (for example, “PingFang TC”) or an individual font’s display name (“PingFang TC Ultralight”) or PostScript name (“PingFangTC-Ultralight”). The key can name a font that is installed system-wide or bundled with the application. -- A string value of a single font family name. +In case your preferred font has glyphs for every character that may appear on the map, you can set this key to an array of font or font family names in order from most preferred to least preferred. Each character is rendered in the first font in the list that has a glyph for the character. If the entire font is exhausted, the map uses the system’s font cascade list. -- An array of font family names. Fonts will be used in the defined order, eventually falling back to default system font if none are available. - -- A boolean value `NO` to disable client-side rendering of CJK glyphs — remote fonts specified in your style will be used instead. +To disable client-side rendering of CJK characters, set this key to the Boolean value `NO`. Glyphs from the fonts specified by `MGLSymbolStyleLayer.textFont` will be downloaded and rendered instead. ## MGLCollisionBehaviorPre4_0 diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 50a1441aa2..e158e1083b 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -193,10 +193,10 @@ 35E79F211D41266300957B9E /* MGLStyleLayer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */; }; 3E6465D62065767A00685536 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E6465D42065767A00685536 /* LimeGreenStyleLayer.m */; }; 3E8770612074297100B7E842 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; - 3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */; }; + 3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.m */; }; 3EA934623AD0000B7D99C3FB /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */; }; 3EA9363147E77DD29FA06063 /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */; }; - 3EA9366247780E4F252652A8 /* MGLRendererConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */; }; + 3EA9366247780E4F252652A8 /* MGLRendererConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.m */; }; 400533011DB0862B0069F638 /* NSArray+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 400532FF1DB0862B0069F638 /* NSArray+MGLAdditions.h */; }; 400533021DB0862B0069F638 /* NSArray+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 400533001DB0862B0069F638 /* NSArray+MGLAdditions.mm */; }; 400533031DB086490069F638 /* NSArray+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 400533001DB0862B0069F638 /* NSArray+MGLAdditions.mm */; }; @@ -415,7 +415,7 @@ CA7766842229C11A0008DE9E /* SMCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88488A1CBB037E00AB86E3 /* SMCalloutView.m */; }; CA86FF0E22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA86FF0D22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m */; }; CA88DC3021C85D900059ED5A /* MGLStyleURLIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA88DC2F21C85D900059ED5A /* MGLStyleURLIntegrationTest.m */; }; - CA8FBC0921A47BB100D1203C /* MGLRendererConfigurationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA8FBC0821A47BB100D1203C /* MGLRendererConfigurationTests.mm */; }; + CA8FBC0921A47BB100D1203C /* MGLRendererConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8FBC0821A47BB100D1203C /* MGLRendererConfigurationTests.m */; }; CA94E5FB237D21030037AEA0 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; }; CA94E5FC237D21030037AEA0 /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CA94E601237EF4110037AEA0 /* MapboxMobileEvents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA96299B23731199004F1330 /* MapboxMobileEvents.framework */; }; @@ -910,7 +910,7 @@ 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleLayer_Private.h; sourceTree = ""; }; 3E6465D42065767A00685536 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LimeGreenStyleLayer.m; path = ../../darwin/app/LimeGreenStyleLayer.m; sourceTree = ""; }; 3E6465D52065767A00685536 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LimeGreenStyleLayer.h; path = ../../darwin/app/LimeGreenStyleLayer.h; sourceTree = ""; }; - 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRendererConfiguration.mm; sourceTree = ""; }; + 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLRendererConfiguration.m; sourceTree = ""; }; 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererConfiguration.h; sourceTree = ""; }; 400532FF1DB0862B0069F638 /* NSArray+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+MGLAdditions.h"; sourceTree = ""; }; 400533001DB0862B0069F638 /* NSArray+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSArray+MGLAdditions.mm"; sourceTree = ""; }; @@ -1061,7 +1061,7 @@ CA6914B420E67F50002DB0EE /* MGLAnnotationViewIntegrationTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLAnnotationViewIntegrationTests.mm; path = "Annotation Tests/MGLAnnotationViewIntegrationTests.mm"; sourceTree = ""; }; CA86FF0D22D8D5A0009EB14A /* MGLNetworkConfigurationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfigurationTests.m; sourceTree = ""; }; CA88DC2F21C85D900059ED5A /* MGLStyleURLIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLStyleURLIntegrationTest.m; sourceTree = ""; }; - CA8FBC0821A47BB100D1203C /* MGLRendererConfigurationTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLRendererConfigurationTests.mm; path = ../../darwin/test/MGLRendererConfigurationTests.mm; sourceTree = ""; }; + CA8FBC0821A47BB100D1203C /* MGLRendererConfigurationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLRendererConfigurationTests.m; path = ../../darwin/test/MGLRendererConfigurationTests.m; sourceTree = ""; }; CA96299B23731199004F1330 /* MapboxMobileEvents.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MapboxMobileEvents.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CAAA65D72321BBA900F08A39 /* MGLTestAssertionHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MGLTestAssertionHandler.h; path = ../../darwin/test/MGLTestAssertionHandler.h; sourceTree = ""; }; CAAA65D82321BBA900F08A39 /* MGLTestAssertionHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLTestAssertionHandler.m; path = ../../darwin/test/MGLTestAssertionHandler.m; sourceTree = ""; }; @@ -1876,7 +1876,7 @@ DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */, 55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */, 35B8E08B1D6C8B5100E768D2 /* MGLPredicateTests.mm */, - CA8FBC0821A47BB100D1203C /* MGLRendererConfigurationTests.mm */, + CA8FBC0821A47BB100D1203C /* MGLRendererConfigurationTests.m */, CAD9D0A922A86D6F001B25EE /* MGLResourceTests.mm */, DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */, 556660D71E1D085500E2C41B /* MGLVersionNumber.m */, @@ -1942,7 +1942,7 @@ DA35D9BF240916290013ECB0 /* MGLReachability.h */, DA35D9BE240916290013ECB0 /* MGLReachability.m */, 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */, - 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */, + 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.m */, 92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */, DA8847EC1CBAFA5100AB86E3 /* MGLStyle.h */, 35E0CFE51D3E501500188327 /* MGLStyle_Private.h */, @@ -3035,7 +3035,7 @@ 1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */, DA695426215B1E76002041A4 /* MGLMapCameraTests.m in Sources */, 96381C0222C6F3950053497D /* MGLMapViewPitchTests.m in Sources */, - CA8FBC0921A47BB100D1203C /* MGLRendererConfigurationTests.mm in Sources */, + CA8FBC0921A47BB100D1203C /* MGLRendererConfigurationTests.m in Sources */, CAD9D0AA22A86D6F001B25EE /* MGLResourceTests.mm in Sources */, DD58A4C61D822BD000E1F038 /* MGLExpressionTests.mm in Sources */, 3575798B1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm in Sources */, @@ -3160,7 +3160,7 @@ AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */, DA8848271CBAFA6200AB86E3 /* MGLPolyline.mm in Sources */, 35CE61841D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */, - 3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.mm in Sources */, + 3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3261,7 +3261,7 @@ DAA4E41C1CBB730400178DFB /* MGLAccountManager.m in Sources */, 35CE61851D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */, DAA4E4241CBB730400178DFB /* MGLPolyline.mm in Sources */, - 3EA9366247780E4F252652A8 /* MGLRendererConfiguration.mm in Sources */, + 3EA9366247780E4F252652A8 /* MGLRendererConfiguration.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 24e4e8f43c..2d9b280df0 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -483,7 +483,8 @@ - (void)commonInit // setup mbgl map MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; - auto renderer = std::make_unique(_mbglView->getRendererBackend(), config.scaleFactor, config.localFontFamilyName); + auto localFontFamilyName = config.localFontFamilyName ? std::string(config.localFontFamilyName.UTF8String) : nullptr; + auto renderer = std::make_unique(_mbglView->getRendererBackend(), config.scaleFactor, localFontFamilyName); BOOL enableCrossSourceCollisions = !config.perSourceCollisions; _rendererFrontend = std::make_unique(std::move(renderer), self, _mbglView->getRendererBackend()); diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index f054c4cc69..0088dc010a 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -10,8 +10,6 @@ * Added the `within` expression function for testing whether the evaluated feature lies within the given GeoJSON object. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. ([#16157](https://github.com/mapbox/mapbox-gl-native/pull/16157), [#16194](https://github.com/mapbox/mapbox-gl-native/pull/16194), [#16220](https://github.com/mapbox/mapbox-gl-native/pull/16220)) * Added the `MGLSymbolStyleLayer.textWritingModes` layout property. This property can be set to `MGLTextWritingModeHorizontal` or `MGLTextWritingModeVertical`. ([#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932)) * Added the `MGLLineStyleLayer.lineSortKey` and `MGLFillStyleLayer.fillSortKey` properties. ([#179](https://github.com/mapbox/mapbox-gl-native-ios/pull/179)) -* The `MGLIdeographicFontFamilyName` Info.plist key now also accepts an array of font family names, to customize font fallback behavior. It can also be set to a Boolean value of `NO` to force the SDK to typeset CJK characters in a remote font specified by `MGLSymbolStyleLayer.textFontNames`. ([#14862](https://github.com/mapbox/mapbox-gl-native/pull/14862)) -* The `-[MGLStyle localizeLabelsIntoLocale:]` and `-[NSExpression mgl_expressionLocalizedIntoLocale:]` methods can now localize text into Traditional Chinese and Vietnamese. ([#173](https://github.com/mapbox/mapbox-gl-native-ios/pull/173)) * Fixed crashes triggered when `MGLSource` and `MGLStyleLayer` objects are accessed after having been invalidated after a style change. ([#15539](https://github.com/mapbox/mapbox-gl-native/pull/15539)) * Fixed an issue where fill extrusion layers would be incorrectly rendered above other layers. ([#15065](https://github.com/mapbox/mapbox-gl-native/pull/15065)) * Fixed rendering and collision detection issues with using `MGLSymbolStyleLayer.textVariableAnchor` and `MGLSymbolStyleLayer.iconTextFit` properties on the same layer. ([#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367)) @@ -50,6 +48,12 @@ * When an offline pack encounters an HTTP 404 error, the `MGLOfflinePackUserInfoKeyError` user info key of the `MGLOfflinePackErrorNotification` now indicates the resource that could not be downloaded. ([#16240](https://github.com/mapbox/mapbox-gl-native/pull/16240)) * Expired resources are now fetched at a lower priority than new resources. ([#15950](https://github.com/mapbox/mapbox-gl-native/pull/15950)) +### Localization and internationalization + +* The `MGLIdeographicFontFamilyName` Info.plist key now also accepts an array of font family names, to customize font fallback behavior. It can also be set to a Boolean value of `NO` to force the SDK to typeset CJK characters in a remote font specified by `MGLSymbolStyleLayer.textFontNames`. ([#14862](https://github.com/mapbox/mapbox-gl-native/pull/14862)) +* The `MGLIdeographicFontFamilyName` Info.plist key can now be set to the display names or PostScript names of individual fonts. ([#189](https://github.com/mapbox/mapbox-gl-native-ios/pull/189)) +* The `-[MGLStyle localizeLabelsIntoLocale:]` and `-[NSExpression mgl_expressionLocalizedIntoLocale:]` methods can now localize text into Traditional Chinese and Vietnamese. ([#173](https://github.com/mapbox/mapbox-gl-native-ios/pull/173)) + ### Other changes * Added an `-[MGLMapSnapshotter startWithOverlayHandler:completionHandler:]` method to provide the snapshot's current `CGContext` in order to perform custom drawing on `MGLMapSnapshot` objects. ([#15530](https://github.com/mapbox/mapbox-gl-native/pull/15530)) diff --git a/platform/macos/docs/guides/Info.plist Keys.md b/platform/macos/docs/guides/Info.plist Keys.md index 35ecf67e36..f373d8bea2 100644 --- a/platform/macos/docs/guides/Info.plist Keys.md +++ b/platform/macos/docs/guides/Info.plist Keys.md @@ -18,15 +18,13 @@ The default value is `https://api.mapbox.com`. ## MGLIdeographicFontFamilyName -The name of the font family to use for client-side text rendering of CJK ideographs. +This key configures client-side text rendering of Chinese hanzi, Japanese kana, and Korean hangul characters (CJK) that appear in text labels. Client-side text rendering uses less bandwidth than server-side text rendering, especially when viewing regions of the map that feature a wide variety of CJK characters. -Set `MGLIdeographicFontFamilyName` in your containing application's Info.plist to font family name(s) that will be available at run time, such as “PingFang TC” or “Marker Felt”. This plist key accepts: +By default, the map renders CJK characters using locally installed fonts as specified by the system’s font cascade list, which may vary from device to device, and ignores the `MGLSymbolStyleLayer.textFont` property for these characters. To customize the displayed font, set this key to a string containing a font family name (for example, “PingFang TC”) or an individual font’s display name (“PingFang TC Ultralight”) or PostScript name (“PingFangTC-Ultralight”). The key can name a font that is installed system-wide or bundled with the application. -- A string value of a single font family name. +In case your preferred font has glyphs for every character that may appear on the map, you can set this key to an array of font or font family names in order from most preferred to least preferred. Each character is rendered in the first font in the list that has a glyph for the character. If the entire font is exhausted, the map uses the system’s font cascade list. -- An array of font family names. Fonts will be used in the defined order, eventually falling back to default system font if none are available. - -- A boolean value `NO` to disable client-side rendering of CJK glyphs — remote fonts specified in your style will be used instead. +To disable client-side rendering of CJK characters, set this key to the Boolean value `NO`. Glyphs from the fonts specified by `MGLSymbolStyleLayer.textFont` will be downloaded and rendered instead. ## MGLCollisionBehaviorPre4_0 diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index dd3cafecbb..242dc43fd2 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -74,7 +74,7 @@ 35D65C5B1D65AD5500722C23 /* NSDate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35D65C591D65AD5500722C23 /* NSDate+MGLAdditions.mm */; }; 3E6465D9206576A900685536 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E6465D7206576A800685536 /* LimeGreenStyleLayer.m */; }; 3EA9317388DC9A0BF46B7674 /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9369A4C46957566058822 /* MGLRendererConfiguration.h */; }; - 3EA93BA38DBB4B814B6C1FCC /* MGLRendererConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.mm */; }; + 3EA93BA38DBB4B814B6C1FCC /* MGLRendererConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.m */; }; 4031ACFC1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4031ACFB1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift */; }; 4031AD031E9FD6AA00A3EA26 /* MGLSDKTestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4031AD011E9FD6A300A3EA26 /* MGLSDKTestHelpers.swift */; }; 4049C2A51DB6CE7F00B3F799 /* MGLPointCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 4049C2A11DB6CE7800B3F799 /* MGLPointCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -127,7 +127,7 @@ CA0B3C072329F7E700E4B493 /* MGLTestAssertionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0B3C052329F7E600E4B493 /* MGLTestAssertionHandler.m */; }; CA0B3C092329FB4800E4B493 /* MGLOfflinePackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA0B3C082329FB4800E4B493 /* MGLOfflinePackTests.mm */; }; CA4045C7216720D700B356E1 /* MGLCluster.h in Headers */ = {isa = PBXBuildFile; fileRef = CA4045C4216720D700B356E1 /* MGLCluster.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CA8FBC0D21A4A74300D1203C /* MGLRendererConfigurationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA8FBC0C21A4A74300D1203C /* MGLRendererConfigurationTests.mm */; }; + CA8FBC0D21A4A74300D1203C /* MGLRendererConfigurationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8FBC0C21A4A74300D1203C /* MGLRendererConfigurationTests.m */; }; CA9461A620884CCB0015EB12 /* MGLAnnotationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA9461A520884CCB0015EB12 /* MGLAnnotationTests.m */; }; CAD9D0AC22A88A32001B25EE /* MGLResourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CAD9D0AB22A88A32001B25EE /* MGLResourceTests.mm */; }; CF762DEF22DC7EFF00338472 /* MGLLoggingConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = CF762DEE22DC7EFF00338472 /* MGLLoggingConfiguration.mm */; }; @@ -406,7 +406,7 @@ 3E6465D7206576A800685536 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LimeGreenStyleLayer.m; path = ../../darwin/app/LimeGreenStyleLayer.m; sourceTree = ""; }; 3E6465D8206576A900685536 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LimeGreenStyleLayer.h; path = ../../darwin/app/LimeGreenStyleLayer.h; sourceTree = ""; }; 3EA9369A4C46957566058822 /* MGLRendererConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererConfiguration.h; sourceTree = ""; }; - 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRendererConfiguration.mm; sourceTree = ""; }; + 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLRendererConfiguration.m; sourceTree = ""; }; 4031ACFB1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLMapViewDelegateIntegrationTests.swift; sourceTree = ""; }; 4031AD011E9FD6A300A3EA26 /* MGLSDKTestHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLSDKTestHelpers.swift; path = ../../darwin/test/MGLSDKTestHelpers.swift; sourceTree = ""; }; 4049C2A11DB6CE7800B3F799 /* MGLPointCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection.h; sourceTree = ""; }; @@ -473,7 +473,7 @@ CA0B3C052329F7E600E4B493 /* MGLTestAssertionHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLTestAssertionHandler.m; path = ../../darwin/test/MGLTestAssertionHandler.m; sourceTree = ""; }; CA0B3C082329FB4800E4B493 /* MGLOfflinePackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLOfflinePackTests.mm; path = ../../darwin/test/MGLOfflinePackTests.mm; sourceTree = ""; }; CA4045C4216720D700B356E1 /* MGLCluster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCluster.h; sourceTree = ""; }; - CA8FBC0C21A4A74300D1203C /* MGLRendererConfigurationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLRendererConfigurationTests.mm; path = ../../darwin/test/MGLRendererConfigurationTests.mm; sourceTree = ""; }; + CA8FBC0C21A4A74300D1203C /* MGLRendererConfigurationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLRendererConfigurationTests.m; path = ../../darwin/test/MGLRendererConfigurationTests.m; sourceTree = ""; }; CA9461A520884CCB0015EB12 /* MGLAnnotationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLAnnotationTests.m; path = test/MGLAnnotationTests.m; sourceTree = SOURCE_ROOT; }; CAD9D0AB22A88A32001B25EE /* MGLResourceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLResourceTests.mm; path = ../../darwin/test/MGLResourceTests.mm; sourceTree = ""; }; CF762DEE22DC7EFF00338472 /* MGLLoggingConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLoggingConfiguration.mm; sourceTree = ""; }; @@ -1214,7 +1214,7 @@ DAE6C3CA1CC34BD800DB3429 /* MGLOfflineRegionTests.m */, 55E2AD101E5B0A6900E8C587 /* MGLOfflineStorageTests.mm */, 35C5D84B1D6DD75B00E95907 /* MGLPredicateTests.mm */, - CA8FBC0C21A4A74300D1203C /* MGLRendererConfigurationTests.mm */, + CA8FBC0C21A4A74300D1203C /* MGLRendererConfigurationTests.m */, CAD9D0AB22A88A32001B25EE /* MGLResourceTests.mm */, DAE6C3CC1CC34BD800DB3429 /* MGLStyleTests.mm */, 556660D51E1D07E400E2C41B /* MGLVersionNumber.m */, @@ -1262,7 +1262,7 @@ DA35D9C4240916370013ECB0 /* MGLReachability.h */, DA35D9C5240916380013ECB0 /* MGLReachability.m */, 3EA9369A4C46957566058822 /* MGLRendererConfiguration.h */, - 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.mm */, + 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.m */, 92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */, DAE6C3571CC31E0400DB3429 /* MGLStyle.h */, 3537CA731D3F93A600380318 /* MGLStyle_Private.h */, @@ -1750,7 +1750,7 @@ 3529039C1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm in Sources */, DA8F25981D51CAC70010E6B5 /* MGLVectorTileSource.mm in Sources */, 352742A11D4C25BD00A1ECE6 /* MGLStyleValue.mm in Sources */, - 3EA93BA38DBB4B814B6C1FCC /* MGLRendererConfiguration.mm in Sources */, + 3EA93BA38DBB4B814B6C1FCC /* MGLRendererConfiguration.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1783,7 +1783,7 @@ CA0B3C092329FB4800E4B493 /* MGLOfflinePackTests.mm in Sources */, 170A82C4201FB6EC00943087 /* MGLHeatmapColorTests.mm in Sources */, 4031ACFC1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift in Sources */, - CA8FBC0D21A4A74300D1203C /* MGLRendererConfigurationTests.mm in Sources */, + CA8FBC0D21A4A74300D1203C /* MGLRendererConfigurationTests.m in Sources */, 4031AD031E9FD6AA00A3EA26 /* MGLSDKTestHelpers.swift in Sources */, DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.mm in Sources */, DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */, diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 49b8d3e9f6..de2fca1158 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -278,7 +278,8 @@ - (void)commonInit { MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration]; - auto renderer = std::make_unique(_mbglView->getRendererBackend(), config.scaleFactor, config.localFontFamilyName); + auto localFontFamilyName = config.localFontFamilyName ? std::string(config.localFontFamilyName.UTF8String) : nullptr; + auto renderer = std::make_unique(_mbglView->getRendererBackend(), config.scaleFactor, localFontFamilyName); BOOL enableCrossSourceCollisions = !config.perSourceCollisions; _rendererFrontend = std::make_unique(std::move(renderer), self, _mbglView->getRendererBackend(), true); diff --git a/vendor/mapbox-gl-native b/vendor/mapbox-gl-native index 55d5b976dc..fe73020b4a 160000 --- a/vendor/mapbox-gl-native +++ b/vendor/mapbox-gl-native @@ -1 +1 @@ -Subproject commit 55d5b976dcea60ad68873fdd4c035630b5912ca9 +Subproject commit fe73020b4a922781438a4f52d3d149b0347cec88