From 1381cd8fca3b38e08720c2befba559678e3842bb Mon Sep 17 00:00:00 2001 From: Horcrux Date: Wed, 11 Jan 2017 17:02:40 +0800 Subject: [PATCH] Refactor Text related elements --- ios/RNSVG.xcodeproj/project.pbxproj | 8 +- ios/RNSVGRenderable.h | 3 + ios/RNSVGRenderable.m | 11 +- ios/Text/RNSVGGlyphContext.h | 21 +++ ios/Text/RNSVGGlyphContext.m | 170 +++++++++++++++++++ ios/Text/RNSVGTSpan.m | 96 ++++++----- ios/Text/RNSVGText.h | 14 +- ios/Text/RNSVGText.m | 243 +++++++++------------------- ios/Text/RNSVGTextPath.m | 5 - ios/Utils/RNSVGGlyphPoint.h | 23 --- 10 files changed, 347 insertions(+), 247 deletions(-) create mode 100644 ios/Text/RNSVGGlyphContext.h create mode 100644 ios/Text/RNSVGGlyphContext.m delete mode 100644 ios/Utils/RNSVGGlyphPoint.h diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj index 1895cbb5..bc02b45c 100644 --- a/ios/RNSVG.xcodeproj/project.pbxproj +++ b/ios/RNSVG.xcodeproj/project.pbxproj @@ -54,6 +54,7 @@ 7F08CEA01E23479700650F83 /* RNSVGTextPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */; }; 7F08CEA11E23479700650F83 /* RNSVGTSpan.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE9F1E23479700650F83 /* RNSVGTSpan.m */; }; 7F9CDAFA1E1F809C00E0C805 /* RNSVGPathParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */; }; + 7FFC4EA41E24E5AD00AD5BE5 /* RNSVGGlyphContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FFC4EA31E24E5AD00AD5BE5 /* RNSVGGlyphContext.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -167,10 +168,11 @@ 7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGTextPath.m; path = Text/RNSVGTextPath.m; sourceTree = ""; }; 7F08CE9E1E23479700650F83 /* RNSVGTSpan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTSpan.h; path = Text/RNSVGTSpan.h; sourceTree = ""; }; 7F08CE9F1E23479700650F83 /* RNSVGTSpan.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGTSpan.m; path = Text/RNSVGTSpan.m; sourceTree = ""; }; - 7F08CEA21E23481F00650F83 /* RNSVGGlyphPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGGlyphPoint.h; path = Utils/RNSVGGlyphPoint.h; sourceTree = ""; }; 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextAnchor.h; path = Utils/RNSVGTextAnchor.h; sourceTree = ""; }; 7F9CDAF81E1F809C00E0C805 /* RNSVGPathParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGPathParser.h; path = Utils/RNSVGPathParser.h; sourceTree = ""; }; 7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPathParser.m; path = Utils/RNSVGPathParser.m; sourceTree = ""; }; + 7FFC4EA21E24E52500AD5BE5 /* RNSVGGlyphContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSVGGlyphContext.h; path = Text/RNSVGGlyphContext.h; sourceTree = ""; }; + 7FFC4EA31E24E5AD00AD5BE5 /* RNSVGGlyphContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGGlyphContext.m; path = Text/RNSVGGlyphContext.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -293,6 +295,8 @@ 1039D27F1CE71D9B001E90A8 /* Text */ = { isa = PBXGroup; children = ( + 7FFC4EA21E24E52500AD5BE5 /* RNSVGGlyphContext.h */, + 7FFC4EA31E24E5AD00AD5BE5 /* RNSVGGlyphContext.m */, 7F08CE9C1E23479700650F83 /* RNSVGTextPath.h */, 7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */, 7F08CE9E1E23479700650F83 /* RNSVGTSpan.h */, @@ -333,7 +337,6 @@ 1039D29A1CE7212C001E90A8 /* Utils */ = { isa = PBXGroup; children = ( - 7F08CEA21E23481F00650F83 /* RNSVGGlyphPoint.h */, 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */, 1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */, 10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */, @@ -410,6 +413,7 @@ 10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */, 10BEC1BC1D3F66F500FDCB19 /* RNSVGLinearGradient.m in Sources */, 1039D2B01CE72F27001E90A8 /* RNSVGPercentageConverter.m in Sources */, + 7FFC4EA41E24E5AD00AD5BE5 /* RNSVGGlyphContext.m in Sources */, 10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */, 10ABC7331D435915006CCF6E /* RNSVGViewBoxManager.m in Sources */, 1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */, diff --git a/ios/RNSVGRenderable.h b/ios/RNSVGRenderable.h index 6b40fad5..0d77e393 100644 --- a/ios/RNSVGRenderable.h +++ b/ios/RNSVGRenderable.h @@ -12,6 +12,7 @@ #import "RNSVGCGFloatArray.h" #import "RNSVGCGFCRule.h" #import "RNSVGNode.h" +#import "RNSVGPercentageConverter.h" @interface RNSVGRenderable : RNSVGNode @@ -36,5 +37,7 @@ - (CGRect)getLayoutBoundingBox; - (CGFloat)getWidthRelatedValue:(NSString *)string; - (CGFloat)getHeightRelatedValue:(NSString *)string; +- (RNSVGPercentageConverter *)getWidthConverter; +- (RNSVGPercentageConverter *)getHeightConverter; @end diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index 82a41e2d..20f5fb67 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -7,7 +7,6 @@ */ #import "RNSVGRenderable.h" -#import "RNSVGPercentageConverter.h" @implementation RNSVGRenderable { @@ -308,6 +307,16 @@ offset:0]; } +- (RNSVGPercentageConverter *)getWidthConverter +{ + return _widthConverter; +} + +- (RNSVGPercentageConverter *)getHeightConverter +{ + return _heightConverter; +} + - (CGRect)getContextBoundingBox { return _contextBoundingBox; diff --git a/ios/Text/RNSVGGlyphContext.h b/ios/Text/RNSVGGlyphContext.h new file mode 100644 index 00000000..9a5340dc --- /dev/null +++ b/ios/Text/RNSVGGlyphContext.h @@ -0,0 +1,21 @@ +/** + * 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. + */ + +#import +#import +#import "RNSVGPercentageConverter.h" + +@interface RNSVGGlyphContext : NSObject + +- (instancetype)initWithConverters:(RNSVGPercentageConverter *)widthConverter heightConverter:(RNSVGPercentageConverter *)heightConverter; +- (void)pushContext:(NSDictionary *)font deltaX:(NSArray *)deltaX deltaY:(NSArray *)deltaY positionX:(NSString *)positionX positionY:(NSString *)positionY; +- (void)popContext; +- (CTFontRef)getGlyphFont; +- (CGPoint)getNextGlyphPoint:(CGPoint)offset glyphWidth:(CGFloat)glyphWidth; + +@end diff --git a/ios/Text/RNSVGGlyphContext.m b/ios/Text/RNSVGGlyphContext.m new file mode 100644 index 00000000..8766d385 --- /dev/null +++ b/ios/Text/RNSVGGlyphContext.m @@ -0,0 +1,170 @@ +/** + * 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. + */ + + +#import "RNSVGGlyphContext.h" +#import + +@implementation RNSVGGlyphContext +{ + NSMutableArray *_fontContext; + NSMutableArray *_locationContext; + NSMutableArray *_deltaXContext; + NSMutableArray *_deltaYContext; + NSMutableArray *_xContext; + CGPoint _currentLocation; + RNSVGPercentageConverter *_widthConverter; + RNSVGPercentageConverter *_heightConverter; +} + +- (instancetype)initWithConverters:(RNSVGPercentageConverter *)widthConverter heightConverter:(RNSVGPercentageConverter *)heightConverter +{ + if (self = [super init]) { + _widthConverter = widthConverter; + _heightConverter = heightConverter; + _fontContext = [[NSMutableArray alloc] init]; + _locationContext = [[NSMutableArray alloc] init]; + _deltaXContext = [[NSMutableArray alloc] init]; + _deltaYContext = [[NSMutableArray alloc] init]; + _xContext = [[NSMutableArray alloc] init]; + _currentLocation = CGPointZero; + } + return self; +} + +- (void)pushContext:(NSDictionary *)font deltaX:(NSArray *)deltaX deltaY:(NSArray *)deltaY positionX:(NSString *)positionX positionY:(NSString *)positionY +{ + CGPoint location = _currentLocation; + + if (positionX) { + location.x = [_widthConverter stringToFloat:positionX]; + } + + if (positionY) { + location.y = [_heightConverter stringToFloat:positionY]; + } + + [_locationContext addObject:[NSValue valueWithCGPoint:location]]; + [_fontContext addObject:font ? font : @{}]; + [_deltaXContext addObject:deltaX ? deltaX : @[]]; + [_deltaYContext addObject:deltaY ? deltaY : @[]]; + [_xContext addObject:[NSNumber numberWithFloat:location.x]]; + _currentLocation = location; +} + +- (void)popContext +{ + NSNumber *x = [_xContext lastObject]; + [_fontContext removeLastObject]; + [_locationContext removeLastObject]; + [_deltaXContext removeLastObject]; + [_deltaYContext removeLastObject]; + + if (_locationContext.count) { + _currentLocation = [[_locationContext lastObject] CGPointValue]; + _currentLocation.x = [x floatValue]; + [_locationContext replaceObjectAtIndex:_locationContext.count - 1 + withObject:[NSValue valueWithCGPoint:_currentLocation]]; + } +} + +- (CGPoint)getNextGlyphPoint:(CGPoint)offset glyphWidth:(CGFloat)glyphWidth +{ + CGPoint currentLocation = _currentLocation; + NSNumber *dx = [self getNextDelta:_deltaXContext]; + currentLocation.x += [dx floatValue]; + + NSNumber *dy = [self getNextDelta:_deltaYContext]; + currentLocation.y += [dy floatValue]; + + for (NSUInteger i = 0; i < _locationContext.count; i++) { + CGPoint point = [[_locationContext objectAtIndex:i] CGPointValue]; + point.x += [dx floatValue]; + point.y += [dy floatValue]; + [_locationContext replaceObjectAtIndex:i withObject:[NSValue valueWithCGPoint:point]]; + } + + _currentLocation = currentLocation; + NSNumber *x = [NSNumber numberWithFloat:currentLocation.x + offset.x + glyphWidth]; + [_xContext replaceObjectAtIndex:_xContext.count - 1 withObject:x]; + return CGPointMake(currentLocation.x + offset.x, currentLocation.y + offset.y); +} + +- (NSNumber *)getNextDelta:(NSMutableArray *)deltaContext +{ + NSNumber *value; + NSUInteger index = deltaContext.count; + for (NSArray *delta in [deltaContext reverseObjectEnumerator]) { + index--; + if (value == nil) { + value = [delta firstObject]; + } + + if (delta.count) { + NSMutableArray *mutableDelta = [delta mutableCopy]; + [mutableDelta removeObjectAtIndex:0]; + [deltaContext replaceObjectAtIndex:index withObject:[mutableDelta copy]]; + } + } + + return value; +} + +- (CTFontRef)getGlyphFont +{ + NSString *fontFamily; + NSNumber *fontSize; + NSString *fontWeight; + NSString *fontStyle; + + for (NSDictionary *font in [_fontContext reverseObjectEnumerator]) { + if (!fontFamily) { + fontFamily = font[@"fontFamily"]; + } + + if (fontSize == nil) { + fontSize = font[@"fontSize"]; + } + + if (!fontWeight) { + fontWeight = font[@"fontWeight"]; + } + if (!fontStyle) { + fontStyle = font[@"fontStyle"]; + } + + if (fontFamily && fontSize && fontWeight && fontStyle) { + break; + } + } + + 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:fontSize + weight:fontWeight + style:fontStyle + variant:nil + scaleMultiplier:1.0]; +} + +@end diff --git a/ios/Text/RNSVGTSpan.m b/ios/Text/RNSVGTSpan.m index fcb510bc..471c22ac 100644 --- a/ios/Text/RNSVGTSpan.m +++ b/ios/Text/RNSVGTSpan.m @@ -15,6 +15,7 @@ @implementation RNSVGTSpan { RNSVGBezierTransformer *_bezierTransformer; + CGPathRef _cache; } - (void)renderLayerTo:(CGContextRef)context @@ -27,31 +28,39 @@ } } +- (void)releaseCachedPath +{ + CGPathRelease(_cache); + _cache = nil; +} + - (CGPathRef)getPath:(CGContextRef)context { - if (!self.content) { - return [self getGroupPath:context]; + if (_cache) { + return _cache; } - [self setContextBoundingBox:CGContextGetClipBoundingBox(context)]; - + + NSString *text = self.content; + if (!text) { + return [super getPath:context]; + } + [self initialTextPath]; + [self setContextBoundingBox:CGContextGetClipBoundingBox(context)]; + CGMutablePathRef path = CGPathCreateMutable(); - - 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]; + + // append spacing + text = [text stringByAppendingString:@" "]; + + CTFontRef font = [self getFontFromContext]; // Create a dictionary for this font CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ (NSString *)kCTFontAttributeName: (__bridge id)font, (NSString *)kCTForegroundColorFromContextAttributeName: @YES }; - CFStringRef string = (__bridge CFStringRef)self.content; + CFStringRef string = (__bridge CFStringRef)text; CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); CTLineRef line = CTLineCreateWithAttributedString(attrString); @@ -63,17 +72,16 @@ CFRelease(attrString); CFRelease(line); CGPathRelease(linePath); - [self resetTextPathAttributes]; + + _cache = CGPathRetain(CGPathCreateCopy(path)); + return (CGPathRef)CFAutorelease(path); } - (CGMutablePathRef)getLinePath:(CTLineRef)line { - CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0); - CGMutablePathRef path = CGPathCreateMutable(); - - CFArrayRef glyphRuns = CTLineGetGlyphRuns(line); - CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, 0); + [self pushGlyphContext]; + CTRunRef run = CFArrayGetValueAtIndex(CTLineGetGlyphRuns(line), 0); CFIndex runGlyphCount = CTRunGetGlyphCount(run); CGPoint positions[runGlyphCount]; @@ -82,22 +90,17 @@ // Grab the glyphs, positions, and font CTRunGetPositions(run, CFRangeMake(0, 0), positions); CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs); - CFDictionaryRef attributes = CTRunGetAttributes(run); + CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName); - CTFontRef runFont = CFDictionaryGetValue(attributes, kCTFontAttributeName); - - CGFloat lineStartX; - RNSVGGlyphPoint computedPoint; + CGPoint glyphPoint; + CGMutablePathRef path = CGPathCreateMutable(); + for(CFIndex i = 0; i < runGlyphCount; i++) { - computedPoint = [self getComputedGlyphPoint:i glyphOffset:positions[i]]; - - if (!i) { - lineStartX = computedPoint.x; - } - CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[i], nil); - - CGAffineTransform textPathTransform = [self getTextPathTransform:computedPoint.x]; + + glyphPoint = [self getGlyphPointFromContext:positions[i] glyphWidth:CGPathGetBoundingBox(letter).size.width]; + + CGAffineTransform textPathTransform = [self getTextPathTransform:glyphPoint.x]; if ([RNSVGBezierTransformer hasReachedEnd:textPathTransform]) { break; @@ -108,18 +111,17 @@ CGAffineTransform transform; if (_bezierTransformer) { - textPathTransform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(0, computedPoint.y), textPathTransform); + textPathTransform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(0, glyphPoint.y), textPathTransform); transform = CGAffineTransformScale(textPathTransform, 1.0, -1.0); } else { - transform = CGAffineTransformTranslate(upsideDown, computedPoint.x, -computedPoint.y); + transform = CGAffineTransformTranslate(CGAffineTransformMakeScale(1.0, -1.0), glyphPoint.x, -glyphPoint.y); } - + CGPathAddPath(path, &transform, letter); CGPathRelease(letter); } - - [self getTextRoot].lastX = computedPoint.x; - + + [self popGlyphContext]; return path; } @@ -138,6 +140,22 @@ _bezierTransformer = bezierTransformer; } +- (void)traverseTextSuperviews:(BOOL (^)(__kindof RNSVGText *node))block +{ + RNSVGText *targetView = self; + BOOL result = block(self); + + while (targetView && [targetView class] != [RNSVGText class] && result) { + if (![targetView isKindOfClass:[RNSVGText class]]) { + //todo: throw exception here + break; + } + + targetView = [targetView superview]; + result = block(targetView); + } +} + - (CGAffineTransform)getTextPathTransform:(CGFloat)distance { if (_bezierTransformer) { diff --git a/ios/Text/RNSVGText.h b/ios/Text/RNSVGText.h index 4f2c2c4e..fe775915 100644 --- a/ios/Text/RNSVGText.h +++ b/ios/Text/RNSVGText.h @@ -7,9 +7,9 @@ */ #import -#import "RNSVGGlyphPoint.h" #import "RNSVGGroup.h" #import "RNSVGTextAnchor.h" +#import "RNSVGGlyphContext.h" @interface RNSVGText : RNSVGGroup @@ -24,11 +24,13 @@ @property (nonatomic, assign) CGFloat lastY; @property (nonatomic, assign) NSUInteger lastIndex; -- (CTFontRef)getComputedFont; -- (RNSVGGlyphPoint)getComputedGlyphPoint:(NSUInteger *)index glyphOffset:(CGPoint)glyphOffset; - (RNSVGText *)getTextRoot; -- (CGPathRef)getGroupPath:(CGContextRef)context; -- (void)resetTextPathAttributes; -- (void)traverseTextSuperviews:(BOOL (^)(__kindof RNSVGText *node))block; +- (void)releaseCachedPath; + +- (RNSVGGlyphContext *)getGlyphContext; +- (void)pushGlyphContext; +- (void)popGlyphContext; +- (CTFontRef)getFontFromContext; +- (CGPoint)getGlyphPointFromContext:(CGPoint)offset glyphWidth:(CGFloat)glyphWidth; @end diff --git a/ios/Text/RNSVGText.m b/ios/Text/RNSVGText.m index e529dcf3..6e5bbddd 100644 --- a/ios/Text/RNSVGText.m +++ b/ios/Text/RNSVGText.m @@ -10,10 +10,12 @@ #import "RNSVGTextPath.h" #import #import +#import "RNSVGGlyphContext.h" @implementation RNSVGText { RNSVGText *_textRoot; + RNSVGGlyphContext *_glyphContext; } - (void)setTextAnchor:(RNSVGTextAnchor)textAnchor @@ -24,206 +26,83 @@ - (void)renderLayerTo:(CGContextRef)context { + [self setContextBoundingBox:CGContextGetClipBoundingBox(context)]; + _glyphContext = [[RNSVGGlyphContext alloc] initWithConverters:[self getWidthConverter] + heightConverter:[self getHeightConverter]]; + [self clip:context]; CGContextSaveGState(context); - CGAffineTransform transform = CGAffineTransformMakeTranslation([self getShift:context path:nil], 0); + CGAffineTransform transform = [self getAlignTransform:context]; CGContextConcatCTM(context, transform); [self renderGroupTo:context]; - [self resetTextPathAttributes]; + [self releaseCachedPath]; CGContextRestoreGState(context); } +- (void)releaseCachedPath +{ + [self traverseSubviews:^BOOL(__kindof RNSVGNode *node) { + RNSVGText *text = node; + [text releaseCachedPath]; + return YES; + }]; +} + - (CGPathRef)getPath:(CGContextRef)context { - CGPathRef path = [self getGroupPath:context]; - CGAffineTransform transform = CGAffineTransformMakeTranslation([self getShift:context path:path], 0); - CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &transform); - - // check memory leaks here - //CGPathRelease(path); - return (CGPathRef)CFAutorelease(transformedPath); -} - -- (CGPathRef)getGroupPath:(CGContextRef)context -{ + [self pushGlyphContext]; CGPathRef groupPath = [super getPath:context]; - [self resetTextPathAttributes]; - return groupPath; + [self popGlyphContext]; + + CGAffineTransform transform = [self getAlignTransform:context path:groupPath]; + CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(groupPath, &transform); + + return transformedPath; } -- (CGFloat)getShift:(CGContextRef)context path:(CGPathRef)path +- (void)renderGroupTo:(CGContextRef)context +{ + [self pushGlyphContext]; + [super renderGroupTo:context]; + [self popGlyphContext]; +} + +- (CGAffineTransform)getAlignTransform:(CGContextRef)context +{ + return [self getAlignTransform:context path:[self getPath:context]]; +} + +- (CGAffineTransform)getAlignTransform:(CGContextRef)context path:(CGPathRef)path { - if (path == nil) { - path = [self getGroupPath:context]; - } - CGFloat width = CGRectGetWidth(CGPathGetBoundingBox(path)); + CGFloat x = 0; switch ([self getComputedTextAnchor]) { case kRNSVGTextAnchorMiddle: - return -width / 2; + x = -width / 2; + break; case kRNSVGTextAnchorEnd: - return -width; - default: - return 0; + x = -width; + break; } + + return CGAffineTransformMakeTranslation(x, 0); } - (RNSVGTextAnchor)getComputedTextAnchor { RNSVGTextAnchor anchor = self.textAnchor; if (self.subviews.count > 0) { - RNSVGText *child = [self.subviews objectAtIndex:0]; + RNSVGText *child = [self.subviews firstObject]; while (child.subviews.count && anchor == kRNSVGTextAnchorAuto) { anchor = child.textAnchor; - child = [child.subviews objectAtIndex:0]; + child = [child.subviews firstObject]; } } 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:^(__kindof 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 -{ - __block RNSVGGlyphPoint point; - point.isDeltaXSet = point.isDeltaYSet = point.isPositionXSet = point.isPositionYSet = NO; - - [self traverseTextSuperviews:^(__kindof RNSVGText *node) { - if ([node class] != [RNSVGTextPath class]) { - 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(self); - - 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) { @@ -240,17 +119,39 @@ return _textRoot; } +- (RNSVGGlyphContext *)getGlyphContext +{ + return _glyphContext; +} + +- (void)pushGlyphContext +{ + [[[self getTextRoot] getGlyphContext] pushContext:self.font + deltaX:self.deltaX + deltaY:self.deltaY + positionX:self.positionX + positionY:self.positionY]; +} + +- (void)popGlyphContext +{ + [[[self getTextRoot] getGlyphContext] popContext]; +} + - (CGPoint)getGlyphLineOffset { RNSVGText *text = [self getTextRoot]; return CGPointMake(text.lastX, text.lastY); } -// reset Text path related attributes -- (void)resetTextPathAttributes +- (CTFontRef)getFontFromContext { - self.lastIndex = 0; - self.lastX = self.lastY = 0; + return [[[self getTextRoot] getGlyphContext] getGlyphFont]; +} + +- (CGPoint)getGlyphPointFromContext:(CGPoint)offset glyphWidth:(CGFloat)glyphWidth +{ + return [[[self getTextRoot] getGlyphContext] getNextGlyphPoint:(CGPoint)offset glyphWidth:glyphWidth]; } @end diff --git a/ios/Text/RNSVGTextPath.m b/ios/Text/RNSVGTextPath.m index f3421914..c97314db 100644 --- a/ios/Text/RNSVGTextPath.m +++ b/ios/Text/RNSVGTextPath.m @@ -18,11 +18,6 @@ [self renderGroupTo:context]; } -- (CGPathRef)getPath:(CGContextRef)context -{ - return [self getGroupPath:context]; -} - - (RNSVGBezierTransformer *)getBezierTransformer { RNSVGSvgView *svg = [self getSvgView]; diff --git a/ios/Utils/RNSVGGlyphPoint.h b/ios/Utils/RNSVGGlyphPoint.h deleted file mode 100644 index a752bcc9..00000000 --- a/ios/Utils/RNSVGGlyphPoint.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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; - -