mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-02-10 17:22:45 +08:00
Summary: Dynamic Text Sizes for Text component. Text gains new prop - allowFontScaling (true by default). There is also AccessibilityManager module that allows you to tune multipliers per each content size category, but predefined multipliers are there. This could potentially break some apps so please test carefully.
224 lines
6.7 KiB
Objective-C
224 lines
6.7 KiB
Objective-C
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
#import "RCTConvert+ART.h"
|
|
|
|
#import "ARTLinearGradient.h"
|
|
#import "ARTPattern.h"
|
|
#import "ARTRadialGradient.h"
|
|
#import "ARTSolidColor.h"
|
|
#import "RCTLog.h"
|
|
|
|
@implementation RCTConvert (ART)
|
|
|
|
+ (CGPathRef)CGPath:(id)json
|
|
{
|
|
NSArray *arr = [self NSNumberArray:json];
|
|
|
|
NSUInteger count = [arr count];
|
|
|
|
#define NEXT_VALUE [self double:arr[i++]]
|
|
|
|
CGMutablePathRef path = CGPathCreateMutable();
|
|
CGPathMoveToPoint(path, NULL, 0, 0);
|
|
|
|
@try {
|
|
NSUInteger i = 0;
|
|
while (i < count) {
|
|
NSUInteger type = [arr[i++] unsignedIntegerValue];
|
|
switch (type) {
|
|
case 0:
|
|
CGPathMoveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
|
|
break;
|
|
case 1:
|
|
CGPathCloseSubpath(path);
|
|
break;
|
|
case 2:
|
|
CGPathAddLineToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
|
|
break;
|
|
case 3:
|
|
CGPathAddCurveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE);
|
|
break;
|
|
case 4:
|
|
CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0);
|
|
break;
|
|
default:
|
|
RCTLogError(@"Invalid CGPath type %zd at element %zd of %@", type, i, arr);
|
|
CGPathRelease(path);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
@catch (NSException *exception) {
|
|
RCTLogError(@"Invalid CGPath format: %@", arr);
|
|
CGPathRelease(path);
|
|
return NULL;
|
|
}
|
|
|
|
return (CGPathRef)CFAutorelease(path);
|
|
}
|
|
|
|
RCT_ENUM_CONVERTER(CTTextAlignment, (@{
|
|
@"auto": @(kCTTextAlignmentNatural),
|
|
@"left": @(kCTTextAlignmentLeft),
|
|
@"center": @(kCTTextAlignmentCenter),
|
|
@"right": @(kCTTextAlignmentRight),
|
|
@"justify": @(kCTTextAlignmentJustified),
|
|
}), kCTTextAlignmentNatural, integerValue)
|
|
|
|
// This takes a tuple of text lines and a font to generate a CTLine for each text line.
|
|
// This prepares everything for rendering a frame of text in ARTText.
|
|
+ (ARTTextFrame)ARTTextFrame:(id)json
|
|
{
|
|
NSDictionary *dict = [self NSDictionary:json];
|
|
ARTTextFrame frame;
|
|
frame.count = 0;
|
|
|
|
NSArray *lines = [self NSArray:dict[@"lines"]];
|
|
NSUInteger lineCount = [lines count];
|
|
if (lineCount == 0) {
|
|
return frame;
|
|
}
|
|
|
|
NSDictionary *fontDict = dict[@"font"];
|
|
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] scaleMultiplier:1.0];
|
|
if (!font) {
|
|
return frame;
|
|
}
|
|
|
|
// Create a dictionary for this font
|
|
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
|
|
(NSString *)kCTFontAttributeName: (__bridge id)font,
|
|
(NSString *)kCTForegroundColorFromContextAttributeName: @YES
|
|
};
|
|
|
|
// Set up text frame with font metrics
|
|
CGFloat size = CTFontGetSize(font);
|
|
frame.count = lineCount;
|
|
frame.baseLine = size; // estimate base line
|
|
frame.lineHeight = size * 1.1; // Base on ART canvas line height estimate
|
|
frame.lines = malloc(sizeof(CTLineRef) * lineCount);
|
|
frame.widths = malloc(sizeof(CGFloat) * lineCount);
|
|
|
|
[lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) {
|
|
|
|
CFStringRef string = (__bridge CFStringRef)text;
|
|
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
|
|
CTLineRef line = CTLineCreateWithAttributedString(attrString);
|
|
CFRelease(attrString);
|
|
|
|
frame.lines[i] = line;
|
|
frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
|
|
}];
|
|
|
|
return frame;
|
|
}
|
|
|
|
+ (ARTCGFloatArray)ARTCGFloatArray:(id)json
|
|
{
|
|
NSArray *arr = [self NSNumberArray:json];
|
|
NSUInteger count = arr.count;
|
|
|
|
ARTCGFloatArray array;
|
|
array.count = count;
|
|
array.array = NULL;
|
|
|
|
if (count) {
|
|
// Ideally, these arrays should already use the same memory layout.
|
|
// In that case we shouldn't need this new malloc.
|
|
array.array = malloc(sizeof(CGFloat) * count);
|
|
for (NSUInteger i = 0; i < count; i++) {
|
|
array.array[i] = [arr[i] doubleValue];
|
|
}
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
+ (ARTBrush *)ARTBrush:(id)json
|
|
{
|
|
NSArray *arr = [self NSArray:json];
|
|
NSUInteger type = [self NSUInteger:arr[0]];
|
|
switch (type) {
|
|
case 0: // solid color
|
|
// These are probably expensive allocations since it's often the same value.
|
|
// We should memoize colors but look ups may be just as expensive.
|
|
return [[ARTSolidColor alloc] initWithArray:arr];
|
|
case 1: // linear gradient
|
|
return [[ARTLinearGradient alloc] initWithArray:arr];
|
|
case 2: // radial gradient
|
|
return [[ARTRadialGradient alloc] initWithArray:arr];
|
|
case 3: // pattern
|
|
return [[ARTPattern alloc] initWithArray:arr];
|
|
default:
|
|
RCTLogError(@"Unknown brush type: %zd", type);
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset
|
|
{
|
|
NSArray *arr = [self NSArray:json];
|
|
if (arr.count < offset + 2) {
|
|
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 2 + offset, arr);
|
|
return CGPointZero;
|
|
}
|
|
return (CGPoint){
|
|
[self CGFloat:arr[offset]],
|
|
[self CGFloat:arr[offset + 1]],
|
|
};
|
|
}
|
|
|
|
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset
|
|
{
|
|
NSArray *arr = [self NSArray:json];
|
|
if (arr.count < offset + 4) {
|
|
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr);
|
|
return CGRectZero;
|
|
}
|
|
return (CGRect){
|
|
{[self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]]},
|
|
{[self CGFloat:arr[offset + 2]], [self CGFloat:arr[offset + 3]]},
|
|
};
|
|
}
|
|
|
|
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset
|
|
{
|
|
NSArray *arr = [self NSArray:json];
|
|
if (arr.count < offset + 4) {
|
|
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr);
|
|
return NULL;
|
|
}
|
|
return [self CGColor:[arr subarrayWithRange:(NSRange){offset, 4}]];
|
|
}
|
|
|
|
+ (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset
|
|
{
|
|
NSArray *arr = [self NSArray:json];
|
|
if (arr.count < offset) {
|
|
RCTLogError(@"Too few elements in array (expected at least %zd): %@", offset, arr);
|
|
return NULL;
|
|
}
|
|
arr = [arr subarrayWithRange:(NSRange){offset, arr.count - offset}];
|
|
ARTCGFloatArray colorsAndOffsets = [self ARTCGFloatArray:arr];
|
|
size_t stops = colorsAndOffsets.count / 5;
|
|
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
|
|
CGGradientRef gradient = CGGradientCreateWithColorComponents(
|
|
rgb,
|
|
colorsAndOffsets.array,
|
|
colorsAndOffsets.array + stops * 4,
|
|
stops
|
|
);
|
|
CGColorSpaceRelease(rgb);
|
|
free(colorsAndOffsets.array);
|
|
return (CGGradientRef)CFAutorelease(gradient);
|
|
}
|
|
|
|
@end
|