Repository: incubator-weex
Updated Branches:
  refs/heads/0.12-dev 24ef3af94 -> 90c358303


* [ios] draw ellipse  and support letter-spacing


Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/242b1ec0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/242b1ec0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/242b1ec0

Branch: refs/heads/0.12-dev
Commit: 242b1ec04c0153dffc472635a5ea507b98a133fc
Parents: 5ea9966
Author: acton393 <zhangxing610...@gmail.com>
Authored: Mon Apr 17 20:46:42 2017 +0800
Committer: acton393 <zhangxing610...@gmail.com>
Committed: Mon Apr 17 20:46:42 2017 +0800

----------------------------------------------------------------------
 .../WeexSDK/Sources/Component/WXTextComponent.h |   1 +
 .../WeexSDK/Sources/Component/WXTextComponent.m | 259 +++++++++++++++----
 2 files changed, 212 insertions(+), 48 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/242b1ec0/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.h 
b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.h
index 627b774..e993366 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.h
@@ -22,4 +22,5 @@
 @interface WXTextComponent : WXComponent
 
 + (void)setRenderUsingCoreText:(BOOL)usingCoreText;
+- (BOOL)useCoreText;
 @end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/242b1ec0/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
index f9906ad..c4c8cd3 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m
+++ b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m
@@ -80,10 +80,13 @@
 
 @end
 
-static BOOL textRenderUsingCoreText = NO;
+static BOOL textRenderUsingCoreText = YES;
+
+NSString *const WXTextTruncationToken = @"\u2026";
+CGFloat WXTextDefaultLineThroughWidth = 1.2;
 
 @interface WXTextComponent()
-@property (nonatomic, assign)BOOL useCoreTextAttr;
+@property (nonatomic, assign) NSString *useCoreTextAttr;
 @end
 
 @implementation WXTextComponent
@@ -104,6 +107,8 @@ static BOOL textRenderUsingCoreText = NO;
     WXTextDecoration _textDecoration;
     NSString *_textOverflow;
     CGFloat _lineHeight;
+    CGFloat _letterSpacing;
+    BOOL _truncationLine; // support trunk tail
 }
 
 + (void)setRenderUsingCoreText:(BOOL)usingCoreText
@@ -127,9 +132,9 @@ static BOOL textRenderUsingCoreText = NO;
     if (self) {
         // just for coretext and textkit render replacement
         if ([attributes objectForKey:@"coretext"]) {
-            _useCoreTextAttr = [WXConvert BOOL:attributes[@"coretext"]];
+            _useCoreTextAttr = [WXConvert NSString:attributes[@"coretext"]];
         } else {
-            _useCoreTextAttr = NO;
+            _useCoreTextAttr = nil;
         }
         
         [self fillCSSStyles:styles];
@@ -141,9 +146,12 @@ static BOOL textRenderUsingCoreText = NO;
 
 - (BOOL)useCoreText
 {
-    if (_useCoreTextAttr) {
+    if ([_useCoreTextAttr isEqualToString:@"yes"]) {
         return YES;
     }
+    if ([_useCoreTextAttr isEqualToString:@"false"]) {
+        return NO;
+    }
     if ([WXTextComponent textRenderUsingCoreText]) {
         return YES;
     }
@@ -191,6 +199,7 @@ do {\
     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)
     
     UIEdgeInsets padding = {
         WXFloorPixelValue(self.cssNode->style.padding[CSS_TOP] + 
self.cssNode->style.border[CSS_TOP]),
@@ -229,7 +238,13 @@ do {\
 
 - (void)viewDidLoad
 {
-    ((WXText *)self.view).textStorage = _textStorage;
+    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) {
+        ((WXText *)self.view).textStorage = _textStorage;
+    }
     [self setNeedsDisplay];
 }
 
@@ -363,11 +378,11 @@ do {\
     }
     
     // set default lineBreakMode
-    // TODO:default clip
     paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
+    _truncationLine = NO;
     if (_textOverflow && [_textOverflow length] > 0) {
         if (_lines && [_textOverflow isEqualToString:@"ellipsis"])
-            paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
+            _truncationLine = YES;
     }
     
     if (_lineHeight) {
@@ -380,6 +395,10 @@ do {\
                                  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
@@ -497,10 +516,15 @@ do {\
 - (void)syncTextStorageForView
 {
     CGFloat width = self.calculatedFrame.size.width - (_padding.left + 
_padding.right);
-    NSTextStorage *textStorage = [self textStorageWithWidth:width];
+    NSTextStorage *textStorage = nil;
+    if (![self useCoreText]) {
+        textStorage = [self textStorageWithWidth:width];
+    }
     [self.weexInstance.componentManager  _addUITask:^{
         if ([self isViewLoaded]) {
-            ((WXText *)self.view).textStorage = textStorage;
+            if (![self useCoreText]) {
+                ((WXText *)self.view).textStorage = textStorage;
+            }
             [self readyToRender]; // notify super component
             [self setNeedsDisplay];
         }
@@ -553,7 +577,7 @@ do {\
         
         [layoutManager drawBackgroundForGlyphRange:glyphRange 
atPoint:textFrame.origin];
         [layoutManager drawGlyphsForGlyphRange:glyphRange 
atPoint:textFrame.origin];
-    }else {
+    } 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;
@@ -565,60 +589,191 @@ do {\
         
         NSMutableAttributedString * attributedStringCopy = [self 
buildCTAttributeString];
         //add path
-        CGPathRef path = NULL;
-        path = CGPathCreateWithRect(textFrame, NULL);
+        CGPathRef cgPath = NULL;
+        cgPath = CGPathCreateWithRect(textFrame, NULL);
         CTFramesetterRef framesetter = NULL;
         framesetter = 
CTFramesetterCreateWithAttributedString((CFTypeRef)attributedStringCopy);
-        CTFrameRef coretextFrameRef = NULL;
-        coretextFrameRef = CTFramesetterCreateFrame(framesetter, 
CFRangeMake(0, 0), path, NULL);
-        CGPathRelease(path);
-        path = NULL;
+        CTFrameRef _coreTextFrameRef = NULL;
+        if (_coreTextFrameRef) {
+            CFRelease(_coreTextFrameRef);
+        }
+        _coreTextFrameRef = CTFramesetterCreateFrame(framesetter, 
CFRangeMake(0, 0), cgPath, NULL);
         CFRelease(framesetter);
         framesetter = NULL;
-        CFArrayRef lines = NULL;
-        lines = CTFrameGetLines(coretextFrameRef);
-        CFIndex lineCount = CFArrayGetCount(lines);
+        CFArrayRef ctLines = NULL;
+        ctLines = CTFrameGetLines(_coreTextFrameRef);
+        CFIndex lineCount = CFArrayGetCount(ctLines);
+        NSMutableArray * mutableLines = [NSMutableArray new];
         CGPoint lineOrigins[lineCount];
-        CTFrameGetLineOrigins(coretextFrameRef, CFRangeMake(0, 0), 
lineOrigins);
-        for (CFIndex lineIndex = 0;(!_lines || _lines > lineIndex) && 
lineIndex < lineCount; lineIndex ++) {
+        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(lines, lineIndex);
+            lineRef = CFArrayGetValueAtIndex(ctLines, lineIndex);
+            if (!lineRef) {
+                break;
+            }
             CGPoint lineOrigin = lineOrigins[lineIndex];
             lineOrigin.x += padding.left;
             lineOrigin.y -= padding.top;
             CGContextSetTextPosition(context, lineOrigin.x, lineOrigin.y);
             CFArrayRef runs = CTLineGetGlyphRuns(lineRef);
-            CGFloat xHeight = 0, underLinePosition = 0, lineThickness = 0 ;
-            WXTextGetRunsMaxMetric(runs, &xHeight, &underLinePosition, 
&lineThickness);
-            CGPoint strikethroughStart;
-            strikethroughStart.x =  lineOrigin.x - underLinePosition;
-            strikethroughStart.y = lineOrigin.y + xHeight/2;
-            for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runs); 
runIndex ++) {
-                CTRunRef run = NULL;
-                run = CFArrayGetValueAtIndex(runs, runIndex);
-                CTRunDraw(run, context, CFRangeMake(0, 0));
-                CFDictionaryRef attr = NULL;
-                attr = CTRunGetAttributes(run);
-                NSUnderlineStyle strikethrough = 
(NSUnderlineStyle)CFDictionaryGetValue(attr, NSStrikethroughStyleAttributeName);
-                
-                if (strikethrough) {
-                    // currently draw strikethrough
-                    CGPoint runPosition = CGPointZero;
-                    CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition);
-                    strikethroughStart.x = lineOrigin.x + runPosition.x;
-                    CGContextSetLineWidth(context, 1.5);
-                    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);
+            [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) {
+                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];
             }
         }
-        CFRelease(coretextFrameRef);
+        
+        [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);
+        CTRunDraw(run, context, CFRangeMake(0, 0));
+        CFDictionaryRef attr = NULL;
+        attr = CTRunGetAttributes(run);
+        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 buildCTAttributeString];
+        NSAttributedString * lastLineText = [attributedString 
attributedSubstringFromRange: 
WXNSRangeFromCFRange(CTLineGetStringRange(lastLine))];
+//        NSMutableAttributedString *.mutableCopy;
+        NSMutableAttributedString *mutableLastLineText = 
lastLineText.mutableCopy;
+        [mutableLastLineText appendAttributedString:truncationToken];
+        CTLineRef ctLastLineExtend = 
CTLineCreateWithAttributedString((__bridge 
CFAttributedStringRef)mutableLastLineText);
+        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
 {
     if (isnan(aWidth)) {
@@ -670,7 +825,9 @@ do {\
     return CGSizeMake(suggestSize.width, totalHeight);
 }
 
-static void WXTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat 
*underlinePosition, CGFloat *lineThickness) {
+static void WXTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat 
*underlinePosition, CGFloat *lineThickness)
+{
+    CFRetain(runs);
     CGFloat maxXHeight = 0;
     CGFloat maxUnderlinePos = 0;
     CGFloat maxLineThickness = 0;
@@ -709,6 +866,12 @@ static void WXTextGetRunsMaxMetric(CFArrayRef runs, 
CGFloat *xHeight, CGFloat *u
     if (lineThickness) {
         *lineThickness = maxLineThickness;
     }
+    
+    CFRelease(runs);
+}
+                                                                               
                                                        
+NS_INLINE NSRange WXNSRangeFromCFRange(CFRange range) {
+    return NSMakeRange(range.location, range.length);
 }
 
 #ifdef UITEST

Reply via email to