Skip to content
This repository has been archived by the owner on Jun 21, 2023. It is now read-only.

Commit

Permalink
[ios, macos] Allow specifying multiple fonts or font families for loc…
Browse files Browse the repository at this point in the history
…al 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.
  • Loading branch information
1ec5 committed Mar 1, 2020
1 parent 87fac0b commit 98c7b4f
Show file tree
Hide file tree
Showing 15 changed files with 277 additions and 356 deletions.
3 changes: 2 additions & 1 deletion platform/darwin/src/MGLMapSnapshotter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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<mbgl::MapSnapshotter>(
style, size, pixelRatio, cameraOptions, coordinateBounds, config.localFontFamilyName, resourceOptions);
style, size, pixelRatio, cameraOptions, coordinateBounds, localFontFamilyName, resourceOptions);
}

@end
8 changes: 5 additions & 3 deletions platform/darwin/src/MGLRendererConfiguration.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#import "MGLFoundation.h"
#import <Foundation/Foundation.h>

#include <mbgl/util/optional.hpp>

NS_ASSUME_NONNULL_BEGIN

/**
Expand Down Expand Up @@ -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<std::string> 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
Expand All @@ -49,6 +49,8 @@ MGL_EXPORT
*/
@property (nonatomic, readonly) BOOL perSourceCollisions;

- (BOOL)perSourceCollisionsWithInfoDictionaryObject:(nullable id)infoDictionaryObject;

@end

NS_ASSUME_NONNULL_END
66 changes: 66 additions & 0 deletions platform/darwin/src/MGLRendererConfiguration.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#import "MGLRendererConfiguration.h"

#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#import <AppKit/AppKit.h>
#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
119 changes: 0 additions & 119 deletions platform/darwin/src/MGLRendererConfiguration.mm

This file was deleted.

162 changes: 162 additions & 0 deletions platform/darwin/test/MGLRendererConfigurationTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#import <Mapbox/Mapbox.h>
#import <XCTest/XCTest.h>
#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
Loading

0 comments on commit 98c7b4f

Please sign in to comment.