From aae482236d7faffc225ae99962c427845782a4ce Mon Sep 17 00:00:00 2001 From: Horcrux Date: Fri, 11 Nov 2016 00:48:55 +0800 Subject: [PATCH] Finish TSpan --- index.js | 8 +- ios/Elements/RNSVGGroup.h | 6 +- ios/Elements/RNSVGGroup.m | 17 +- ios/Elements/RNSVGImage.m | 8 +- ios/Elements/RNSVGPath.m | 2 + ios/RNSVG.xcodeproj/project.pbxproj | 2 + ios/RNSVGRenderable.h | 10 +- ios/RNSVGRenderable.m | 50 ++--- ios/RNSVGViewBox.m | 10 +- ios/Shapes/RNSVGCircle.m | 8 +- ios/Shapes/RNSVGEllipse.m | 2 +- ios/Shapes/RNSVGLine.m | 2 +- ios/Shapes/RNSVGRect.m | 2 +- ios/Text/RNSVGTSpan.h | 8 +- ios/Text/RNSVGTSpan.m | 106 +++++----- ios/Text/RNSVGText.h | 18 +- ios/Text/RNSVGText.m | 312 +++++++++++++++++++--------- ios/Utils/RCTConvert+RNSVG.h | 1 - ios/Utils/RCTConvert+RNSVG.m | 36 ---- ios/Utils/RNSVGGlyphPoint.h | 23 ++ ios/ViewManagers/RNSVGTextManager.m | 4 +- lib/extract/extractText.js | 4 +- package.json | 2 +- 23 files changed, 379 insertions(+), 262 deletions(-) create mode 100644 ios/Utils/RNSVGGlyphPoint.h diff --git a/index.js b/index.js index 02107e05..adfcab1c 100644 --- a/index.js +++ b/index.js @@ -8,6 +8,7 @@ import Svg from './elements/Svg'; import Path from './elements/Path'; import G from './elements/G'; import Text from './elements/Text'; +import TSpan from './elements/TSpan'; import Use from './elements/Use'; import Image from './elements/Image'; import Symbol from './elements/Symbol'; @@ -16,8 +17,6 @@ import LinearGradient from './elements/LinearGradient'; import RadialGradient from './elements/RadialGradient'; import Stop from './elements/Stop'; import ClipPath from './elements/ClipPath'; -import TSpan from './elements/TSpan'; -import TextPath from './elements/TextPath'; export { Svg, @@ -25,6 +24,7 @@ export { Ellipse, G, Text, + TSpan, Path, Polygon, Polyline, @@ -37,9 +37,7 @@ export { LinearGradient, RadialGradient, Stop, - ClipPath, - TSpan, - TextPath + ClipPath }; export default Svg; diff --git a/ios/Elements/RNSVGGroup.h b/ios/Elements/RNSVGGroup.h index 411db7f1..1ef6a8f5 100644 --- a/ios/Elements/RNSVGGroup.h +++ b/ios/Elements/RNSVGGroup.h @@ -11,8 +11,10 @@ #import "RNSVGContainer.h" #import "RNSVGCGFCRule.h" #import "RNSVGSvgView.h" -#import "RNSVGRenderable.h" +#import "RNSVGPath.h" -@interface RNSVGGroup : RNSVGRenderable +@interface RNSVGGroup : RNSVGPath + +- (void)pathRenderLayerTo:(CGContextRef)contex; @end diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m index 492abf38..39538caf 100644 --- a/ios/Elements/RNSVGGroup.m +++ b/ios/Elements/RNSVGGroup.m @@ -21,14 +21,29 @@ } return YES; }]; - + [self traverseSubviews:^(RNSVGNode *node) { [node mergeProperties:self mergeList:self.ownedPropList inherited:YES]; [node renderTo:context]; + + if ([node isKindOfClass: [RNSVGRenderable class]]) { + RNSVGRenderable *renderable = node; + [self concatLayoutBoundingBox:[renderable getLayoutBoundingBox]]; + } return YES; }]; } +- (void)pathRenderLayerTo:(CGContextRef)context +{ + [super renderLayerTo:context]; +} + +- (void)concatLayoutBoundingBox:(CGRect)boundingBox +{ + [self setLayoutBoundingBox:CGRectUnion(boundingBox, [self getLayoutBoundingBox])]; +} + - (CGPathRef)getPath:(CGContextRef)context { CGMutablePathRef path = CGPathCreateMutable(); diff --git a/ios/Elements/RNSVGImage.m b/ios/Elements/RNSVGImage.m index f6587686..9a0a43c9 100644 --- a/ios/Elements/RNSVGImage.m +++ b/ios/Elements/RNSVGImage.m @@ -126,13 +126,15 @@ viewBox.height = [NSString stringWithFormat:@"%f", rectHeight]; viewBox.align = self.align; viewBox.meetOrSlice = self.meetOrSlice; - [viewBox setBoundingBox:CGRectMake(0, 0, rectWidth, rectHeight)]; - CGAffineTransform transform = [viewBox getTransform]; + [viewBox setContextBoundingBox:CGRectMake(0, 0, rectWidth, rectHeight)]; + + CGAffineTransform transform = [viewBox getTransform]; renderRect = CGRectApplyAffineTransform(renderRect, transform); renderRect = CGRectApplyAffineTransform(renderRect, CGAffineTransformMakeTranslation(rectX, rectY)); CGContextClipToRect(context, rect); + [self setLayoutBoundingBox:rect]; CGContextDrawImage(context, renderRect, _image); CGContextRestoreGState(context); @@ -140,7 +142,7 @@ - (CGRect)getRect:(CGContextRef)context { - [self setBoundingBox:CGContextGetClipBoundingBox(context)]; + [self setContextBoundingBox:CGContextGetClipBoundingBox(context)]; CGFloat x = [self getWidthRelatedValue:self.x]; CGFloat y = [self getHeightRelatedValue:self.y]; CGFloat width = [self getWidthRelatedValue:self.width]; diff --git a/ios/Elements/RNSVGPath.m b/ios/Elements/RNSVGPath.m index 72d54e5c..c59f5c37 100644 --- a/ios/Elements/RNSVGPath.m +++ b/ios/Elements/RNSVGPath.m @@ -32,6 +32,8 @@ self.d = [self getPath:context]; CGPathRef path = self.d; + [self setLayoutBoundingBox:CGPathGetBoundingBox(path)]; + if ((!self.fill && !self.stroke) || !path) { return; } diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj index 0f9f69d0..3727f6cf 100644 --- a/ios/RNSVG.xcodeproj/project.pbxproj +++ b/ios/RNSVG.xcodeproj/project.pbxproj @@ -160,6 +160,7 @@ 10FDEEB01D3FB60500A5C46C /* RNSVGBaseBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGBaseBrush.h; sourceTree = ""; }; 10FDEEB11D3FB60500A5C46C /* RNSVGBaseBrush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGBaseBrush.m; sourceTree = ""; }; 10FDEEB31D3FBED400A5C46C /* RNSVGBrushType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGBrushType.h; sourceTree = ""; }; + 7F888B9C1DD378000038D083 /* RNSVGGlyphPoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSVGGlyphPoint.h; path = Utils/RNSVGGlyphPoint.h; sourceTree = ""; }; 7FF070191DC249BE000E28A0 /* RNSVGTextAnchor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextAnchor.h; path = Utils/RNSVGTextAnchor.h; sourceTree = ""; }; /* End PBXFileReference section */ @@ -327,6 +328,7 @@ 1039D29B1CE72177001E90A8 /* RCTConvert+RNSVG.h */, 1039D29C1CE72177001E90A8 /* RCTConvert+RNSVG.m */, 1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */, + 7F888B9C1DD378000038D083 /* RNSVGGlyphPoint.h */, ); name = Utils; sourceTree = ""; diff --git a/ios/RNSVGRenderable.h b/ios/RNSVGRenderable.h index 6866e511..14ee14bf 100644 --- a/ios/RNSVGRenderable.h +++ b/ios/RNSVGRenderable.h @@ -30,13 +30,11 @@ @property (nonatomic, copy) NSArray *propList; @property (nonatomic, strong) NSMutableArray *ownedPropList; -- (void)setBoundingBox:(CGRect)boundingBox; +- (void)setContextBoundingBox:(CGRect)contextBoundingBox; +- (CGRect)getContextBoundingBox; +- (void)setLayoutBoundingBox:(CGRect)layoutBoundingBox; +- (CGRect)getLayoutBoundingBox; - (CGFloat)getWidthRelatedValue:(NSString *)string; - (CGFloat)getHeightRelatedValue:(NSString *)string; -- (CGFloat)getContextWidth; -- (CGFloat)getContextHeight; -- (CGFloat)getContextX; -- (CGFloat)getContextY; - @end diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index 8e6ecbd7..9228d107 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -15,9 +15,8 @@ NSArray *_changedList; RNSVGPercentageConverter *_widthConverter; RNSVGPercentageConverter *_heightConverter; - CGFloat _contextWidth; - CGFloat _contextHeight; - CGRect _boundingBox; + CGRect _contextBoundingBox; + CGRect _renderBoundingBox; } - (id)init @@ -206,11 +205,28 @@ } } -- (void)setBoundingBox:(CGRect)boundingBox +- (void)setContextBoundingBox:(CGRect)contextBoundingBox { - _boundingBox = boundingBox; - _widthConverter = [[RNSVGPercentageConverter alloc] initWithRelativeAndOffset:boundingBox.size.width offset:0]; - _heightConverter = [[RNSVGPercentageConverter alloc] initWithRelativeAndOffset:boundingBox.size.height offset:0]; + _contextBoundingBox = contextBoundingBox; + _widthConverter = [[RNSVGPercentageConverter alloc] initWithRelativeAndOffset:contextBoundingBox.size.width + offset:0]; + _heightConverter = [[RNSVGPercentageConverter alloc] initWithRelativeAndOffset:contextBoundingBox.size.height + offset:0]; +} + +- (CGRect)getContextBoundingBox +{ + return _contextBoundingBox; +} + +- (void)setLayoutBoundingBox:(CGRect)layoutBoundingBox +{ + _renderBoundingBox = layoutBoundingBox; +} + +- (CGRect)getLayoutBoundingBox +{ + return _renderBoundingBox; } - (CGFloat)getWidthRelatedValue:(NSString *)string @@ -223,26 +239,6 @@ return [_heightConverter stringToFloat:string]; } -- (CGFloat)getContextWidth -{ - return CGRectGetWidth(_boundingBox); -} - -- (CGFloat)getContextHeight -{ - return CGRectGetHeight(_boundingBox); -} - -- (CGFloat)getContextX -{ - return CGRectGetMinX(_boundingBox); -} - -- (CGFloat)getContextY -{ - return CGRectGetMinY(_boundingBox); -} - - (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray *)mergeList { diff --git a/ios/RNSVGViewBox.m b/ios/RNSVGViewBox.m index fa790361..e803f571 100644 --- a/ios/RNSVGViewBox.m +++ b/ios/RNSVGViewBox.m @@ -71,7 +71,7 @@ - (void)renderTo:(CGContextRef)context { - [self setBoundingBox:CGContextGetClipBoundingBox(context)]; + [self setContextBoundingBox:CGContextGetClipBoundingBox(context)]; self.matrix = [self getTransform]; [super renderTo:context]; } @@ -87,10 +87,10 @@ CGFloat vbHeight = [self getHeightRelatedValue:self.vbHeight]; // Let e-x, e-y, e-width, e-height be the position and size of the element respectively. - CGFloat eX = [self getContextX]; - CGFloat eY = [self getContextY]; - CGFloat eWidth = self.width ? [self getWidthRelatedValue:self.width] : [self getContextWidth]; - CGFloat eHeight = self.height ? [self getHeightRelatedValue:self.height] : [self getContextHeight]; + CGFloat eX = [self getContextBoundingBox].origin.x; + CGFloat eY = [self getContextBoundingBox].origin.y; + CGFloat eWidth = self.width ? [self getWidthRelatedValue:self.width] : [self getContextBoundingBox].size.width; + CGFloat eHeight = self.height ? [self getHeightRelatedValue:self.height] : [self getContextBoundingBox].size.height; // Let align be the align value of preserveAspectRatio, or 'xMidyMid' if preserveAspectRatio is not defined. NSString *align = self.align; diff --git a/ios/Shapes/RNSVGCircle.m b/ios/Shapes/RNSVGCircle.m index 0dec9f34..a8791cc5 100644 --- a/ios/Shapes/RNSVGCircle.m +++ b/ios/Shapes/RNSVGCircle.m @@ -40,7 +40,7 @@ - (CGPathRef)getPath:(CGContextRef)context { - [self setBoundingBox:CGContextGetClipBoundingBox(context)]; + [self setContextBoundingBox:CGContextGetClipBoundingBox(context)]; CGMutablePathRef path = CGPathCreateMutable(); RNSVGPercentageConverter* convert = [[RNSVGPercentageConverter alloc] init]; CGFloat cx = [self getWidthRelatedValue:self.cx]; @@ -51,7 +51,11 @@ if ([convert isPercentage:self.r]) { CGFloat radiusPercent = [convert percentageToFloat:self.r relative:1 offset:0]; - r = sqrt(pow(([self getContextWidth] * radiusPercent), 2) + pow(([self getContextHeight] * radiusPercent), 2)) / sqrt(2); + + r = sqrt( + pow((CGRectGetWidth([self getContextBoundingBox]) * radiusPercent), 2) + + pow((CGRectGetHeight([self getContextBoundingBox]) * radiusPercent), 2) + ) / sqrt(2); } else { r = [self.r floatValue]; } diff --git a/ios/Shapes/RNSVGEllipse.m b/ios/Shapes/RNSVGEllipse.m index eda97bf0..814c1e85 100644 --- a/ios/Shapes/RNSVGEllipse.m +++ b/ios/Shapes/RNSVGEllipse.m @@ -49,7 +49,7 @@ - (CGPathRef)getPath:(CGContextRef)context { - [self setBoundingBox:CGContextGetClipBoundingBox(context)]; + [self setContextBoundingBox:CGContextGetClipBoundingBox(context)]; CGMutablePathRef path = CGPathCreateMutable(); CGFloat cx = [self getWidthRelatedValue:self.cx]; CGFloat cy = [self getHeightRelatedValue:self.cy]; diff --git a/ios/Shapes/RNSVGLine.m b/ios/Shapes/RNSVGLine.m index fb3f3751..2f7df69b 100644 --- a/ios/Shapes/RNSVGLine.m +++ b/ios/Shapes/RNSVGLine.m @@ -49,7 +49,7 @@ - (CGPathRef)getPath:(CGContextRef)context { - [self setBoundingBox:CGContextGetClipBoundingBox(context)]; + [self setContextBoundingBox:CGContextGetClipBoundingBox(context)]; CGMutablePathRef path = CGPathCreateMutable(); CGFloat x1 = [self getWidthRelatedValue:self.x1]; CGFloat y1 = [self getHeightRelatedValue:self.y1]; diff --git a/ios/Shapes/RNSVGRect.m b/ios/Shapes/RNSVGRect.m index 233b78e5..4e3d3e79 100644 --- a/ios/Shapes/RNSVGRect.m +++ b/ios/Shapes/RNSVGRect.m @@ -67,7 +67,7 @@ - (CGPathRef)getPath:(CGContextRef)context { - [self setBoundingBox:CGContextGetClipBoundingBox(context)]; + [self setContextBoundingBox:CGContextGetClipBoundingBox(context)]; CGMutablePathRef path = CGPathCreateMutable(); CGFloat x = [self getWidthRelatedValue:self.x]; CGFloat y = [self getHeightRelatedValue:self.y]; diff --git a/ios/Text/RNSVGTSpan.h b/ios/Text/RNSVGTSpan.h index caaff504..f40bf197 100644 --- a/ios/Text/RNSVGTSpan.h +++ b/ios/Text/RNSVGTSpan.h @@ -11,14 +11,8 @@ #import "RNSVGPath.h" #import "RNSVGText.h" -@interface RNSVGTSpan : RNSVGPath +@interface RNSVGTSpan : RNSVGText -@property (nonatomic, assign) RNSVGTextAnchor textAnchor; -@property (nonatomic, assign) NSArray *deltaX; -@property (nonatomic, assign) NSArray *deltaY; -@property (nonatomic, strong) NSString *positionX; -@property (nonatomic, strong) NSString *positionY; -@property (nonatomic, assign) NSDictionary *font; @property (nonatomic, strong) NSString *content; @end diff --git a/ios/Text/RNSVGTSpan.m b/ios/Text/RNSVGTSpan.m index 21281c91..d5465333 100644 --- a/ios/Text/RNSVGTSpan.m +++ b/ios/Text/RNSVGTSpan.m @@ -9,60 +9,59 @@ #import "RNSVGTSpan.h" #import "RNSVGBezierPath.h" +#import "RNSVGText.h" -@class RNSVGText; @implementation RNSVGTSpan +- (void)renderLayerTo:(CGContextRef)context +{ + if (self.content) { + [self pathRenderLayerTo:context]; + } else { + [super renderLayerTo:context]; + } +} + - (CGPathRef)getPath:(CGContextRef)context { - [self setBoundingBox:CGContextGetClipBoundingBox(context)]; + if (!self.content) { + return [self getTextGroupPath:context]; + } + [self setContextBoundingBox:CGContextGetClipBoundingBox(context)]; CGMutablePathRef path = CGPathCreateMutable(); -// if (![self.content isEqualToString:@""]) { -// // Create a dictionary for this font -// CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ -// (NSString *)kCTFontAttributeName: (__bridge id)self.font, -// (NSString *)kCTForegroundColorFromContextAttributeName: @YES -// }; -// -// CFStringRef string = (__bridge CFStringRef)self.content; -// CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); -// CTLineRef line = CTLineCreateWithAttributedString(attrString); -// CFRelease(attrString); -// -// CGMutablePathRef linePath = [self setLinePath:line]; -// -// // Set up text frame with font metrics -// CGFloat size = CTFontGetSize(self.font); -// CGFloat px = self.px ? [self getWidthRelatedValue:self.px] : 0; -// CGFloat py = self.py ? [self getHeightRelatedValue:self.py] : 0; -// -// if (self.px) { -// text.offsetX = px; -// } -// -// if (self.py) { -// text.offsetY = py + size * 1.1; -// } -// -// text.offsetX += self.dx; -// text.offsetY += self.dy; -// -// CGAffineTransform offset = CGAffineTransformMakeTranslation(text.offsetX, text.offsetY); -// -// text.offsetX += CTLineGetTypographicBounds(line, nil, nil, nil); -// -// CGPathAddPath(path, &offset, linePath); -// CGPathRelease(linePath); -// } else { -// text.offsetX += self.dx; -// text.offsetY += self.dy; -// } + if ([self.content isEqualToString:@""]) { + RNSVGGlyphPoint computedPoint = [self getComputedGlyphPoint:0 glyphOffset:CGPointZero]; + [self getTextRoot].lastX = computedPoint.x; + [self getTextRoot].lastY = computedPoint.y; + return path; + } + + CTFontRef font = [self getComputedFont]; + // Create a dictionary for this font + CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ + (NSString *)kCTFontAttributeName: (__bridge id)font, + (NSString *)kCTForegroundColorFromContextAttributeName: @YES + }; + + CFStringRef string = (__bridge CFStringRef)self.content; + CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); + CTLineRef line = CTLineCreateWithAttributedString(attrString); + + CGMutablePathRef linePath = [self getLinePath:line]; + CGAffineTransform offset = CGAffineTransformMakeTranslation(0, CTFontGetSize(font)); + CGPathAddPath(path, &offset, linePath); + + // clean up + CFRelease(attrString); + CFRelease(line); + CGPathRelease(linePath); + [self resetTextPathAttributes]; return (CGPathRef)CFAutorelease(path); } -- (CGMutablePathRef)setLinePath:(CTLineRef)line +- (CGMutablePathRef)getLinePath:(CTLineRef)line { CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0); CGMutablePathRef path = CGPathCreateMutable(); @@ -81,22 +80,21 @@ CTFontRef runFont = CFDictionaryGetValue(attributes, kCTFontAttributeName); - for(CFIndex i = 0; i < runGlyphCount; ++i) { - CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[i], nil); - CGPoint point = positions[i]; - - if (letter) { - CGAffineTransform transform; - - transform = CGAffineTransformTranslate(upsideDown, point.x, point.y); - - - CGPathAddPath(path, &transform, letter); + CGFloat lineStartX; + CFIndex i; + for(i = 0; i < runGlyphCount; i++) { + RNSVGGlyphPoint computedPoint = [self getComputedGlyphPoint:i glyphOffset:positions[i]]; + if (!i) { + lineStartX = computedPoint.x; } + CGAffineTransform transform = CGAffineTransformTranslate(upsideDown, computedPoint.x, -computedPoint.y); + CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[i], nil); + CGPathAddPath(path, &transform, letter); CGPathRelease(letter); } + [self getTextRoot].lastX = lineStartX + CGPathGetBoundingBox(path).size.width; return path; } diff --git a/ios/Text/RNSVGText.h b/ios/Text/RNSVGText.h index 3e1b16c5..3aa36d0a 100644 --- a/ios/Text/RNSVGText.h +++ b/ios/Text/RNSVGText.h @@ -7,19 +7,27 @@ */ #import +#import "RNSVGGlyphPoint.h" #import "RNSVGGroup.h" #import "RNSVGTextAnchor.h" @interface RNSVGText : RNSVGGroup @property (nonatomic, assign) RNSVGTextAnchor textAnchor; -@property (nonatomic, assign) NSArray *deltaX; -@property (nonatomic, assign) NSArray *deltaY; +@property (nonatomic, strong) NSArray *deltaX; +@property (nonatomic, strong) NSArray *deltaY; @property (nonatomic, strong) NSString *positionX; @property (nonatomic, strong) NSString *positionY; -@property (nonatomic, assign) NSDictionary *font; +@property (nonatomic, strong) NSDictionary *font; -@property (nonatomic, assign) CGFloat offsetX; -@property (nonatomic, assign) CGFloat offsetY; +@property (nonatomic, assign) CGFloat lastX; +@property (nonatomic, assign) CGFloat lastY; +@property (nonatomic, assign) NSUInteger lastIndex; + +- (CTFontRef)getComputedFont; +- (RNSVGGlyphPoint)getComputedGlyphPoint:(NSUInteger *)index glyphOffset:(CGPoint)glyphOffset; +- (RNSVGText *)getTextRoot; +- (CGPathRef)getTextGroupPath:(CGContextRef)context; +- (void)resetTextPathAttributes; @end diff --git a/ios/Text/RNSVGText.m b/ios/Text/RNSVGText.m index dc18451f..a21ab386 100644 --- a/ios/Text/RNSVGText.m +++ b/ios/Text/RNSVGText.m @@ -8,10 +8,13 @@ #import "RNSVGText.h" #import "RNSVGBezierPath.h" -#import "RCTConvert+RNSVG.h" +#import "RCTFont.h" #import @implementation RNSVGText +{ + RNSVGText *_textRoot; +} - (void)setTextAnchor:(RNSVGTextAnchor)textAnchor { @@ -19,112 +22,221 @@ _textAnchor = textAnchor; } -//- (void)renderLayerTo:(CGContextRef)context -//{ - //CGFloat shift = [self getShift:context path:nil]; - // Translate path by alignment offset - //CGContextSaveGState(context); - //CGContextConcatCTM(context, CGAffineTransformMakeTranslation(-shift, 0)); - //[super renderLayerTo:context]; - //CGContextRestoreGState(context); -//} - -//- (CGPathRef)getPath:(CGContextRef)context -//{ -// CGMutablePathRef path = CGPathCreateMutable(); - -// CGPathRef collection = [self getPathFromSuper:context]; -// -// // get alignment shift and Translate CGPath by it. -// CGFloat shift = [self getShift:context path:collection]; -// CGAffineTransform align = CGAffineTransformMakeTranslation(shift, 0); -// CGPathAddPath(path, &align, collection); -// CGPathRelease(collection); - -// return (CGPathRef)CFAutorelease(path); -//} - -- (CGPathRef)getContentPath:(CGContextRef)context +- (void)renderLayerTo:(CGContextRef)context { - [self setBoundingBox:CGContextGetClipBoundingBox(context)]; - CGMutablePathRef path = CGPathCreateMutable(); - -// if (![self.content isEqualToString:@""]) { -// CGFontRef *font = [RCTConvert RNSVGFont:self.font]; -// NSLog(@"font: %@", font); - // Create a dictionary for this font -// CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ -// (NSString *)kCTFontAttributeName: (__bridge id)self.font, -// (NSString *)kCTForegroundColorFromContextAttributeName: @YES -// }; -// -// CFStringRef string = (__bridge CFStringRef)self.content; -// CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); -// CTLineRef line = CTLineCreateWithAttributedString(attrString); -// CFRelease(attrString); -// -// CGMutablePathRef linePath = [self setLinePath:line]; -// -// // Set up text frame with font metrics -// CGFloat size = CTFontGetSize(self.font); -// CGFloat px = self.px ? [self getWidthRelatedValue:self.px] : 0; -// CGFloat py = self.py ? [self getHeightRelatedValue:self.py] : 0; -// -// if (self.px) { -// text.offsetX = px; -// } -// -// if (self.py) { -// text.offsetY = py + size * 1.1; -// } -// -// text.offsetX += self.dx; -// text.offsetY += self.dy; -// -// CGAffineTransform offset = CGAffineTransformMakeTranslation(text.offsetX, text.offsetY); -// -// text.offsetX += CTLineGetTypographicBounds(line, nil, nil, nil); -// -// CGPathAddPath(path, &offset, linePath); -// CGPathRelease(linePath); -// } else { -// text.offsetX += self.dx; -// text.offsetY += self.dy; -// } - - - return path; + CGContextSaveGState(context); + CGContextConcatCTM(context, CGAffineTransformMakeTranslation([self getShift:context path:nil], 0)); + [super renderLayerTo:context]; + CGContextRestoreGState(context); } -- (CGPathRef)getPathFromSuper:(CGContextRef)context +- (CGPathRef)getPath:(CGContextRef)context +{ + CGMutablePathRef shape = [self getTextGroupPath:context]; + CGAffineTransform translation = CGAffineTransformMakeTranslation([self getShift:context path:shape], 0); + CGMutablePathRef path = CGPathCreateCopyByTransformingPath(shape, &translation); + CGPathRelease(shape); + return (CGPathRef)CFAutorelease(path); +} + +- (CGPathRef)getTextGroupPath:(CGContextRef)context { CGPathRef path = [super getPath:context]; - // reset offsetX and offsetY - self.offsetX = self.offsetY = 0; + [self resetTextPathAttributes]; return path; } -//- (CGFloat)getShift:(CGContextRef)context path:(CGPathRef)path -//{ -// if (!path) { -// path = [self getPathFromSuper:context]; -// } -// -// CGFloat width = CGPathGetBoundingBox(path).size.width; -// CGFloat shift; -// switch (self.alignment) { -// case kCTTextAlignmentRight: -// shift = width; -// break; -// case kCTTextAlignmentCenter: -// shift = width / 2; -// break; -// default: -// shift = 0; -// break; -// } -// -// return shift; -//} +- (CGFloat)getShift:(CGContextRef)context path:(CGPathRef)path +{ + if (!path) { + path = [self getTextGroupPath:context]; + } + + CGFloat width = CGRectGetWidth(CGPathGetBoundingBox(path)); + + switch ([self getComputedTextAnchor]) { + case kRNSVGTextAnchorMiddle: + return -width / 2; + case kRNSVGTextAnchorEnd: + return -width; + default: + return 0; + } +} + +- (RNSVGTextAnchor)getComputedTextAnchor +{ + RNSVGTextAnchor anchor = self.textAnchor; + RNSVGText *child = [self.subviews objectAtIndex:0]; + + while (child.subviews.count && anchor == kRNSVGTextAnchorAuto) { + anchor = child.textAnchor; + child = [child.subviews objectAtIndex:0]; + } + + return anchor; +} + +- (BOOL)extendFontFromInheritedFont:(NSMutableDictionary *)font inheritedFont:(NSDictionary *)inheritedFont +{ + NSString *fontFamily = font[@"fontFamily"]; + NSNumber *fontSize = font[@"fontSize"]; + NSString *fontWeight = font[@"fontWeight"]; + NSString *fontStyle = font[@"fontStyle"]; + + BOOL fontAttributesSet = YES; + if (!fontFamily && inheritedFont[@"fontFamily"]) { + [font setObject:inheritedFont[@"fontFamily"] forKey:@"fontFamily"]; + fontAttributesSet = NO; + } + + if (fontSize == nil && inheritedFont[@"fontSize"] != nil) { + [font setObject:inheritedFont[@"fontSize"] forKey:@"fontSize"]; + fontAttributesSet = NO; + } + + if (!fontWeight && inheritedFont[@"fontWeight"]) { + [font setObject:inheritedFont[@"fontWeight"] forKey:@"fontWeight"]; + fontAttributesSet = NO; + } + + if (!fontStyle && inheritedFont[@"fontStyle"]) { + [font setObject:inheritedFont[@"fontStyle"] forKey:@"fontStyle"]; + fontAttributesSet = NO; + } + + return fontAttributesSet; +} + +- (CTFontRef)getComputedFont +{ + NSMutableDictionary *fontDict = [[NSMutableDictionary alloc] init]; + [self traverseTextSuperviews:^(RNSVGText *node) { + return [self extendFontFromInheritedFont:fontDict inheritedFont:node.font]; + }]; + + NSString *fontFamily = fontDict[@"fontFamily"]; + BOOL fontFamilyFound = NO; + NSArray *supportedFontFamilyNames = [UIFont familyNames]; + + if ([supportedFontFamilyNames containsObject:fontFamily]) { + fontFamilyFound = YES; + } else { + for (NSString *fontFamilyName in supportedFontFamilyNames) { + if ([[UIFont fontNamesForFamilyName: fontFamilyName] containsObject:fontFamily]) { + fontFamilyFound = YES; + break; + } + } + } + fontFamily = fontFamilyFound ? fontFamily : nil; + + return (__bridge CTFontRef)[RCTFont updateFont:nil withFamily:fontFamily size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] variant:nil scaleMultiplier:1.0]; +} + +- (RNSVGGlyphPoint)getComputedGlyphPoint:(NSUInteger *)index glyphOffset:(CGPoint)glyphOffset +{ + RNSVGGlyphPoint __block point; + point.isDeltaXSet = point.isDeltaYSet = point.isPositionXSet = point.isPositionYSet = NO; + + [self traverseTextSuperviews:^(RNSVGText *node) { + NSUInteger index = node.lastIndex; + + if (!point.isPositionXSet && node.positionX && !index) { + point.positionX = [self getWidthRelatedValue:node.positionX]; + point.isPositionXSet = YES; + } + + if (!point.isDeltaXSet && node.deltaX.count > index) { + point.deltaX = [[node.deltaX objectAtIndex:index] floatValue]; + point.isDeltaXSet = YES; + } + + if (!point.isPositionYSet && node.positionY && !index) { + point.positionY = [self getHeightRelatedValue:node.positionY]; + point.isPositionYSet = YES; + } + + if (!point.isDeltaYSet && node.deltaY.count > index) { + point.deltaY = [[node.deltaY objectAtIndex:index] floatValue]; + point.isDeltaYSet = YES; + } + + node.lastIndex++; + return YES; + }]; + + CGPoint lineOffset = [self getGlyphLineOffset]; + CGFloat lastX = lineOffset.x; + CGFloat lastY = lineOffset.y; + + if (point.isPositionXSet) { + lastX = point.positionX; + } + + if (point.isPositionYSet) { + lastY = point.positionY; + } + + if (point.isDeltaXSet) { + lastX += point.deltaX; + } + + if (point.isDeltaYSet) { + lastY += point.deltaY; + } + + [self getTextRoot].lastX = lastX; + [self getTextRoot].lastY = lastY; + + point.x = lastX + glyphOffset.x; + point.y = lastY + glyphOffset.y; + + return point; +} + +- (void)traverseTextSuperviews:(BOOL (^)(__kindof RNSVGText *node))block +{ + RNSVGText *targetView = self; + block(targetView); + while (targetView && [targetView class] != [RNSVGText class]) { + if (![targetView isKindOfClass:[RNSVGText class]]) { + //todo: throw exception here + break; + } + + targetView = [targetView superview]; + block(targetView); + } +} + +- (RNSVGText *)getTextRoot +{ + if (!_textRoot) { + _textRoot = self; + while (_textRoot && [_textRoot class] != [RNSVGText class]) { + if (![_textRoot isKindOfClass:[RNSVGText class]]) { + //todo: throw exception here + break; + } + _textRoot = [_textRoot superview]; + } + } + + return _textRoot; +} + +- (CGPoint)getGlyphLineOffset +{ + RNSVGText *text = [self getTextRoot]; + return CGPointMake(text.lastX, text.lastY); +} + +// reset Text path related attributes +- (void)resetTextPathAttributes +{ + self.lastIndex = 0; + self.lastX = self.lastY = 0; +} @end diff --git a/ios/Utils/RCTConvert+RNSVG.h b/ios/Utils/RCTConvert+RNSVG.h index b3510344..959c1e5e 100644 --- a/ios/Utils/RCTConvert+RNSVG.h +++ b/ios/Utils/RCTConvert+RNSVG.h @@ -23,7 +23,6 @@ + (RNSVGTextAnchor)RNSVGTextAnchor:(id)json; + (RNSVGCGFCRule)RNSVGCGFCRule:(id)json; + (RNSVGVBMOS)RNSVGVBMOS:(id)json; -+ (CTFontRef)RNSVGFont:(id)json; + (RNSVGCGFloatArray)RNSVGCGFloatArray:(id)json; + (RNSVGBrush *)RNSVGBrush:(id)json; diff --git a/ios/Utils/RCTConvert+RNSVG.m b/ios/Utils/RCTConvert+RNSVG.m index 2551861e..397919c0 100644 --- a/ios/Utils/RCTConvert+RNSVG.m +++ b/ios/Utils/RCTConvert+RNSVG.m @@ -12,7 +12,6 @@ #import "RNSVGPattern.h" #import "RNSVGSolidColorBrush.h" #import "RCTLog.h" -#import "RCTFont.h" @implementation RCTConvert (RNSVG) @@ -81,41 +80,6 @@ RCT_ENUM_CONVERTER(RNSVGTextAnchor, (@{ @"end": @(kRNSVGTextAnchorEnd) }), kRNSVGTextAnchorAuto, intValue) -+ (CTFontRef)RNSVGFont:(id)json -{ - NSDictionary *dict = [self NSDictionary:json]; - - NSDictionary *fontDict = dict[@"font"]; - NSString *fontFamily = fontDict[@"fontFamily"]; - - BOOL fontFound = NO; - NSArray *supportedFontFamilyNames = [UIFont familyNames]; - - if ([supportedFontFamilyNames containsObject:fontFamily]) { - fontFound = YES; - } else { - for (NSString *fontFamilyName in supportedFontFamilyNames) { - if ([[UIFont fontNamesForFamilyName: fontFamilyName] containsObject:fontFamily]) { - fontFound = YES; - break; - } - } - } - - fontFamily = fontFound ? fontFamily : nil; - - CTFontRef font = (__bridge CTFontRef)[RCTFont updateFont:nil withFamily:fontFamily size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] - variant:nil scaleMultiplier:1.0]; - if (!font) { - return frame; - - } - - fontFamily = fontFound ? fontFamily : nil; - - return (__bridge CTFontRef)[RCTFont updateFont:nil withFamily:fontFamily size:dict[@"fontSize"] weight:dict[@"fontWeight"] style:dict[@"fontStyle"] variant:nil scaleMultiplier:1.0]; -} - + (RNSVGCGFloatArray)RNSVGCGFloatArray:(id)json { NSArray *arr = [self NSNumberArray:json]; diff --git a/ios/Utils/RNSVGGlyphPoint.h b/ios/Utils/RNSVGGlyphPoint.h new file mode 100644 index 00000000..a752bcc9 --- /dev/null +++ b/ios/Utils/RNSVGGlyphPoint.h @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-present, Horcrux. + * All rights reserved. + * + * This source code is licensed under the MIT-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +typedef struct { + float x; + float deltaX; + BOOL isDeltaXSet; + float positionX; + BOOL isPositionXSet; + + float y; + float deltaY; + BOOL isDeltaYSet; + float positionY; + BOOL isPositionYSet; +} RNSVGGlyphPoint; + + diff --git a/ios/ViewManagers/RNSVGTextManager.m b/ios/ViewManagers/RNSVGTextManager.m index 3cd1befc..edbf7a21 100644 --- a/ios/ViewManagers/RNSVGTextManager.m +++ b/ios/ViewManagers/RNSVGTextManager.m @@ -23,8 +23,8 @@ RCT_EXPORT_MODULE() RCT_EXPORT_VIEW_PROPERTY(textAnchor, RNSVGTextAnchor) RCT_EXPORT_VIEW_PROPERTY(deltaX, NSArray) RCT_EXPORT_VIEW_PROPERTY(deltaY, NSArray) -RCT_EXPORT_VIEW_PROPERTY(px, NSString) -RCT_EXPORT_VIEW_PROPERTY(py, NSString) +RCT_EXPORT_VIEW_PROPERTY(positionX, NSString) +RCT_EXPORT_VIEW_PROPERTY(positionY, NSString) RCT_EXPORT_VIEW_PROPERTY(font, NSDictionary) @end diff --git a/lib/extract/extractText.js b/lib/extract/extractText.js index 159f6cd1..4f0d356e 100644 --- a/lib/extract/extractText.js +++ b/lib/extract/extractText.js @@ -118,7 +118,7 @@ export default function(props, isText) { content, deltaX, deltaY, - positionX: x || null, - positionY: y || null + positionX: _.isNil(x) ? null : x, + positionY: _.isNil(y) ? null : y } } diff --git a/package.json b/package.json index 095b389a..c27e7e6e 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "color": "^0.11.1", - "lodash": "^4.0.0" + "lodash": "^4.16.6" }, "devDependencies": { "babel-eslint": "^6.1.2",