http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm new file mode 100644 index 0000000..bb263c8 --- /dev/null +++ b/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import "WXTextAreaComponent.h" +#import "WXUtility.h" +#import "WXComponent+Layout.h" +#import "WXComponent_internal.h" +#import "WXComponent+Layout.h" + +#define CorrectX 4 //textview fill text 4 pixel from left. so placeholderlabel have 4 pixel too +#define CorrectY 8 // textview fill text 8 pixel from top +typedef UITextView WXTextAreaView; + +@interface WXTextAreaComponent() + +@property (nonatomic, strong) WXTextAreaView *textView; +@property (nonatomic) NSUInteger rows; + +@end + +@implementation WXTextAreaComponent { + UIEdgeInsets _border; + UIEdgeInsets _padding; +} + +-(void)viewDidLoad +{ + _padding = UIEdgeInsetsZero; + _border = UIEdgeInsetsZero; + if (self.placeholderString) { + self.placeHolderLabel = [[UILabel alloc] init]; + self.placeHolderLabel.numberOfLines = 0; + [_textView addSubview:self.placeHolderLabel]; + } + // default placeholder hide from voice over + self.placeHolderLabel.isAccessibilityElement = NO; + _textView.isAccessibilityElement = YES; + _textView.delegate = self; + [_textView setNeedsDisplay]; + [_textView setClipsToBounds:YES]; + [super viewDidLoad]; +} + +- (void)viewWillUnload +{ + _textView = nil; +} + +- (UIView *)loadView +{ + _textView = [[WXTextAreaView alloc] init]; + return _textView; +} + +#pragma mark measure frame +- (CGSize (^)(CGSize))measureBlock +{ + __weak typeof(self) weakSelf = self; + return ^CGSize (CGSize constrainedSize) { + + CGSize computedSize = [[[NSString alloc] init]sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:weakSelf.textView.font.pointSize]}]; + computedSize.height = _rows? computedSize.height *weakSelf.rows + (CorrectY + CorrectY/2):0; +//#ifndef USE_FLEX + + if (![WXComponent isUseFlex]) + { + if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) { + computedSize.width = MAX(computedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]); + } + + if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) { + computedSize.width = MIN(computedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]); + } + + if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_HEIGHT])) { + computedSize.height = MAX(computedSize.height, weakSelf.cssNode->style.minDimensions[CSS_HEIGHT]); + } + + if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT])) { + computedSize.height = MIN(computedSize.height, weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT]); + } + } + +//#else + + else + { + if (!isnan(weakSelf.flexCssNode->getMinWidth())) { + computedSize.width = MAX(computedSize.width, weakSelf.flexCssNode->getMinWidth()); + } + + if (!isnan(weakSelf.flexCssNode->getMaxWidth())) { + computedSize.width = MIN(computedSize.width, weakSelf.flexCssNode->getMaxWidth()); + } + + if (!isnan(weakSelf.flexCssNode->getMinHeight())) { + computedSize.height = MAX(computedSize.height, weakSelf.flexCssNode->getMinHeight()); + } + + if (!isnan(weakSelf.flexCssNode->getMaxHeight())) { + computedSize.height = MIN(computedSize.height, weakSelf.flexCssNode->getMaxHeight()); + } + } + +//#endif + return (CGSize) { + WXCeilPixelValue(computedSize.width), + WXCeilPixelValue(computedSize.height) + }; + }; +} + +#pragma mark -Overwrite method +-(NSString *)text +{ + return _textView.text; +} + +- (void)setText:(NSString *)text +{ + _textView.text = text; + if ([text length] >0) { + self.placeHolderLabel.text = @""; + } +} + +-(void)setTextColor:(UIColor *)color +{ + [_textView setTextColor:color]; +} + +-(void)setTextAlignment:(NSTextAlignment)textAlignForStyle +{ + [_textView setTextAlignment:textAlignForStyle]; +} + +-(void)setUserInteractionEnabled:(BOOL)userInteractionEnabled +{ + [_textView setUserInteractionEnabled:userInteractionEnabled]; +} + +-(void)setEnabled:(BOOL)enabled +{ + _textView.editable = enabled; + _textView.selectable = enabled; +} + +-(void)setReturnKeyType:(UIReturnKeyType)returnKeyType +{ + [_textView setReturnKeyType:returnKeyType]; +} + +-(void)setInputAccessoryView:(UIView *)inputAccessoryView +{ + [_textView setInputAccessoryView:inputAccessoryView]; +} + +-(void)setEditSelectionRange:(NSInteger)selectionStart selectionEnd:(NSInteger)selectionEnd +{ + [self.textView becomeFirstResponder]; + UITextPosition *startPos = [self.textView positionFromPosition:self.textView.beginningOfDocument offset:selectionStart]; + UITextPosition *endPos = [self.textView positionFromPosition:self.textView.beginningOfDocument offset:selectionEnd]; + UITextRange *textRange = [self.textView textRangeFromPosition:startPos + toPosition:endPos]; + self.textView.selectedTextRange = textRange; +} + +-(NSDictionary *)getEditSelectionRange +{ + NSInteger selectionStart = [self.textView offsetFromPosition:self.textView.beginningOfDocument toPosition:self.textView.selectedTextRange.start]; + NSInteger selectionEnd = [self.textView offsetFromPosition:self.textView.beginningOfDocument toPosition:self.textView.selectedTextRange.end]; + NSDictionary *res = @{@"selectionStart":@(selectionStart),@"selectionEnd":@(selectionEnd)}; + return res; +} + +-(void)setKeyboardType:(UIKeyboardType)keyboardType +{ + [_textView setKeyboardType:keyboardType]; +} + +-(void)setSecureTextEntry:(BOOL)secureTextEntry +{ + [_textView setSecureTextEntry:secureTextEntry]; +} + +-(void)setEditPadding:(UIEdgeInsets)padding +{ + _padding = padding; + [self _updateTextContentInset]; +} + +-(void)setEditBorder:(UIEdgeInsets)border +{ + _border = border; + [self _updateTextContentInset]; +} + +-(void)setAttributedPlaceholder:(NSMutableAttributedString *)attributedString font:(UIFont *)font +{ + if (self.placeholderColor) { + [attributedString addAttribute:NSForegroundColorAttributeName value:self.placeholderColor range:NSMakeRange(0, self.placeholderString.length)]; + [attributedString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, self.placeholderString.length)]; + } + self.placeHolderLabel.backgroundColor = [UIColor clearColor]; + CGRect expectedLabelSize = [attributedString boundingRectWithSize:(CGSize){self.view.frame.size.width, CGFLOAT_MAX} + options:NSStringDrawingUsesLineFragmentOrigin + context:nil]; + + self.placeHolderLabel.clipsToBounds = NO; + CGRect newFrame = self.placeHolderLabel.frame; + newFrame.size.height = ceil(expectedLabelSize.size.height); + newFrame.size.width = _textView.frame.size.width- CorrectX*2; + newFrame.origin.x = CorrectX + _padding.left + _border.left; // the cursor origin.x + self.placeHolderLabel.frame = newFrame; + self.placeHolderLabel.attributedText = attributedString; +} + +-(void)setFont:(UIFont *)font +{ + [_textView setFont:font]; +} + +-(void)setRows:(NSUInteger)rows +{ + _rows = rows; + [self setNeedsLayout]; +} + +#pragma mark -Private Method +- (void)_updateTextContentInset +{ + [_textView setTextContainerInset:UIEdgeInsetsMake(_padding.top + _border.top, + _padding.left + _border.left, + _padding.bottom + _border.bottom, + _border.right + _border.right)]; + + //when textview update, placeHolderLabel update too + CGRect newFrame = self.placeHolderLabel.frame; + newFrame.size.width = self.textView.frame.size.width - (_padding.left + _border.left) -CorrectX*2; + newFrame.origin.x = CorrectX + _padding.left + _border.left; // the cursor origin.x + newFrame.origin.y = _padding.top + _border.top; + self.placeHolderLabel.frame = newFrame; +} + +@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m ---------------------------------------------------------------------- diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m deleted file mode 100644 index 2a948e2..0000000 --- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m +++ /dev/null @@ -1,1076 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#import "WXTextComponent.h" -#import "WXSDKInstance_private.h" -#import "WXComponent_internal.h" -#import "WXLayer.h" -#import "WXUtility.h" -#import "WXConvert.h" -#import "WXRuleManager.h" -#import "WXDefine.h" -#import "WXView.h" -#import <pthread/pthread.h> -#import <CoreText/CoreText.h> - -// WXText is a non-public is not permitted -@interface WXTextView : WXView -@property (nonatomic, strong) NSTextStorage *textStorage; -@end - -@implementation WXTextView - -- (instancetype)initWithFrame:(CGRect)frame -{ - if ((self = [super initWithFrame:frame])) { - self.accessibilityTraits |= UIAccessibilityTraitStaticText; - - self.opaque = NO; - self.contentMode = UIViewContentModeRedraw; - self.textStorage = [NSTextStorage new]; - } - return self; -} - -+ (Class)layerClass -{ - return [WXLayer class]; -} - -- (void)copy:(id)sender -{ - [[UIPasteboard generalPasteboard] setString:((WXTextComponent*)self.wx_component).text]; -} - -- (void)setTextStorage:(NSTextStorage *)textStorage -{ - if (_textStorage != textStorage) { - _textStorage = textStorage; - [self.wx_component setNeedsDisplay]; - } -} - -- (BOOL)canBecomeFirstResponder -{ - return YES; -} - -- (BOOL)canPerformAction:(SEL)action withSender:(id)sender -{ - if (action == @selector(copy:)) { - return [[self.wx_component valueForKey:@"_enableCopy"] boolValue]; - } - return [super canPerformAction:action withSender:sender]; -} - -- (NSString *)description -{ - NSString *superDescription = super.description; - NSRange semicolonRange = [superDescription rangeOfString:@";"]; - NSString * content = _textStorage.string; - if ([(WXTextComponent*)self.wx_component useCoreText]) { - content = ((WXTextComponent*)self.wx_component).text; - } - NSString *replacement = [NSString stringWithFormat:@"; text: %@; frame:%f,%f,%f,%f", content, self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height]; - return [superDescription stringByReplacingCharactersInRange:semicolonRange withString:replacement]; -} - -- (NSString *)accessibilityValue -{ - if (self.wx_component && self.wx_component->_ariaLabel) { - return [super accessibilityValue]; - } - if (![(WXTextComponent*)self.wx_component useCoreText]) { - return _textStorage.string; - } - return ((WXTextComponent*)self.wx_component).text; -} - -- (NSString *)accessibilityLabel -{ - if (self.wx_component) { - if (self.wx_component->_ariaLabel) { - return self.wx_component->_ariaLabel; - } - } - return [super accessibilityLabel]; -} - -@end - -static BOOL textRenderUsingCoreText = YES; - -NSString *const WXTextTruncationToken = @"\u2026"; -CGFloat WXTextDefaultLineThroughWidth = 1.2; - -@interface WXTextComponent() -@property (nonatomic, strong) NSString *useCoreTextAttr; -@end - -@implementation WXTextComponent -{ - UIEdgeInsets _border; - UIEdgeInsets _padding; - NSTextStorage *_textStorage; - CGFloat _textStorageWidth; - - UIColor *_color; - NSString *_fontFamily; - CGFloat _fontSize; - CGFloat _fontWeight; - WXTextStyle _fontStyle; - NSUInteger _lines; - NSTextAlignment _textAlign; - NSString *_direction; - WXTextDecoration _textDecoration; - NSString *_textOverflow; - CGFloat _lineHeight; - CGFloat _letterSpacing; - BOOL _truncationLine; // support trunk tail - - NSAttributedString * _ctAttributedString; - NSString *_wordWrap; - - pthread_mutex_t _ctAttributedStringMutex; - pthread_mutexattr_t _propertMutexAttr; - BOOL _observerIconfont; - BOOL _enableCopy; -} - -+ (void)setRenderUsingCoreText:(BOOL)usingCoreText -{ - textRenderUsingCoreText = usingCoreText; -} - -+ (BOOL)textRenderUsingCoreText -{ - return textRenderUsingCoreText; -} - -- (instancetype)initWithRef:(NSString *)ref - type:(NSString *)type - styles:(NSDictionary *)styles - attributes:(NSDictionary *)attributes - events:(NSArray *)events - weexInstance:(WXSDKInstance *)weexInstance -{ - self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance]; - if (self) { - // just for coretext and textkit render replacement - pthread_mutexattr_init(&(_propertMutexAttr)); - pthread_mutexattr_settype(&(_propertMutexAttr), PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&(_ctAttributedStringMutex), &(_propertMutexAttr)); - - if ([attributes objectForKey:@"coretext"]) { - _useCoreTextAttr = [WXConvert NSString:attributes[@"coretext"]]; - } else { - _useCoreTextAttr = nil; - } - - [self fillCSSStyles:styles]; - [self fillAttributes:attributes]; - } - - return self; -} - -- (BOOL)useCoreText -{ - if ([_useCoreTextAttr isEqualToString:@"true"]) { - return YES; - } - if ([_useCoreTextAttr isEqualToString:@"false"]) { - return NO; - } - - if ([WXTextComponent textRenderUsingCoreText]) { - return YES; - } - return NO; -} - -- (void)dealloc -{ - if (_fontFamily && _observerIconfont) { - [[NSNotificationCenter defaultCenter] removeObserver:self name:WX_ICONFONT_DOWNLOAD_NOTIFICATION object:nil]; - } - pthread_mutex_destroy(&_ctAttributedStringMutex); - pthread_mutexattr_destroy(&_propertMutexAttr); -} - -#define WX_STYLE_FILL_TEXT(key, prop, type, needLayout)\ -do {\ - id value = styles[@#key];\ - if (value) {\ - _##prop = [WXConvert type:value];\ - [self setNeedsRepaint];\ - if (needLayout) {\ - [self setNeedsLayout];\ - }\ - }\ -} while(0); - -#define WX_STYLE_FILL_TEXT_WITH_DEFAULT_VALUE(key, prop, type, defaultValue,needLayout)\ -do {\ - id value = styles[@#key];\ - if (value) {\ - if([WXUtility isBlankString:value]){\ - _##prop = defaultValue;\ - }else {\ - _##prop = [WXConvert type:value];\ - }\ - [self setNeedsRepaint];\ - if (needLayout) {\ - [self setNeedsLayout];\ - }\ - }\ -} while(0); - - -#define WX_STYLE_FILL_TEXT_PIXEL(key, prop, needLayout)\ -do {\ - id value = styles[@#key];\ - if (value) {\ - _##prop = [WXConvert WXPixelType:value scaleFactor:self.weexInstance.pixelScaleFactor];\ - [self setNeedsRepaint];\ - if (needLayout) {\ - [self setNeedsLayout];\ - }\ -}\ -} while(0); - -- (void)fillCSSStyles:(NSDictionary *)styles -{ - WX_STYLE_FILL_TEXT_WITH_DEFAULT_VALUE(color, color, UIColor, [UIColor blackColor], NO) - WX_STYLE_FILL_TEXT(fontFamily, fontFamily, NSString, YES) - WX_STYLE_FILL_TEXT_PIXEL(fontSize, fontSize, YES) - WX_STYLE_FILL_TEXT(fontWeight, fontWeight, WXTextWeight, YES) - WX_STYLE_FILL_TEXT(fontStyle, fontStyle, WXTextStyle, YES) - WX_STYLE_FILL_TEXT(lines, lines, NSUInteger, YES) - WX_STYLE_FILL_TEXT(textAlign, textAlign, NSTextAlignment, NO) - WX_STYLE_FILL_TEXT(textDecoration, textDecoration, WXTextDecoration, YES) - WX_STYLE_FILL_TEXT(textOverflow, textOverflow, NSString, NO) - WX_STYLE_FILL_TEXT_PIXEL(lineHeight, lineHeight, YES) - WX_STYLE_FILL_TEXT_PIXEL(letterSpacing, letterSpacing, YES) - WX_STYLE_FILL_TEXT(wordWrap, wordWrap, NSString, YES); - WX_STYLE_FILL_TEXT(direction, direction, NSString, YES) - if (_fontFamily && !_observerIconfont) { - // notification received when custom icon font file download finish - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(repaintText:) name:WX_ICONFONT_DOWNLOAD_NOTIFICATION object:nil]; - _observerIconfont = YES; - } - UIEdgeInsets padding = { - WXFloorPixelValue(self.cssNode->style.padding[CSS_TOP] + self.cssNode->style.border[CSS_TOP]), - WXFloorPixelValue(self.cssNode->style.padding[CSS_LEFT] + self.cssNode->style.border[CSS_LEFT]), - WXFloorPixelValue(self.cssNode->style.padding[CSS_BOTTOM] + self.cssNode->style.border[CSS_BOTTOM]), - WXFloorPixelValue(self.cssNode->style.padding[CSS_RIGHT] + self.cssNode->style.border[CSS_RIGHT]) - }; - - if (!UIEdgeInsetsEqualToEdgeInsets(padding, _padding)) { - _padding = padding; - [self setNeedsRepaint]; - } -} - -- (void)fillAttributes:(NSDictionary *)attributes -{ - id text = [WXConvert NSString:attributes[@"value"]]; - if (text && ![self.text isEqualToString:text]) { - self.text = text; - [self setNeedsRepaint]; - [self setNeedsLayout]; - } - if (attributes[@"enableCopy"]) { - _enableCopy = [WXConvert BOOL:attributes[@"enableCopy"]]; - } -} - -- (void)setNeedsRepaint -{ - _textStorage = nil; - - pthread_mutex_lock(&(_ctAttributedStringMutex)); - _ctAttributedString = nil; - pthread_mutex_unlock(&(_ctAttributedStringMutex)); - -} - -#pragma mark - Subclass - -- (void)setNeedsLayout -{ - [super setNeedsLayout]; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - BOOL useCoreText = NO; - if ([self.view.wx_component isKindOfClass:NSClassFromString(@"WXTextComponent")] && [self.view.wx_component respondsToSelector:@selector(useCoreText)]) { - useCoreText = [(WXTextComponent*)self.view.wx_component useCoreText]; - } - if (!useCoreText) { - ((WXTextView *)self.view).textStorage = _textStorage; - } - if (_enableCopy) { - UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(displayMenuController:)]; - [self.view addGestureRecognizer:longPress]; - } - self.view.isAccessibilityElement = YES; - - [self setNeedsDisplay]; -} - -- (void)displayMenuController:(id)sender -{ - if ([self.view becomeFirstResponder] && ((UILongPressGestureRecognizer*)sender).state == UIGestureRecognizerStateBegan) { - UIMenuController *theMenu = [UIMenuController sharedMenuController]; - CGSize size = [self ctAttributedString].size; - CGRect selectionRect = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, size.width, size.height); - [theMenu setTargetRect:selectionRect inView:self.view.superview]; - [theMenu setMenuVisible:YES animated:YES]; - } -} - -- (UIView *)loadView -{ - return [[WXTextView alloc] init]; -} - -- (BOOL)needsDrawRect -{ - return YES; -} - -- (UIImage *)drawRect:(CGRect)rect; -{ - CGContextRef context = UIGraphicsGetCurrentContext(); - if (_isCompositingChild) { - [self drawTextWithContext:context bounds:rect padding:_padding view:nil]; - } else { - WXTextView *textView = (WXTextView *)_view; - [self drawTextWithContext:context bounds:rect padding:_padding view:textView]; - } - - return nil; -} - -- (CGSize (^)(CGSize))measureBlock -{ - __weak typeof(self) weakSelf = self; - return ^CGSize (CGSize constrainedSize) { - CGSize computedSize = CGSizeZero; - NSTextStorage *textStorage = nil; - - //TODO:more elegant way to use max and min constrained size - if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) { - constrainedSize.width = MAX(constrainedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]); - } - - if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) { - constrainedSize.width = MIN(constrainedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]); - } - - if (![self useCoreText]) { - textStorage = [weakSelf textStorageWithWidth:constrainedSize.width]; - NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject; - NSTextContainer *textContainer = layoutManager.textContainers.firstObject; - computedSize = [layoutManager usedRectForTextContainer:textContainer].size; - } else { - computedSize = [weakSelf calculateTextHeightWithWidth:constrainedSize.width]; - } - - if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_HEIGHT])) { - computedSize.height = MAX(computedSize.height, weakSelf.cssNode->style.minDimensions[CSS_HEIGHT]); - } - - if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT])) { - computedSize.height = MIN(computedSize.height, weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT]); - } - if (textStorage && [WXUtility isBlankString:textStorage.string]) { - // if the text value is empty or nil, then set the height is 0. - computedSize.height = 0; - } - - return (CGSize) { - WXCeilPixelValue(computedSize.width), - WXCeilPixelValue(computedSize.height) - }; - }; -} - -#pragma mark Text Building - -- (NSAttributedString *)ctAttributedString -{ - if (!self.text) { - return nil; - } - NSAttributedString * attributedString = nil; - pthread_mutex_lock(&(_ctAttributedStringMutex)); - if (!_ctAttributedString) { - _ctAttributedString = [self buildCTAttributeString]; - WXPerformBlockOnComponentThread(^{ - [self.weexInstance.componentManager startComponentTasks]; - }); - } - attributedString = [_ctAttributedString copy]; - pthread_mutex_unlock(&(_ctAttributedStringMutex)); - return attributedString; -} - -- (void)repaintText:(NSNotification *)notification -{ - if (![_fontFamily isEqualToString:notification.userInfo[@"fontFamily"]]) { - return; - } - [self setNeedsRepaint]; - WXPerformBlockOnComponentThread(^{ - [self.weexInstance.componentManager startComponentTasks]; - WXPerformBlockOnMainThread(^{ - [self setNeedsLayout]; - [self setNeedsDisplay]; - }); - }); -} - -- (NSMutableAttributedString *)buildCTAttributeString -{ - NSString * string = self.text; - if (![string isKindOfClass:[NSString class]]) { - WXLogError(@"text %@ is invalid", self.text); - string = @""; - } - NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: string]; - if (_color) { - [attributedString addAttribute:NSForegroundColorAttributeName value:_color range:NSMakeRange(0, string.length)]; - } - - // set font - UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor useCoreText:[self useCoreText]]; - CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)font.fontName, - font.pointSize, - NULL); - if (ctFont) { - [attributedString addAttribute:(id)kCTFontAttributeName value:(__bridge id)(ctFont) range:NSMakeRange(0, string.length)]; - CFRelease(ctFont); - } - - if(_textDecoration == WXTextDecorationUnderline){ - [attributedString addAttribute:(id)kCTUnderlineStyleAttributeName value:@(kCTUnderlinePatternSolid | kCTUnderlineStyleSingle) range:NSMakeRange(0, string.length)]; - } else if(_textDecoration == WXTextDecorationLineThrough){ - [attributedString addAttribute:NSStrikethroughStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)]; - } - - NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; - - // handle text direction style, default ltr - if (_cssNode->layout.direction == CSS_DIRECTION_RTL) { - if (0 == _textAlign) { - //force text right-align if don't specified any align. - _textAlign = NSTextAlignmentRight; - } - paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft; - } else { - //if you specify NSWritingDirectionNaturalDirection, the receiver resolves the writing - //directionto eitherNSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft, - //depending on the direction for the userâs language preference setting. - paragraphStyle.baseWritingDirection = NSWritingDirectionNatural; - } - - if (_textAlign) { - paragraphStyle.alignment = _textAlign; - } - - if ([[_wordWrap lowercaseString] isEqualToString:@"break-word"]) { - paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; - } else if ([[_wordWrap lowercaseString] isEqualToString:@"normal"]){ - paragraphStyle.lineBreakMode = NSLineBreakByClipping; - } else { - // set default lineBreakMode - paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping; - } - _truncationLine = NO; - if (_textOverflow && [_textOverflow length] > 0) { - if (_lines && [_textOverflow isEqualToString:@"ellipsis"]) - _truncationLine = YES; - } - - if (_lineHeight) { - paragraphStyle.maximumLineHeight = _lineHeight; - paragraphStyle.minimumLineHeight = _lineHeight; - } - if (_lineHeight || _textAlign || [_textOverflow length] > 0) { - [attributedString addAttribute:NSParagraphStyleAttributeName - value:paragraphStyle - range:(NSRange){0, attributedString.length}]; - } - - if (_letterSpacing) { - [attributedString addAttribute:NSKernAttributeName value:@(_letterSpacing) range:(NSRange){0, attributedString.length}]; - } - - if ([self adjustLineHeight]) { - if (_lineHeight > font.lineHeight) { - [attributedString addAttribute:NSBaselineOffsetAttributeName - value:@((_lineHeight - font.lineHeight)/ 2) - range:(NSRange){0, attributedString.length}]; - } - } - - return attributedString; -} - -- (NSAttributedString *)buildAttributeString -{ - NSString *string = self.text ?: @""; - - NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string]; - - // set textColor - if(_color) { - [attributedString addAttribute:NSForegroundColorAttributeName value:_color range:NSMakeRange(0, string.length)]; - } - - // set font - UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor]; - if (font) { - [attributedString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, string.length)]; - } - - if(_textDecoration == WXTextDecorationUnderline){ - [attributedString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)]; - } else if(_textDecoration == WXTextDecorationLineThrough){ - [attributedString addAttribute:NSStrikethroughStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)]; - } - - NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; - - // handle text direction style, default ltr - if (_cssNode->layout.direction == CSS_DIRECTION_RTL) { - if (0 == _textAlign) { - //force text right-align if don't specified any align. - _textAlign = NSTextAlignmentRight; - } - paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft; - } else { - //if you specify NSWritingDirectionNaturalDirection, the receiver resolves the writing - //directionto eitherNSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft, - //depending on the direction for the userâs language preference setting. - paragraphStyle.baseWritingDirection = NSWritingDirectionNatural; - } - - if (_textAlign) { - paragraphStyle.alignment = _textAlign; - } - - if (_lineHeight) { - paragraphStyle.maximumLineHeight = _lineHeight; - paragraphStyle.minimumLineHeight = _lineHeight; - } - - if (_lineHeight || _textAlign) { - [attributedString addAttribute:NSParagraphStyleAttributeName - value:paragraphStyle - range:(NSRange){0, attributedString.length}]; - } - if ([self adjustLineHeight]) { - if (_lineHeight > font.lineHeight) { - [attributedString addAttribute:NSBaselineOffsetAttributeName - value:@((_lineHeight - font.lineHeight)/ 2) - range:(NSRange){0, attributedString.length}]; - } - } - - return attributedString; -} - -- (BOOL)adjustLineHeight -{ - if (WX_SYS_VERSION_LESS_THAN(@"10.0")) { - return true; - } - return ![self useCoreText]; -} - -- (NSTextStorage *)textStorageWithWidth:(CGFloat)width -{ - if (_textStorage && width == _textStorageWidth) { - return _textStorage; - } - - NSLayoutManager *layoutManager = [NSLayoutManager new]; - - // build AttributeString - NSAttributedString *attributedString = [self buildAttributeString]; - - NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; - [textStorage addLayoutManager:layoutManager]; - - NSTextContainer *textContainer = [NSTextContainer new]; - textContainer.lineFragmentPadding = 0.0; - - if ([[_wordWrap lowercaseString] isEqualToString:@"break-word"]) { - textContainer.lineBreakMode = NSLineBreakByWordWrapping; - } else if ([[_wordWrap lowercaseString] isEqualToString:@"normal"]){ - textContainer.lineBreakMode = NSLineBreakByClipping; - } else { - // set default lineBreakMode - textContainer.lineBreakMode = NSLineBreakByCharWrapping; - } - - if (_textOverflow && [_textOverflow length] > 0) { - if ([_textOverflow isEqualToString:@"ellipsis"]) - textContainer.lineBreakMode = NSLineBreakByTruncatingTail; - } - textContainer.maximumNumberOfLines = _lines > 0 ? _lines : 0; - textContainer.size = (CGSize){isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX}; - - [layoutManager addTextContainer:textContainer]; - [layoutManager ensureLayoutForTextContainer:textContainer]; - - _textStorageWidth = width; - _textStorage = textStorage; - - return textStorage; -} - -- (void)syncTextStorageForView -{ - CGFloat width = self.calculatedFrame.size.width - (_padding.left + _padding.right); - NSTextStorage *textStorage = nil; - if (![self useCoreText]) { - textStorage = [self textStorageWithWidth:width]; - } - [self.weexInstance.componentManager _addUITask:^{ - if ([self isViewLoaded]) { - if (![self useCoreText]) { - ((WXTextView *)self.view).textStorage = textStorage; - } - [self readyToRender]; // notify super component - [self setNeedsDisplay]; - } - }]; -} - -- (void)_frameDidCalculated:(BOOL)isChanged -{ - [super _frameDidCalculated:isChanged]; - [self syncTextStorageForView]; -} - -- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles -{ - [super _updateStylesOnComponentThread:styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:isUpdateStyles]; - NSMutableDictionary * newStyles = [styles mutableCopy]; - for (NSString * key in [resetStyles copy]) { - [newStyles setObject:@"" forKey:key]; - } - [self fillCSSStyles:newStyles]; - - [self syncTextStorageForView]; -} - -- (void)_updateAttributesOnComponentThread:(NSDictionary *)attributes -{ - [super _updateAttributesOnComponentThread:attributes]; - - [self fillAttributes:attributes]; - - [self syncTextStorageForView]; -} - -- (void)drawTextWithContext:(CGContextRef)context bounds:(CGRect)bounds padding:(UIEdgeInsets)padding view:(WXTextView *)view -{ - if (bounds.size.width <= 0 || bounds.size.height <= 0) { - return; - } - - if ([self _needsDrawBorder]) { - [self _drawBorderWithContext:context size:bounds.size]; - } else { - WXPerformBlockOnMainThread(^{ - [self _resetNativeBorderRadius]; - }); - } - if (![self useCoreText]) { - NSLayoutManager *layoutManager = _textStorage.layoutManagers.firstObject; - NSTextContainer *textContainer = layoutManager.textContainers.firstObject; - - CGRect textFrame = UIEdgeInsetsInsetRect(bounds, padding); - NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; - - [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin]; - [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin]; - } else { - CGRect textFrame = UIEdgeInsetsInsetRect(bounds, padding); - // sufficient height for text to draw, or frame lines will be empty - textFrame.size.height = bounds.size.height * 2; - CGContextSaveGState(context); - //flip the coordinate system - CGContextSetTextMatrix(context, CGAffineTransformIdentity); - CGContextTranslateCTM(context, 0, textFrame.size.height); - CGContextScaleCTM(context, 1.0, -1.0); - - NSAttributedString * attributedStringCopy = [self ctAttributedString]; - //add path - CGPathRef cgPath = NULL; - cgPath = CGPathCreateWithRect(textFrame, NULL); - CTFrameRef _coreTextFrameRef = NULL; - if (_coreTextFrameRef) { - CFRelease(_coreTextFrameRef); - _coreTextFrameRef = NULL; - } - if(!attributedStringCopy) { - return; - } - CTFramesetterRef ctframesetterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attributedStringCopy)); - _coreTextFrameRef = CTFramesetterCreateFrame(ctframesetterRef, CFRangeMake(0, attributedStringCopy.length), cgPath, NULL); - CFArrayRef ctLines = NULL; - if (NULL == _coreTextFrameRef) { - // try to protect crash from frame is NULL - return; - } - CFRelease(ctframesetterRef); - ctframesetterRef = NULL; - ctLines = CTFrameGetLines(_coreTextFrameRef); - CFIndex lineCount = CFArrayGetCount(ctLines); - NSMutableArray * mutableLines = [NSMutableArray new]; - CGPoint lineOrigins[lineCount]; - NSUInteger rowCount = 0; - BOOL needTruncation = NO; - CTLineRef ctTruncatedLine = NULL; - CTFrameGetLineOrigins(_coreTextFrameRef, CFRangeMake(0, 0), lineOrigins); - for (CFIndex lineIndex = 0;(!_lines || _lines > lineIndex) && lineIndex < lineCount; lineIndex ++) { - CTLineRef lineRef = NULL; - lineRef = CFArrayGetValueAtIndex(ctLines, lineIndex); - if (!lineRef) { - break; - } - CGPoint lineOrigin = lineOrigins[lineIndex]; - lineOrigin.x += padding.left; - lineOrigin.y -= padding.top; - CFArrayRef runs = CTLineGetGlyphRuns(lineRef); - [mutableLines addObject:(__bridge id _Nonnull)(lineRef)]; - // lineIndex base 0 - rowCount = lineIndex + 1; - if (_lines > 0 && _truncationLine) { - if (_truncationLine && rowCount > _lines) { - needTruncation = YES; - do { - NSUInteger lastRow = [mutableLines count]; - if (lastRow < rowCount) { - break; - } - [mutableLines removeLastObject]; - } while (1); - - } - } - if (_lines > 0 && _truncationLine) { - if (rowCount >= _lines &&!needTruncation && (CTLineGetStringRange(lineRef).length + CTLineGetStringRange(lineRef).location) < attributedStringCopy.length) { - needTruncation = YES; - } - } - - if (needTruncation) { - CGContextSetTextPosition(context, lineOrigin.x, lineOrigin.y); - ctTruncatedLine = [self buildTruncatedLineWithRuns:runs lines:mutableLines path:cgPath]; - if (ctTruncatedLine) { - CFArrayRef truncatedRuns = CTLineGetGlyphRuns(ctTruncatedLine); - [self drawTextWithRuns:truncatedRuns context:context lineOrigin:lineOrigin]; - CFRelease(ctTruncatedLine); - ctTruncatedLine = NULL; - continue; - } - }else { - [self drawTextWithRuns:runs context:context lineOrigin:lineOrigin]; - } - } - - [mutableLines removeAllObjects]; - CGPathRelease(cgPath); - CFRelease(_coreTextFrameRef); - _coreTextFrameRef = NULL; - cgPath = NULL; - CGContextRestoreGState(context); - } -} - -- (void)drawTextWithRuns:(CFArrayRef)runs context:(CGContextRef)context lineOrigin:(CGPoint)lineOrigin -{ - for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runs); runIndex ++) { - CTRunRef run = NULL; - run = CFArrayGetValueAtIndex(runs, runIndex); - CFDictionaryRef attr = NULL; - attr = CTRunGetAttributes(run); - if (0 == runIndex) { - NSNumber *baselineOffset = (NSNumber*)CFDictionaryGetValue(attr, NSBaselineOffsetAttributeName); - if (baselineOffset) { - lineOrigin.y += [baselineOffset doubleValue]; - } - } - CGContextSetTextPosition(context, lineOrigin.x, lineOrigin.y); - CTRunDraw(run, context, CFRangeMake(0, 0)); - CFIndex glyphCount = CTRunGetGlyphCount(run); - if (glyphCount <= 0) continue; - - NSUnderlineStyle strikethrough = (NSUnderlineStyle)CFDictionaryGetValue(attr, NSStrikethroughStyleAttributeName); - - if (strikethrough) { - // draw strikethrough - [self drawLineThroughWithRun:runs context:context index:runIndex origin:lineOrigin]; - } - } -} - -- (CTLineRef)buildTruncatedLineWithRuns:(CFArrayRef)runs lines:(NSMutableArray*)mutableLines path:(CGPathRef)cgPath -{ - NSAttributedString * truncationToken = nil; - CTLineRef ctTruncatedLine = NULL; - CTLineRef lastLine = (__bridge CTLineRef)(mutableLines.lastObject); - - CFArrayRef lastLineRuns = CTLineGetGlyphRuns(lastLine); - NSUInteger lastLineRunCount = CFArrayGetCount(lastLineRuns); - - CTLineRef truncationTokenLine = NULL; - NSMutableDictionary *attrs = nil; - if (lastLineRunCount > 0) { - CTRunRef run = CFArrayGetValueAtIndex(runs, lastLineRunCount - 1); - attrs = (id)CTRunGetAttributes(run); - attrs = attrs ? attrs.mutableCopy : [NSMutableDictionary new]; - CTFontRef font = (__bridge CTFontRef)(attrs[(id)kCTFontAttributeName]); - CGFloat fontSize = font ? CTFontGetSize(font):32 * self.weexInstance.pixelScaleFactor; - UIFont * uiFont = [UIFont systemFontOfSize:fontSize]; - if (uiFont) { - font = CTFontCreateWithName((__bridge CFStringRef)uiFont.fontName, uiFont.pointSize, NULL); - } - if (font) { - attrs[(id)kCTFontAttributeName] = (__bridge id)(font); - uiFont = nil; - CFRelease(font); - } - CGColorRef color = (__bridge CGColorRef)(attrs[(id)kCTForegroundColorAttributeName]); - if (color && CFGetTypeID(color) == CGColorGetTypeID() && CGColorGetAlpha(color) == 0) { - [attrs removeObjectForKey:(id)kCTForegroundColorAttributeName]; - } - - attrs = attrs?:[NSMutableDictionary new]; - truncationToken = [[NSAttributedString alloc] initWithString:WXTextTruncationToken attributes:attrs]; - truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)truncationToken); - } - - if (truncationTokenLine) { - // default truncationType is kCTLineTruncationEnd - CTLineTruncationType truncationType = kCTLineTruncationEnd; - NSAttributedString *attributedString = [self ctAttributedString]; - NSAttributedString * lastLineText = nil; - NSRange lastLineTextRange = WXNSRangeFromCFRange(CTLineGetStringRange(lastLine)); - NSRange attributeStringRange = NSMakeRange(0, attributedString.string.length); - NSRange interSectionRange = NSIntersectionRange(lastLineTextRange, attributeStringRange); - if (!NSEqualRanges(interSectionRange, lastLineTextRange)) { - // out of bounds - lastLineTextRange = interSectionRange; - } - lastLineText = [attributedString attributedSubstringFromRange: lastLineTextRange]; - if (!lastLineText) { - lastLineText = attributedString; - } - NSMutableAttributedString *mutableLastLineText = lastLineText.mutableCopy; - [mutableLastLineText appendAttributedString:truncationToken]; - CTLineRef ctLastLineExtend = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)[mutableLastLineText copy]); - if (ctLastLineExtend) { - CGRect cgPathRect = CGRectZero; - CGFloat truncatedWidth = 0; - if (CGPathIsRect(cgPath, &cgPathRect)) { - truncatedWidth = cgPathRect.size.width; - } - ctTruncatedLine = CTLineCreateTruncatedLine(ctLastLineExtend, truncatedWidth, truncationType, truncationTokenLine); - CFRelease(ctLastLineExtend); - ctLastLineExtend = NULL; - CFRelease(truncationTokenLine); - truncationTokenLine = NULL; - } - } - - return ctTruncatedLine; -} - -- (void)drawLineThroughWithRun:(CFArrayRef)runs context:(CGContextRef)context index:(CFIndex)runIndex origin:(CGPoint)lineOrigin -{ - CFRetain(runs); - CGContextRetain(context); - - CGContextSaveGState(context); - CGFloat xHeight = 0, underLinePosition = 0, lineThickness = 0; - CTRunRef run = CFArrayGetValueAtIndex(runs, runIndex); - WXTextGetRunsMaxMetric(runs, &xHeight, &underLinePosition, &lineThickness); - - CGPoint strikethroughStart; - strikethroughStart.x = lineOrigin.x - underLinePosition; - strikethroughStart.y = lineOrigin.y + xHeight/2; - CGPoint runPosition = CGPointZero; - CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition); - strikethroughStart.x = lineOrigin.x + runPosition.x; - CGContextSetLineWidth(context, WXTextDefaultLineThroughWidth); - double length = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL); - CGContextMoveToPoint(context, strikethroughStart.x, strikethroughStart.y); - CGContextAddLineToPoint(context, strikethroughStart.x + length, strikethroughStart.y); - CGContextStrokePath(context); - - CGContextRestoreGState(context); - CFRelease(runs); - CGContextRelease(context); -} - -- (CGSize)calculateTextHeightWithWidth:(CGFloat)aWidth -{ - CGFloat totalHeight = 0; - CGSize suggestSize = CGSizeZero; - NSAttributedString * attributedStringCpy = [self ctAttributedString]; - if (!attributedStringCpy) { - return CGSizeZero; - } - if (isnan(aWidth)) { - aWidth = CGFLOAT_MAX; - } - aWidth = [attributedStringCpy boundingRectWithSize:CGSizeMake(aWidth, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil].size.width; - CTFramesetterRef ctframesetterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attributedStringCpy)); - suggestSize = CTFramesetterSuggestFrameSizeWithConstraints(ctframesetterRef, CFRangeMake(0, 0), NULL, CGSizeMake(aWidth, MAXFLOAT), NULL); - - CGMutablePathRef path = NULL; - path = CGPathCreateMutable(); - // sufficient height to draw text - CGPathAddRect(path, NULL, CGRectMake(0, 0, aWidth, suggestSize.height * 10)); - - CTFrameRef frameRef = NULL; - frameRef = CTFramesetterCreateFrame(ctframesetterRef, CFRangeMake(0, attributedStringCpy.length), path, NULL); - CGPathRelease(path); - - CFArrayRef lines = NULL; - if (NULL == frameRef) { - //try to protect unexpected crash. - return suggestSize; - } - CFRelease(ctframesetterRef); - ctframesetterRef = NULL; - lines = CTFrameGetLines(frameRef); - CFIndex lineCount = CFArrayGetCount(lines); - CGFloat ascent = 0; - CGFloat descent = 0; - CGFloat leading = 0; - - // height = ascent + descent + lineCount*leading - // ignore linespaing - NSUInteger actualLineCount = 0; - for (CFIndex lineIndex = 0; (!_lines|| lineIndex < _lines) && lineIndex < lineCount; lineIndex ++) - { - CTLineRef lineRef = NULL; - lineRef = CFArrayGetValueAtIndex(lines, lineIndex); - CTLineGetTypographicBounds(lineRef, &ascent, &descent, &leading); - totalHeight += ascent + descent; - actualLineCount ++; - } - - totalHeight = totalHeight + actualLineCount * leading; - CFRelease(frameRef); - frameRef = NULL; - - if (WX_SYS_VERSION_LESS_THAN(@"10.0")) { - // there is something wrong with coreText drawing text height, trying to fix this with more efficent way. - if(actualLineCount && actualLineCount < lineCount) { - suggestSize.height = suggestSize.height * actualLineCount / lineCount; - } - return CGSizeMake(aWidth, suggestSize.height); - } - return CGSizeMake(aWidth, totalHeight); -} - -static void WXTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat *underlinePosition, CGFloat *lineThickness) -{ - CFRetain(runs); - CGFloat maxXHeight = 0; - CGFloat maxUnderlinePos = 0; - CGFloat maxLineThickness = 0; - for (NSUInteger index = 0, runsCount = CFArrayGetCount(runs); index < runsCount; index ++) { - CTRunRef run = CFArrayGetValueAtIndex(runs, index); - CFDictionaryRef attrs = CTRunGetAttributes(run); - if (attrs) { - CTFontRef font = CFDictionaryGetValue(attrs, kCTFontAttributeName); - if (font) { - CGFloat xHeight = CTFontGetXHeight(font); - if (xHeight > maxXHeight) { - maxXHeight = xHeight; - } - - CGFloat underlinePos = CTFontGetUnderlinePosition(font); - if (underlinePos < maxUnderlinePos) { - maxUnderlinePos = underlinePos; - } - - CGFloat lineThickness = CTFontGetUnderlineThickness(font); - if (lineThickness > maxLineThickness) { - maxLineThickness = lineThickness; - } - } - } - } - - if (xHeight) { - *xHeight = maxXHeight; - } - - if (underlinePosition) { - *underlinePosition = maxUnderlinePos; - } - - if (lineThickness) { - *lineThickness = maxLineThickness; - } - - CFRelease(runs); -} - -NS_INLINE NSRange WXNSRangeFromCFRange(CFRange range) { - return NSMakeRange(range.location, range.length); -} - -#ifdef UITEST -- (NSString *)description -{ - return super.description; -} -#endif - -- (void)_resetCSSNodeStyles:(NSArray *)styles -{ - [super _resetCSSNodeStyles:styles]; - if ([styles containsObject:@"color"]) { - _color = [UIColor blackColor]; - [self setNeedsRepaint]; - } - if ([styles containsObject:@"fontSize"]) { - _fontSize = WX_TEXT_FONT_SIZE; - [self setNeedsRepaint]; - [self setNeedsLayout]; - } -} - -@end -