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

Allow specifying multiple fonts or font families for local font rendering #189

Merged
merged 2 commits into from
Apr 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion platform/darwin/src/MGLMapSnapshotter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -725,9 +725,10 @@ - (void)configureWithOptions:(MGLMapSnapshotOptions *)options {
.withAssetPath(NSBundle.mainBundle.resourceURL.path.UTF8String);

// Create the snapshotter
auto localFontFamilyName = config.localFontFamilyName ? std::string(config.localFontFamilyName.UTF8String) : nullptr;
_delegateHost = std::make_unique<MGLMapSnapshotterDelegateHost>(self);
_mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(
size, pixelRatio, resourceOptions, *_delegateHost, config.localFontFamilyName);
size, pixelRatio, resourceOptions, *_delegateHost, localFontFamilyName);

_mbglMapSnapshotter->setStyleURL(std::string(options.styleURL.absoluteString.UTF8String));

Expand Down
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.

38 changes: 31 additions & 7 deletions platform/darwin/test/MGLMapSnapshotterTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ BOOL MGLEqualImages(MGLImage *leftImage, MGLImage *rightImage) {
#endif
}

@interface MGLMapSnapshotterTests : XCTestCase <MGLMapSnapshotterDelegate>
@interface MGLMapSnapshotterTests : XCTestCase <MGLMapSnapshotterDelegate, MGLOfflineStorageDelegate>

@property (nonatomic) XCTestExpectation *styleLoadingExpectation;
@property (nonatomic, copy, nullable) void (^runtimeStylingActions)(MGLStyle *style);
Expand All @@ -62,10 +62,13 @@ - (void)setUp {
[super setUp];

[MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];

[MGLOfflineStorage sharedOfflineStorage].delegate = self;
}

- (void)tearDown {
[MGLAccountManager setAccessToken:nil];
[MGLOfflineStorage sharedOfflineStorage].delegate = nil;
self.styleLoadingExpectation = nil;
self.runtimeStylingActions = nil;
[super tearDown];
Expand Down Expand Up @@ -138,20 +141,26 @@ - (void)testDelegate {
}

- (void)testRuntimeStyling {
[self testRuntimeStylingActions:^(MGLStyle *style) {
[self testStyleURL:nil applyingRuntimeStylingActions:^(MGLStyle *style) {
MGLBackgroundStyleLayer *backgroundLayer = [[MGLBackgroundStyleLayer alloc] initWithIdentifier:@"background"];
backgroundLayer.backgroundColor = [NSExpression expressionForConstantValue:[MGLColor orangeColor]];
[style addLayer:backgroundLayer];
} expectedImageName:@"Fixtures/MGLMapSnapshotterTests/background"];
}

- (void)testLocalGlyphRendering {
[[NSUserDefaults standardUserDefaults] setObject:@[@"PingFang TC"] forKey:@"MGLIdeographicFontFamilyName"];
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"mixed" withExtension:@"json"];
[self testStyleURL:styleURL applyingRuntimeStylingActions:^(MGLStyle *style) {} expectedImageName:@"Fixtures/MGLMapSnapshotterTests/PingFang"];
}

/**
Tests that applying the given runtime styling actions on a blank style results in a snapshot image that matches the image with the given name in the asset catalog.

@param actions Runtime styling actions to apply to the blank style.
@param expectedImageName Name of the test fixture image in Media.xcassets.
*/
- (void)testRuntimeStylingActions:(void (^)(MGLStyle *style))actions expectedImageName:(NSString *)expectedImageName {
- (void)testStyleURL:(nullable NSURL *)styleURL applyingRuntimeStylingActions:(void (^)(MGLStyle *style))actions expectedImageName:(NSString *)expectedImageName {
self.styleLoadingExpectation = [self expectationWithDescription:@"Style should finish loading."];
XCTestExpectation *overlayExpectation = [self expectationWithDescription:@"Overlay handler should get called."];
XCTestExpectation *completionExpectation = [self expectationWithDescription:@"Completion handler should get called."];
Expand All @@ -163,9 +172,15 @@ - (void)testRuntimeStylingActions:(void (^)(MGLStyle *style))actions expectedIma
#endif
XCTAssertNotNil(expectedImage, @"Image fixture “%@” missing from Media.xcassets.", expectedImageName);

NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
if (!styleURL) {
styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
}
MGLMapCamera *camera = [MGLMapCamera camera];
MGLMapSnapshotOptions *options = [[MGLMapSnapshotOptions alloc] initWithStyleURL:styleURL camera:camera size:CGSizeMake(500, 500)];
camera.centerCoordinate = kCLLocationCoordinate2DInvalid;
camera.heading = -1;
camera.pitch = -1;
MGLMapSnapshotOptions *options = [[MGLMapSnapshotOptions alloc] initWithStyleURL:styleURL camera:camera size:expectedImage.size];
options.zoomLevel = -1;

MGLMapSnapshotter *snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options];
snapshotter.delegate = self;
Expand All @@ -183,8 +198,8 @@ - (void)testRuntimeStylingActions:(void (^)(MGLStyle *style))actions expectedIma
XCTAssertNil(error);
XCTAssertNotNil(snapshot);
if (snapshot) {
XCTAssertEqual(snapshot.image.size.width, 500);
XCTAssertEqual(snapshot.image.size.height, 500);
XCTAssertEqual(snapshot.image.size.width, expectedImage.size.width);
XCTAssertEqual(snapshot.image.size.height, expectedImage.size.height);
}
[completionExpectation fulfill];
}];
Expand All @@ -205,4 +220,13 @@ - (void)mapSnapshotter:(MGLMapSnapshotter *)snapshotter didFinishLoadingStyle:(M
[self.styleLoadingExpectation fulfill];
}

#pragma mark MGLOfflineStorageDelegate methods

- (NSURL *)offlineStorage:(MGLOfflineStorage *)storage URLForResourceOfKind:(MGLResourceKind)kind withURL:(NSURL *)url {
if (kind == MGLResourceKindGlyphs && [url.scheme isEqualToString:@"local"]) {
return [[NSBundle bundleForClass:[self class]] URLForResource:@"glyphs" withExtension:@"pbf"];
}
return url;
}

@end
Loading