Fabric/Text: textlayoutmanager
Summary: TextLayoutManager measures and renders text using iOS specific APIs (CoreText & TextKit). By desing, only this module should contain platfrom-specific text functionality. Reviewed By: mdvacca Differential Revision: D7751852 fbshipit-source-id: fd6e1907df617fe5a4479ea08f207946765b3a45
This commit is contained in:
parent
62576bcb78
commit
05890a5942
|
@ -185,6 +185,15 @@ Pod::Spec.new do |s|
|
|||
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
|
||||
end
|
||||
|
||||
ss.subspec "textlayoutmanager" do |sss|
|
||||
sss.dependency "Folly", folly_version
|
||||
sss.compiler_flags = folly_compiler_flags
|
||||
sss.source_files = "ReactCommon/fabric/textlayoutmanager/**/*.{cpp,h}"
|
||||
sss.exclude_files = "**/tests/*"
|
||||
sss.header_dir = "fabric/textlayoutmanager"
|
||||
sss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/Folly\"" }
|
||||
end
|
||||
|
||||
ss.subspec "uimanager" do |sss|
|
||||
sss.dependency "Folly", folly_version
|
||||
sss.compiler_flags = folly_compiler_flags
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
load("//configurations/buck/apple:flag_defs.bzl", "get_debug_preprocessor_flags", "get_fbobjc_enable_exception_lang_compiler_flags")
|
||||
load("//ReactNative:DEFS.bzl", "IS_OSS_BUILD", "react_native_xplat_target", "rn_xplat_cxx_library", "get_apple_inspector_flags", "APPLE")
|
||||
|
||||
APPLE_COMPILER_FLAGS = []
|
||||
|
||||
if not IS_OSS_BUILD:
|
||||
load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_static_library_ios_flags", "flags")
|
||||
APPLE_COMPILER_FLAGS = flags.get_flag_value(get_static_library_ios_flags(), 'compiler_flags')
|
||||
|
||||
rn_xplat_cxx_library(
|
||||
name = "textlayoutmanager",
|
||||
srcs = glob(
|
||||
[
|
||||
"**/*.cpp",
|
||||
"**/*.mm",
|
||||
],
|
||||
excludes = glob(["tests/**/*.cpp"]),
|
||||
),
|
||||
headers = glob(
|
||||
["**/*.h"],
|
||||
excludes = glob(["tests/**/*.h"]),
|
||||
),
|
||||
header_namespace = "",
|
||||
exported_headers = subdir_glob(
|
||||
[
|
||||
("", "*.h"),
|
||||
],
|
||||
prefix = "fabric/textlayoutmanager",
|
||||
),
|
||||
compiler_flags = [
|
||||
"-fexceptions",
|
||||
"-frtti",
|
||||
"-std=c++14",
|
||||
"-Wall",
|
||||
],
|
||||
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
|
||||
fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + get_apple_inspector_flags(),
|
||||
fbobjc_tests = [
|
||||
":tests",
|
||||
],
|
||||
force_static = True,
|
||||
macosx_tests_override = [],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
"$SDKROOT/System/Library/Frameworks/QuartzCore.framework",
|
||||
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
|
||||
],
|
||||
lang_compiler_flags = get_fbobjc_enable_exception_lang_compiler_flags(),
|
||||
preprocessor_flags = get_debug_preprocessor_flags() + [
|
||||
"-DLOG_TAG=\"ReactNative\"",
|
||||
"-DWITH_FBSYSTRACE=1",
|
||||
],
|
||||
tests = [],
|
||||
visibility = ["PUBLIC"],
|
||||
deps = [
|
||||
"xplat//fbsystrace:fbsystrace",
|
||||
"xplat//folly:headers_only",
|
||||
"xplat//folly:memory",
|
||||
"xplat//folly:molly",
|
||||
"xplat//third-party/glog:glog",
|
||||
react_native_xplat_target("fabric/attributedstring:attributedstring"),
|
||||
react_native_xplat_target("fabric/core:core"),
|
||||
react_native_xplat_target("fabric/debug:debug"),
|
||||
react_native_xplat_target("fabric/graphics:graphics"),
|
||||
],
|
||||
)
|
||||
|
||||
if not IS_OSS_BUILD:
|
||||
load("@xplat//build_defs:fb_xplat_cxx_test.bzl", "fb_xplat_cxx_test")
|
||||
|
||||
fb_xplat_cxx_test(
|
||||
name = "tests",
|
||||
srcs = glob(["tests/**/*.cpp"]),
|
||||
headers = glob(["tests/**/*.h"]),
|
||||
contacts = ["oncall+react_native@xmail.facebook.com"],
|
||||
compiler_flags = [
|
||||
"-fexceptions",
|
||||
"-frtti",
|
||||
"-std=c++14",
|
||||
"-Wall",
|
||||
],
|
||||
platforms = APPLE,
|
||||
deps = [
|
||||
"xplat//folly:molly",
|
||||
"xplat//third-party/gmock:gtest",
|
||||
":textlayoutmanager",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface NSTextStorage (FontScaling)
|
||||
|
||||
- (void)scaleFontSizeToFitSize:(CGSize)size
|
||||
minimumFontSize:(CGFloat)minimumFontSize
|
||||
maximumFontSize:(CGFloat)maximumFontSize;
|
||||
|
||||
- (void)scaleFontSizeWithRatio:(CGFloat)ratio
|
||||
minimumFontSize:(CGFloat)minimumFontSize
|
||||
maximumFontSize:(CGFloat)maximumFontSize;
|
||||
|
||||
@end
|
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "NSTextStorage+FontScaling.h"
|
||||
|
||||
typedef NS_OPTIONS(NSInteger, RCTTextSizeComparisonOptions) {
|
||||
RCTTextSizeComparisonSmaller = 1 << 0,
|
||||
RCTTextSizeComparisonLarger = 1 << 1,
|
||||
RCTTextSizeComparisonWithinRange = 1 << 2,
|
||||
};
|
||||
|
||||
@implementation NSTextStorage (FontScaling)
|
||||
|
||||
- (void)scaleFontSizeToFitSize:(CGSize)size
|
||||
minimumFontSize:(CGFloat)minimumFontSize
|
||||
maximumFontSize:(CGFloat)maximumFontSize
|
||||
{
|
||||
CGFloat bottomRatio = 1.0/128.0;
|
||||
CGFloat topRatio = 128.0;
|
||||
CGFloat ratio = 1.0;
|
||||
|
||||
NSAttributedString *originalAttributedString = [self copy];
|
||||
|
||||
CGFloat lastRatioWhichFits = 0.02;
|
||||
|
||||
while (true) {
|
||||
[self scaleFontSizeWithRatio:ratio
|
||||
minimumFontSize:minimumFontSize
|
||||
maximumFontSize:maximumFontSize];
|
||||
|
||||
RCTTextSizeComparisonOptions comparsion =
|
||||
[self compareToSize:size thresholdRatio:0.01];
|
||||
|
||||
if (
|
||||
(comparsion & RCTTextSizeComparisonWithinRange) &&
|
||||
(comparsion & RCTTextSizeComparisonSmaller)
|
||||
) {
|
||||
return;
|
||||
} else if (comparsion & RCTTextSizeComparisonSmaller) {
|
||||
bottomRatio = ratio;
|
||||
lastRatioWhichFits = ratio;
|
||||
} else {
|
||||
topRatio = ratio;
|
||||
}
|
||||
|
||||
ratio = (topRatio + bottomRatio) / 2.0;
|
||||
|
||||
CGFloat kRatioThreshold = 0.005;
|
||||
if (
|
||||
ABS(topRatio - bottomRatio) < kRatioThreshold ||
|
||||
ABS(topRatio - ratio) < kRatioThreshold ||
|
||||
ABS(bottomRatio - ratio) < kRatioThreshold
|
||||
) {
|
||||
[self replaceCharactersInRange:(NSRange){0, self.length}
|
||||
withAttributedString:originalAttributedString];
|
||||
|
||||
[self scaleFontSizeWithRatio:lastRatioWhichFits
|
||||
minimumFontSize:minimumFontSize
|
||||
maximumFontSize:maximumFontSize];
|
||||
return;
|
||||
}
|
||||
|
||||
[self replaceCharactersInRange:(NSRange){0, self.length}
|
||||
withAttributedString:originalAttributedString];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (RCTTextSizeComparisonOptions)compareToSize:(CGSize)size thresholdRatio:(CGFloat)thresholdRatio
|
||||
{
|
||||
NSLayoutManager *layoutManager = self.layoutManagers.firstObject;
|
||||
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
|
||||
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
|
||||
// Does it fit the text container?
|
||||
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
|
||||
NSRange truncatedGlyphRange = [layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:glyphRange.length - 1];
|
||||
|
||||
if (truncatedGlyphRange.location != NSNotFound) {
|
||||
return RCTTextSizeComparisonLarger;
|
||||
}
|
||||
|
||||
CGSize measuredSize = [layoutManager usedRectForTextContainer:textContainer].size;
|
||||
|
||||
// Does it fit the size?
|
||||
BOOL fitsSize =
|
||||
size.width >= measuredSize.width &&
|
||||
size.height >= measuredSize.height;
|
||||
|
||||
CGSize thresholdSize = (CGSize){
|
||||
size.width * thresholdRatio,
|
||||
size.height * thresholdRatio,
|
||||
};
|
||||
|
||||
RCTTextSizeComparisonOptions result = 0;
|
||||
|
||||
result |= (fitsSize) ? RCTTextSizeComparisonSmaller : RCTTextSizeComparisonLarger;
|
||||
|
||||
if (ABS(measuredSize.width - size.width) < thresholdSize.width) {
|
||||
result = result | RCTTextSizeComparisonWithinRange;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)scaleFontSizeWithRatio:(CGFloat)ratio
|
||||
minimumFontSize:(CGFloat)minimumFontSize
|
||||
maximumFontSize:(CGFloat)maximumFontSize
|
||||
{
|
||||
[self beginEditing];
|
||||
|
||||
[self enumerateAttribute:NSFontAttributeName
|
||||
inRange:(NSRange){0, self.length}
|
||||
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
||||
usingBlock:
|
||||
^(UIFont *_Nullable font, NSRange range, BOOL *_Nonnull stop) {
|
||||
if (!font) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat fontSize = MAX(MIN(font.pointSize * ratio, maximumFontSize), minimumFontSize);
|
||||
|
||||
[self addAttribute:NSFontAttributeName
|
||||
value:[font fontWithSize:fontSize]
|
||||
range:range];
|
||||
}
|
||||
];
|
||||
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include <fabric/attributedstring/AttributedString.h>
|
||||
#include <fabric/attributedstring/TextAttributes.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const RCTAttributedStringIsHighlightedAttributeName = @"IsHighlighted";
|
||||
NSString *const RCTAttributedStringReactTagAttributeName = @"ReactTag";
|
||||
|
||||
/**
|
||||
* Constructs ready-to-render `NSAttributedString` by given `AttributedString`.
|
||||
*/
|
||||
NSAttributedString *RCTNSAttributedStringFromAttributedString(const facebook::react::AttributedString &attributedString);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,227 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTAttributedTextUtils.h"
|
||||
|
||||
#include <fabric/core/LayoutableShadowNode.h>
|
||||
#include <fabric/textlayoutmanager/RCTFontProperties.h>
|
||||
#include <fabric/textlayoutmanager/RCTFontUtils.h>
|
||||
#include <fabric/textlayoutmanager/RCTTextPrimitivesConversions.h>
|
||||
|
||||
inline static UIFont *RCTEffectiveFontFromTextAttributes(const TextAttributes &textAttributes) {
|
||||
NSString *fontFamily = [NSString stringWithCString:textAttributes.fontFamily.c_str()
|
||||
encoding:NSASCIIStringEncoding];
|
||||
|
||||
RCTFontProperties fontProperties;
|
||||
fontProperties.family = fontFamily;
|
||||
fontProperties.size = textAttributes.fontSize;
|
||||
fontProperties.style = textAttributes.fontStyle.has_value() ? RCTFontStyleFromFontStyle(textAttributes.fontStyle.value()) : RCTFontStyleUndefined;
|
||||
fontProperties.variant = textAttributes.fontVariant.has_value() ? RCTFontVariantFromFontVariant(textAttributes.fontVariant.value()) : RCTFontVariantDefault;
|
||||
fontProperties.weight = textAttributes.fontWeight.has_value() ? CGFloat(textAttributes.fontWeight.value()) : NAN;
|
||||
fontProperties.sizeMultiplier = textAttributes.fontSizeMultiplier;
|
||||
|
||||
return RCTFontWithFontProperties(fontProperties);
|
||||
}
|
||||
|
||||
inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const TextAttributes &textAttributes) {
|
||||
return textAttributes.allowFontScaling.value_or(true) && !isnan(textAttributes.fontSizeMultiplier) ? textAttributes.fontSizeMultiplier : 1.0;
|
||||
}
|
||||
|
||||
inline static UIColor *RCTEffectiveForegroundColorFromTextAttributes(const TextAttributes &textAttributes) {
|
||||
UIColor *effectiveForegroundColor = RCTUIColorFromSharedColor(textAttributes.foregroundColor) ?: [UIColor blackColor];
|
||||
|
||||
if (!isnan(textAttributes.opacity)) {
|
||||
effectiveForegroundColor =
|
||||
[effectiveForegroundColor colorWithAlphaComponent:CGColorGetAlpha(effectiveForegroundColor.CGColor) * textAttributes.opacity];
|
||||
}
|
||||
|
||||
return effectiveForegroundColor;
|
||||
}
|
||||
|
||||
inline static UIColor *RCTEffectiveBackgroundColorFromTextAttributes(const TextAttributes &textAttributes) {
|
||||
UIColor *effectiveBackgroundColor = RCTUIColorFromSharedColor(textAttributes.backgroundColor);
|
||||
|
||||
if (effectiveBackgroundColor && !isnan(textAttributes.opacity)) {
|
||||
effectiveBackgroundColor =
|
||||
[effectiveBackgroundColor colorWithAlphaComponent:CGColorGetAlpha(effectiveBackgroundColor.CGColor) * textAttributes.opacity];
|
||||
}
|
||||
|
||||
return effectiveBackgroundColor ?: [UIColor clearColor];
|
||||
}
|
||||
|
||||
static NSDictionary<NSAttributedStringKey, id> *RCTNSTextAttributesFromTextAttributes(const TextAttributes &textAttributes) {
|
||||
NSMutableDictionary<NSAttributedStringKey, id> *attributes =
|
||||
[NSMutableDictionary dictionaryWithCapacity:10];
|
||||
|
||||
// Font
|
||||
UIFont *font = RCTEffectiveFontFromTextAttributes(textAttributes);
|
||||
if (font) {
|
||||
attributes[NSFontAttributeName] = font;
|
||||
}
|
||||
|
||||
// Colors
|
||||
UIColor *effectiveForegroundColor = RCTEffectiveForegroundColorFromTextAttributes(textAttributes);
|
||||
|
||||
if (textAttributes.foregroundColor || !isnan(textAttributes.opacity)) {
|
||||
attributes[NSForegroundColorAttributeName] = effectiveForegroundColor;
|
||||
}
|
||||
|
||||
if (textAttributes.backgroundColor || !isnan(textAttributes.opacity)) {
|
||||
attributes[NSBackgroundColorAttributeName] = RCTEffectiveBackgroundColorFromTextAttributes(textAttributes);
|
||||
}
|
||||
|
||||
// Kerning
|
||||
if (!isnan(textAttributes.letterSpacing)) {
|
||||
attributes[NSKernAttributeName] = @(textAttributes.letterSpacing);
|
||||
}
|
||||
|
||||
// Paragraph Style
|
||||
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
||||
BOOL isParagraphStyleUsed = NO;
|
||||
if (textAttributes.alignment.has_value()) {
|
||||
TextAlignment textAlignment = textAttributes.alignment.value_or(TextAlignment::Natural);
|
||||
if (textAttributes.layoutDirection.value_or(LayoutDirection::LeftToRight) == LayoutDirection::RightToLeft) {
|
||||
if (textAlignment == TextAlignment::Right) {
|
||||
textAlignment = TextAlignment::Left;
|
||||
} else if (textAlignment == TextAlignment::Left) {
|
||||
textAlignment = TextAlignment::Right;
|
||||
}
|
||||
}
|
||||
|
||||
paragraphStyle.alignment =
|
||||
RCTNSTextAlignmentFromTextAlignment(textAlignment);
|
||||
isParagraphStyleUsed = YES;
|
||||
}
|
||||
|
||||
if (textAttributes.baseWritingDirection.has_value()) {
|
||||
paragraphStyle.baseWritingDirection =
|
||||
RCTNSWritingDirectionFromWritingDirection(textAttributes.baseWritingDirection.value());
|
||||
isParagraphStyleUsed = YES;
|
||||
}
|
||||
|
||||
if (!isnan(textAttributes.lineHeight)) {
|
||||
CGFloat lineHeight =
|
||||
textAttributes.lineHeight * RCTEffectiveFontSizeMultiplierFromTextAttributes(textAttributes);
|
||||
paragraphStyle.minimumLineHeight = lineHeight;
|
||||
paragraphStyle.maximumLineHeight = lineHeight;
|
||||
isParagraphStyleUsed = YES;
|
||||
}
|
||||
|
||||
if (isParagraphStyleUsed) {
|
||||
attributes[NSParagraphStyleAttributeName] = paragraphStyle;
|
||||
}
|
||||
|
||||
// Decoration
|
||||
if (textAttributes.textDecorationLineType.value_or(TextDecorationLineType::None) != TextDecorationLineType::None) {
|
||||
auto textDecorationLineType = textAttributes.textDecorationLineType.value();
|
||||
|
||||
NSUnderlineStyle style =
|
||||
RCTNSUnderlineStyleFromStyleAndPattern(
|
||||
textAttributes.textDecorationLineStyle.value_or(TextDecorationLineStyle::Single),
|
||||
textAttributes.textDecorationLinePattern.value_or(TextDecorationLinePattern::Solid)
|
||||
);
|
||||
|
||||
UIColor *textDecorationColor = RCTUIColorFromSharedColor(textAttributes.textDecorationColor);
|
||||
|
||||
// Underline
|
||||
if (textDecorationLineType == TextDecorationLineType::Underline ||
|
||||
textDecorationLineType == TextDecorationLineType::UnderlineStrikethrough) {
|
||||
|
||||
attributes[NSUnderlineStyleAttributeName] = @(style);
|
||||
|
||||
if (textDecorationColor) {
|
||||
attributes[NSUnderlineColorAttributeName] = textDecorationColor;
|
||||
}
|
||||
}
|
||||
|
||||
// Strikethrough
|
||||
if (textDecorationLineType == TextDecorationLineType::Strikethrough ||
|
||||
textDecorationLineType == TextDecorationLineType::UnderlineStrikethrough) {
|
||||
|
||||
attributes[NSStrikethroughStyleAttributeName] = @(style);
|
||||
|
||||
if (textDecorationColor) {
|
||||
attributes[NSStrikethroughColorAttributeName] = textDecorationColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shadow
|
||||
if (textAttributes.textShadowOffset.has_value()) {
|
||||
auto textShadowOffset = textAttributes.textShadowOffset.value();
|
||||
NSShadow *shadow = [NSShadow new];
|
||||
shadow.shadowOffset = CGSize {textShadowOffset.x, textShadowOffset.y};
|
||||
shadow.shadowBlurRadius = textAttributes.textShadowRadius;
|
||||
shadow.shadowColor = RCTUIColorFromSharedColor(textAttributes.textShadowColor);
|
||||
attributes[NSShadowAttributeName] = shadow;
|
||||
}
|
||||
|
||||
// Special
|
||||
if (textAttributes.isHighlighted) {
|
||||
attributes[RCTAttributedStringIsHighlightedAttributeName] = @YES;
|
||||
}
|
||||
|
||||
return [attributes copy];
|
||||
}
|
||||
|
||||
NSAttributedString *RCTNSAttributedStringFromAttributedString(const AttributedString &attributedString) {
|
||||
NSMutableAttributedString *nsAttributedString = [[NSMutableAttributedString alloc] init];
|
||||
|
||||
[nsAttributedString beginEditing];
|
||||
|
||||
for (auto fragment : attributedString.getFragments()) {
|
||||
NSAttributedString *nsAttributedStringFragment;
|
||||
|
||||
SharedLayoutableShadowNode layoutableShadowNode =
|
||||
std::dynamic_pointer_cast<const LayoutableShadowNode>(fragment.shadowNode);
|
||||
|
||||
if (layoutableShadowNode) {
|
||||
auto layoutMetrics = layoutableShadowNode->getLayoutMetrics();
|
||||
CGRect bounds = {
|
||||
.origin = {
|
||||
.x = layoutMetrics.frame.origin.x,
|
||||
.y = layoutMetrics.frame.origin.y
|
||||
},
|
||||
.size = {
|
||||
.width = layoutMetrics.frame.size.width,
|
||||
.height = layoutMetrics.frame.size.height
|
||||
}
|
||||
};
|
||||
|
||||
NSTextAttachment *attachment = [NSTextAttachment new];
|
||||
attachment.bounds = bounds;
|
||||
|
||||
nsAttributedStringFragment = [NSAttributedString attributedStringWithAttachment:attachment];
|
||||
} else {
|
||||
NSString *string =
|
||||
[NSString stringWithCString:fragment.string.c_str()
|
||||
encoding:NSASCIIStringEncoding];
|
||||
|
||||
nsAttributedStringFragment =
|
||||
[[NSAttributedString alloc] initWithString:string
|
||||
attributes:RCTNSTextAttributesFromTextAttributes(fragment.textAttributes)];
|
||||
}
|
||||
|
||||
NSMutableAttributedString *nsMutableAttributedStringFragment =
|
||||
[[NSMutableAttributedString alloc] initWithAttributedString:nsAttributedStringFragment];
|
||||
|
||||
if (fragment.shadowNode) {
|
||||
NSDictionary<NSAttributedStringKey, id> *additionalTextAttributes = @{
|
||||
RCTAttributedStringReactTagAttributeName: @(fragment.shadowNode->getTag())
|
||||
};
|
||||
|
||||
[nsMutableAttributedStringFragment setAttributes:additionalTextAttributes
|
||||
range:NSMakeRange(0, nsMutableAttributedStringFragment.length)];
|
||||
}
|
||||
|
||||
[nsAttributedString appendAttributedString:nsMutableAttributedStringFragment];
|
||||
}
|
||||
|
||||
[nsAttributedString endEditing];
|
||||
|
||||
return nsAttributedString;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTFontStyle) {
|
||||
RCTFontStyleUndefined = -1,
|
||||
RCTFontStyleNormal,
|
||||
RCTFontStyleItalic,
|
||||
RCTFontStyleOblique,
|
||||
};
|
||||
|
||||
typedef NS_OPTIONS(NSInteger, RCTFontVariant) {
|
||||
RCTFontVariantUndefined = -1,
|
||||
RCTFontVariantDefault = 0,
|
||||
RCTFontVariantSmallCaps = 1 << 1,
|
||||
RCTFontVariantOldstyleNums = 1 << 2,
|
||||
RCTFontVariantLiningNums = 1 << 3,
|
||||
RCTFontVariantTabularNums = 1 << 4,
|
||||
RCTFontVariantProportionalNums = 1 << 5,
|
||||
};
|
||||
|
||||
struct RCTFontProperties {
|
||||
NSString *family;
|
||||
CGFloat size;
|
||||
UIFontWeight weight;
|
||||
RCTFontStyle style;
|
||||
RCTFontVariant variant;
|
||||
CGFloat sizeMultiplier;
|
||||
};
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <fabric/textlayoutmanager/RCTFontProperties.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* Returns UIFont instance corresponded to given font properties.
|
||||
*/
|
||||
UIFont *RCTFontWithFontProperties(RCTFontProperties fontProperties);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTFontUtils.h"
|
||||
|
||||
#import <mutex>
|
||||
|
||||
static RCTFontProperties RCTDefaultFontProperties() {
|
||||
static RCTFontProperties defaultFontProperties;
|
||||
static dispatch_once_t onceToken;
|
||||
|
||||
dispatch_once(&onceToken, ^{
|
||||
defaultFontProperties.size = 14;
|
||||
defaultFontProperties.family =
|
||||
[UIFont systemFontOfSize:defaultFontProperties.size].familyName;
|
||||
defaultFontProperties.style = RCTFontStyleNormal;
|
||||
defaultFontProperties.variant = RCTFontVariantDefault;
|
||||
defaultFontProperties.sizeMultiplier = 1.0;
|
||||
});
|
||||
|
||||
return defaultFontProperties;
|
||||
}
|
||||
|
||||
static RCTFontProperties RCTResolveFontProperties(RCTFontProperties fontProperties) {
|
||||
RCTFontProperties defaultFontProperties = RCTDefaultFontProperties();
|
||||
fontProperties.family = fontProperties.family.length && ![fontProperties.family isEqualToString:@"System"] ? fontProperties.family : defaultFontProperties.family;
|
||||
fontProperties.size = !isnan(fontProperties.size) ? fontProperties.size : defaultFontProperties.size;
|
||||
fontProperties.weight = !isnan(fontProperties.weight) ? fontProperties.weight : defaultFontProperties.weight;
|
||||
fontProperties.style = fontProperties.style != RCTFontStyleUndefined ? fontProperties.style : defaultFontProperties.style;
|
||||
fontProperties.variant = fontProperties.variant != RCTFontVariantUndefined ? fontProperties.variant : defaultFontProperties.variant;
|
||||
return fontProperties;
|
||||
}
|
||||
|
||||
static UIFontWeight RCTGetFontWeight(UIFont *font) {
|
||||
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
|
||||
return [traits[UIFontWeightTrait] doubleValue];
|
||||
}
|
||||
|
||||
static RCTFontStyle RCTGetFontStyle(UIFont *font) {
|
||||
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
|
||||
UIFontDescriptorSymbolicTraits symbolicTraits = [traits[UIFontSymbolicTrait] unsignedIntValue];
|
||||
if (symbolicTraits & UIFontDescriptorTraitItalic) {
|
||||
return RCTFontStyleItalic;
|
||||
}
|
||||
|
||||
return RCTFontStyleNormal;
|
||||
}
|
||||
|
||||
static NSArray *RCTFontFeatures(RCTFontVariant fontVariant) {
|
||||
// FIXME:
|
||||
return @[];
|
||||
}
|
||||
|
||||
static UIFont *RCTDefaultFontWithFontProperties(RCTFontProperties fontProperties) {
|
||||
static NSCache *fontCache;
|
||||
static std::mutex fontCacheMutex;
|
||||
|
||||
NSString *cacheKey = [NSString stringWithFormat:@"%.1f/%.2f", fontProperties.size, fontProperties.weight];
|
||||
UIFont *font;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fontCacheMutex);
|
||||
if (!fontCache) {
|
||||
fontCache = [NSCache new];
|
||||
}
|
||||
font = [fontCache objectForKey:cacheKey];
|
||||
}
|
||||
|
||||
if (!font) {
|
||||
font = [UIFont systemFontOfSize:fontProperties.size
|
||||
weight:fontProperties.weight];
|
||||
|
||||
if (fontProperties.variant == RCTFontStyleItalic) {
|
||||
UIFontDescriptor *fontDescriptor = [font fontDescriptor];
|
||||
UIFontDescriptorSymbolicTraits symbolicTraits = fontDescriptor.symbolicTraits;
|
||||
|
||||
symbolicTraits |= UIFontDescriptorTraitItalic;
|
||||
|
||||
fontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:symbolicTraits];
|
||||
font = [UIFont fontWithDescriptor:fontDescriptor size:fontProperties.size];
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fontCacheMutex);
|
||||
[fontCache setObject:font forKey:cacheKey];
|
||||
}
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
UIFont *RCTFontWithFontProperties(RCTFontProperties fontProperties) {
|
||||
RCTFontProperties defaultFontProperties = RCTDefaultFontProperties();
|
||||
fontProperties = RCTResolveFontProperties(fontProperties);
|
||||
|
||||
CGFloat effectiveFontSize = fontProperties.sizeMultiplier * fontProperties.size;
|
||||
UIFont *font;
|
||||
if ([fontProperties.family isEqualToString:defaultFontProperties.family]) {
|
||||
// Handle system font as special case. This ensures that we preserve
|
||||
// the specific metrics of the standard system font as closely as possible.
|
||||
font = RCTDefaultFontWithFontProperties(fontProperties);
|
||||
} else {
|
||||
NSArray<NSString *> *fontNames =
|
||||
[UIFont fontNamesForFamilyName:fontProperties.family];
|
||||
|
||||
if (fontNames.count == 0) {
|
||||
// Gracefully handle being given a font name rather than font family, for
|
||||
// example: "Helvetica Light Oblique" rather than just "Helvetica".
|
||||
font = [UIFont fontWithName:fontProperties.family size:effectiveFontSize];
|
||||
|
||||
if (!font) {
|
||||
// Failback to system font.
|
||||
font = [UIFont systemFontOfSize:effectiveFontSize weight:fontProperties.weight];
|
||||
}
|
||||
} else {
|
||||
// Get the closest font that matches the given weight for the fontFamily
|
||||
CGFloat closestWeight = INFINITY;
|
||||
for (NSString *name in fontNames) {
|
||||
UIFont *fontMatch = [UIFont fontWithName:name size:effectiveFontSize];
|
||||
|
||||
if (RCTGetFontStyle(fontMatch) != fontProperties.style) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CGFloat testWeight = RCTGetFontWeight(fontMatch);
|
||||
if (ABS(testWeight - fontProperties.weight) < ABS(closestWeight - fontProperties.weight)) {
|
||||
font = fontMatch;
|
||||
closestWeight = testWeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (!font) {
|
||||
// If we still don't have a match at least return the first font in the fontFamily
|
||||
// This is to support built-in font Zapfino and other custom single font families like Impact
|
||||
font = [UIFont fontWithName:fontNames[0] size:effectiveFontSize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply font variants to font object.
|
||||
if (fontProperties.variant != RCTFontVariantDefault) {
|
||||
NSArray *fontFeatures = RCTFontFeatures(fontProperties.variant);
|
||||
UIFontDescriptor *fontDescriptor =
|
||||
[font.fontDescriptor fontDescriptorByAddingAttributes:@{UIFontDescriptorFeatureSettingsAttribute: fontFeatures}];
|
||||
font = [UIFont fontWithDescriptor:fontDescriptor size:effectiveFontSize];
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <fabric/core/LayoutConstraints.h>
|
||||
#import <fabric/graphics/Geometry.h>
|
||||
#import <fabric/attributedstring/AttributedString.h>
|
||||
#import <fabric/attributedstring/ParagraphAttributes.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* iOS-specific TextLayoutManager
|
||||
*/
|
||||
@interface RCTTextLayoutManager : NSObject
|
||||
|
||||
- (facebook::react::Size)measureWithAttributedString:(facebook::react::AttributedString)attributedString
|
||||
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
|
||||
layoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints;
|
||||
|
||||
- (void)drawAttributedString:(facebook::react::AttributedString)attributedString
|
||||
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
|
||||
frame:(CGRect)frame;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTTextLayoutManager.h"
|
||||
|
||||
#import "NSTextStorage+FontScaling.h"
|
||||
#import "RCTAttributedTextUtils.h"
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
@implementation RCTTextLayoutManager
|
||||
|
||||
static NSLineBreakMode RCTNSLineBreakModeFromWritingDirection(EllipsizeMode ellipsizeMode) {
|
||||
switch (ellipsizeMode) {
|
||||
case EllipsizeMode::Clip: return NSLineBreakByClipping;
|
||||
case EllipsizeMode::Head: return NSLineBreakByTruncatingHead;
|
||||
case EllipsizeMode::Tail: return NSLineBreakByTruncatingTail;
|
||||
case EllipsizeMode::Middle: return NSLineBreakByTruncatingMiddle;
|
||||
}
|
||||
}
|
||||
|
||||
- (facebook::react::Size)measureWithAttributedString:(AttributedString)attributedString
|
||||
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
|
||||
layoutConstraints:(LayoutConstraints)layoutConstraints
|
||||
{
|
||||
CGSize maximumSize = CGSize {layoutConstraints.maximumSize.width, layoutConstraints.maximumSize.height};
|
||||
NSTextStorage *textStorage =
|
||||
[self _textStorageAndLayoutManagerWithAttributesString:RCTNSAttributedStringFromAttributedString(attributedString)
|
||||
paragraphAttributes:paragraphAttributes
|
||||
size:maximumSize];
|
||||
|
||||
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
|
||||
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
|
||||
CGSize size = [layoutManager usedRectForTextContainer:textContainer].size;
|
||||
|
||||
size = (CGSize){
|
||||
MIN(size.width, maximumSize.width),
|
||||
MIN(size.height, maximumSize.height)
|
||||
};
|
||||
|
||||
return facebook::react::Size {size.width, size.height};
|
||||
}
|
||||
|
||||
- (void)drawAttributedString:(AttributedString)attributedString
|
||||
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
|
||||
frame:(CGRect)frame
|
||||
{
|
||||
NSTextStorage *textStorage =
|
||||
[self _textStorageAndLayoutManagerWithAttributesString:RCTNSAttributedStringFromAttributedString(attributedString)
|
||||
paragraphAttributes:paragraphAttributes
|
||||
size:frame.size];
|
||||
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
|
||||
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
|
||||
|
||||
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
|
||||
[layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:frame.origin];
|
||||
[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:frame.origin];
|
||||
}
|
||||
|
||||
|
||||
- (NSTextStorage *)_textStorageAndLayoutManagerWithAttributesString:(NSAttributedString *)attributedString
|
||||
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
|
||||
size:(CGSize)size
|
||||
{
|
||||
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:size];
|
||||
|
||||
textContainer.lineFragmentPadding = 0.0; // Note, the default value is 5.
|
||||
textContainer.lineBreakMode =
|
||||
paragraphAttributes.maximumNumberOfLines > 0 ? RCTNSLineBreakModeFromWritingDirection(paragraphAttributes.ellipsizeMode) : NSLineBreakByClipping;
|
||||
textContainer.maximumNumberOfLines = paragraphAttributes.maximumNumberOfLines;
|
||||
|
||||
NSLayoutManager *layoutManager = [NSLayoutManager new];
|
||||
[layoutManager addTextContainer:textContainer];
|
||||
|
||||
NSTextStorage *textStorage =
|
||||
[[NSTextStorage alloc] initWithAttributedString:attributedString];
|
||||
|
||||
[textStorage addLayoutManager:layoutManager];
|
||||
|
||||
if (paragraphAttributes.adjustsFontSizeToFit) {
|
||||
CGFloat minimumFontSize = !isnan(paragraphAttributes.minimumFontSize) ? paragraphAttributes.minimumFontSize : 4.0;
|
||||
CGFloat maximumFontSize = !isnan(paragraphAttributes.maximumFontSize) ? paragraphAttributes.maximumFontSize : 96.0;
|
||||
[textStorage scaleFontSizeToFitSize:size
|
||||
minimumFontSize:minimumFontSize
|
||||
maximumFontSize:maximumFontSize];
|
||||
}
|
||||
|
||||
return textStorage;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include <fabric/textlayoutmanager/RCTFontProperties.h>
|
||||
#include <fabric/textlayoutmanager/RCTFontUtils.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
inline static NSTextAlignment RCTNSTextAlignmentFromTextAlignment(TextAlignment textAlignment) {
|
||||
switch (textAlignment) {
|
||||
case TextAlignment::Natural: return NSTextAlignmentNatural;
|
||||
case TextAlignment::Left: return NSTextAlignmentLeft;
|
||||
case TextAlignment::Right: return NSTextAlignmentRight;
|
||||
case TextAlignment::Center: return NSTextAlignmentCenter;
|
||||
case TextAlignment::Justified: return NSTextAlignmentJustified;
|
||||
}
|
||||
}
|
||||
|
||||
inline static NSWritingDirection RCTNSWritingDirectionFromWritingDirection(WritingDirection writingDirection) {
|
||||
switch (writingDirection) {
|
||||
case WritingDirection::Natural: return NSWritingDirectionNatural;
|
||||
case WritingDirection::LeftToRight: return NSWritingDirectionLeftToRight;
|
||||
case WritingDirection::RightToLeft: return NSWritingDirectionRightToLeft;
|
||||
}
|
||||
}
|
||||
|
||||
inline static RCTFontStyle RCTFontStyleFromFontStyle(FontStyle fontStyle) {
|
||||
switch (fontStyle) {
|
||||
case FontStyle::Normal: return RCTFontStyleNormal;
|
||||
case FontStyle::Italic: return RCTFontStyleItalic;
|
||||
case FontStyle::Oblique: return RCTFontStyleOblique;
|
||||
}
|
||||
}
|
||||
|
||||
inline static RCTFontVariant RCTFontVariantFromFontVariant(FontVariant fontVariant) {
|
||||
return (RCTFontVariant)fontVariant;
|
||||
}
|
||||
|
||||
inline static NSUnderlineStyle RCTNSUnderlineStyleFromStyleAndPattern(TextDecorationLineStyle textDecorationLineStyle, TextDecorationLinePattern textDecorationLinePattern) {
|
||||
NSUnderlineStyle style = NSUnderlineStyleNone;
|
||||
|
||||
switch (textDecorationLineStyle) {
|
||||
case TextDecorationLineStyle::Single:
|
||||
style = NSUnderlineStyle(style | NSUnderlineStyleSingle); break;
|
||||
case TextDecorationLineStyle::Thick:
|
||||
style = NSUnderlineStyle(style | NSUnderlineStyleThick); break;
|
||||
case TextDecorationLineStyle::Double:
|
||||
style = NSUnderlineStyle(style | NSUnderlineStyleDouble); break;
|
||||
}
|
||||
|
||||
switch (textDecorationLinePattern) {
|
||||
case TextDecorationLinePattern::Solid:
|
||||
style = NSUnderlineStyle(style | NSUnderlinePatternSolid); break;
|
||||
case TextDecorationLinePattern::Dash:
|
||||
style = NSUnderlineStyle(style | NSUnderlinePatternDash); break;
|
||||
case TextDecorationLinePattern::Dot:
|
||||
style = NSUnderlineStyle(style | NSUnderlinePatternDot); break;
|
||||
case TextDecorationLinePattern::DashDot:
|
||||
style = NSUnderlineStyle(style | NSUnderlinePatternDashDot); break;
|
||||
case TextDecorationLinePattern::DashDotDot:
|
||||
style = NSUnderlineStyle(style | NSUnderlinePatternDashDotDot); break;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
inline static UIColor *RCTUIColorFromSharedColor(const SharedColor &color) {
|
||||
return color ? [UIColor colorWithCGColor:color.get()] : nil;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <fabric/attributedstring/AttributedString.h>
|
||||
#include <fabric/attributedstring/ParagraphAttributes.h>
|
||||
#include <fabric/core/LayoutConstraints.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class TextLayoutManager;
|
||||
|
||||
using SharedTextLayoutManager = std::shared_ptr<const TextLayoutManager>;
|
||||
|
||||
/*
|
||||
* Cross platform facade for iOS-specific RCTTTextLayoutManager.
|
||||
*/
|
||||
class TextLayoutManager {
|
||||
public:
|
||||
TextLayoutManager();
|
||||
~TextLayoutManager();
|
||||
|
||||
/*
|
||||
* Measures `attributedString` using native text rendering infrastructure.
|
||||
*/
|
||||
Size measure(
|
||||
AttributedString attributedString,
|
||||
ParagraphAttributes paragraphAttributes,
|
||||
LayoutConstraints layoutConstraints
|
||||
) const;
|
||||
|
||||
/*
|
||||
* Returns an opaque pointer to platform-specific TextLayoutManager.
|
||||
* Is used on a native views layer to delegate text rendering to the manager.
|
||||
*/
|
||||
void *getNativeTextLayoutManager() const;
|
||||
|
||||
private:
|
||||
void *self_;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "TextLayoutManager.h"
|
||||
|
||||
#import "RCTTextLayoutManager.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
TextLayoutManager::TextLayoutManager() {
|
||||
self_ = (__bridge_retained void *)[RCTTextLayoutManager new];
|
||||
}
|
||||
|
||||
TextLayoutManager::~TextLayoutManager() {
|
||||
CFRelease(self_);
|
||||
self_ = nullptr;
|
||||
}
|
||||
|
||||
void *TextLayoutManager::getNativeTextLayoutManager() const {
|
||||
return self_;
|
||||
}
|
||||
|
||||
Size TextLayoutManager::measure(
|
||||
AttributedString attributedString,
|
||||
ParagraphAttributes paragraphAttributes,
|
||||
LayoutConstraints layoutConstraints
|
||||
) const {
|
||||
RCTTextLayoutManager *textLayoutManager = (__bridge RCTTextLayoutManager *)self_;
|
||||
return [textLayoutManager measureWithAttributedString:attributedString
|
||||
paragraphAttributes:paragraphAttributes
|
||||
layoutConstraints:layoutConstraints];
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <fabric/textlayoutmanager/TextLayoutManager.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
TEST(TextLayoutManagerTest, testSomething) {
|
||||
// TODO:
|
||||
}
|
Loading…
Reference in New Issue