Refactor Text related elements

This commit is contained in:
Horcrux
2017-01-11 17:02:40 +08:00
parent 0bb87a6eb3
commit 1381cd8fca
10 changed files with 347 additions and 247 deletions
+6 -2
View File
@@ -54,6 +54,7 @@
7F08CEA01E23479700650F83 /* RNSVGTextPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */; }; 7F08CEA01E23479700650F83 /* RNSVGTextPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */; };
7F08CEA11E23479700650F83 /* RNSVGTSpan.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE9F1E23479700650F83 /* RNSVGTSpan.m */; }; 7F08CEA11E23479700650F83 /* RNSVGTSpan.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE9F1E23479700650F83 /* RNSVGTSpan.m */; };
7F9CDAFA1E1F809C00E0C805 /* RNSVGPathParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.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 */ /* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase 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 = "<group>"; }; 7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGTextPath.m; path = Text/RNSVGTextPath.m; sourceTree = "<group>"; };
7F08CE9E1E23479700650F83 /* RNSVGTSpan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTSpan.h; path = Text/RNSVGTSpan.h; sourceTree = "<group>"; }; 7F08CE9E1E23479700650F83 /* RNSVGTSpan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTSpan.h; path = Text/RNSVGTSpan.h; sourceTree = "<group>"; };
7F08CE9F1E23479700650F83 /* RNSVGTSpan.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGTSpan.m; path = Text/RNSVGTSpan.m; sourceTree = "<group>"; }; 7F08CE9F1E23479700650F83 /* RNSVGTSpan.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGTSpan.m; path = Text/RNSVGTSpan.m; sourceTree = "<group>"; };
7F08CEA21E23481F00650F83 /* RNSVGGlyphPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGGlyphPoint.h; path = Utils/RNSVGGlyphPoint.h; sourceTree = "<group>"; };
7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextAnchor.h; path = Utils/RNSVGTextAnchor.h; sourceTree = "<group>"; }; 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextAnchor.h; path = Utils/RNSVGTextAnchor.h; sourceTree = "<group>"; };
7F9CDAF81E1F809C00E0C805 /* RNSVGPathParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGPathParser.h; path = Utils/RNSVGPathParser.h; sourceTree = "<group>"; }; 7F9CDAF81E1F809C00E0C805 /* RNSVGPathParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGPathParser.h; path = Utils/RNSVGPathParser.h; sourceTree = "<group>"; };
7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPathParser.m; path = Utils/RNSVGPathParser.m; sourceTree = "<group>"; }; 7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPathParser.m; path = Utils/RNSVGPathParser.m; sourceTree = "<group>"; };
7FFC4EA21E24E52500AD5BE5 /* RNSVGGlyphContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSVGGlyphContext.h; path = Text/RNSVGGlyphContext.h; sourceTree = "<group>"; };
7FFC4EA31E24E5AD00AD5BE5 /* RNSVGGlyphContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGGlyphContext.m; path = Text/RNSVGGlyphContext.m; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -293,6 +295,8 @@
1039D27F1CE71D9B001E90A8 /* Text */ = { 1039D27F1CE71D9B001E90A8 /* Text */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7FFC4EA21E24E52500AD5BE5 /* RNSVGGlyphContext.h */,
7FFC4EA31E24E5AD00AD5BE5 /* RNSVGGlyphContext.m */,
7F08CE9C1E23479700650F83 /* RNSVGTextPath.h */, 7F08CE9C1E23479700650F83 /* RNSVGTextPath.h */,
7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */, 7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */,
7F08CE9E1E23479700650F83 /* RNSVGTSpan.h */, 7F08CE9E1E23479700650F83 /* RNSVGTSpan.h */,
@@ -333,7 +337,6 @@
1039D29A1CE7212C001E90A8 /* Utils */ = { 1039D29A1CE7212C001E90A8 /* Utils */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7F08CEA21E23481F00650F83 /* RNSVGGlyphPoint.h */,
7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */, 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */,
1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */, 1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */,
10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */, 10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */,
@@ -410,6 +413,7 @@
10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */, 10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */,
10BEC1BC1D3F66F500FDCB19 /* RNSVGLinearGradient.m in Sources */, 10BEC1BC1D3F66F500FDCB19 /* RNSVGLinearGradient.m in Sources */,
1039D2B01CE72F27001E90A8 /* RNSVGPercentageConverter.m in Sources */, 1039D2B01CE72F27001E90A8 /* RNSVGPercentageConverter.m in Sources */,
7FFC4EA41E24E5AD00AD5BE5 /* RNSVGGlyphContext.m in Sources */,
10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */, 10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */,
10ABC7331D435915006CCF6E /* RNSVGViewBoxManager.m in Sources */, 10ABC7331D435915006CCF6E /* RNSVGViewBoxManager.m in Sources */,
1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */, 1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */,
+3
View File
@@ -12,6 +12,7 @@
#import "RNSVGCGFloatArray.h" #import "RNSVGCGFloatArray.h"
#import "RNSVGCGFCRule.h" #import "RNSVGCGFCRule.h"
#import "RNSVGNode.h" #import "RNSVGNode.h"
#import "RNSVGPercentageConverter.h"
@interface RNSVGRenderable : RNSVGNode @interface RNSVGRenderable : RNSVGNode
@@ -36,5 +37,7 @@
- (CGRect)getLayoutBoundingBox; - (CGRect)getLayoutBoundingBox;
- (CGFloat)getWidthRelatedValue:(NSString *)string; - (CGFloat)getWidthRelatedValue:(NSString *)string;
- (CGFloat)getHeightRelatedValue:(NSString *)string; - (CGFloat)getHeightRelatedValue:(NSString *)string;
- (RNSVGPercentageConverter *)getWidthConverter;
- (RNSVGPercentageConverter *)getHeightConverter;
@end @end
+10 -1
View File
@@ -7,7 +7,6 @@
*/ */
#import "RNSVGRenderable.h" #import "RNSVGRenderable.h"
#import "RNSVGPercentageConverter.h"
@implementation RNSVGRenderable @implementation RNSVGRenderable
{ {
@@ -308,6 +307,16 @@
offset:0]; offset:0];
} }
- (RNSVGPercentageConverter *)getWidthConverter
{
return _widthConverter;
}
- (RNSVGPercentageConverter *)getHeightConverter
{
return _heightConverter;
}
- (CGRect)getContextBoundingBox - (CGRect)getContextBoundingBox
{ {
return _contextBoundingBox; return _contextBoundingBox;
+21
View File
@@ -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 <React/UIView+React.h>
#import <CoreText/CoreText.h>
#import "RNSVGPercentageConverter.h"
@interface RNSVGGlyphContext : NSObject
- (instancetype)initWithConverters:(RNSVGPercentageConverter *)widthConverter heightConverter:(RNSVGPercentageConverter *)heightConverter;
- (void)pushContext:(NSDictionary *)font deltaX:(NSArray<NSNumber *> *)deltaX deltaY:(NSArray<NSNumber *> *)deltaY positionX:(NSString *)positionX positionY:(NSString *)positionY;
- (void)popContext;
- (CTFontRef)getGlyphFont;
- (CGPoint)getNextGlyphPoint:(CGPoint)offset glyphWidth:(CGFloat)glyphWidth;
@end
+170
View File
@@ -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 <React/RCTFont.h>
@implementation RNSVGGlyphContext
{
NSMutableArray<NSDictionary* > *_fontContext;
NSMutableArray<NSValue* > *_locationContext;
NSMutableArray<NSArray *> *_deltaXContext;
NSMutableArray<NSArray *> *_deltaYContext;
NSMutableArray<NSNumber *> *_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<NSNumber *> *)deltaX deltaY:(NSArray<NSNumber *> *)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
+57 -39
View File
@@ -15,6 +15,7 @@
@implementation RNSVGTSpan @implementation RNSVGTSpan
{ {
RNSVGBezierTransformer *_bezierTransformer; RNSVGBezierTransformer *_bezierTransformer;
CGPathRef _cache;
} }
- (void)renderLayerTo:(CGContextRef)context - (void)renderLayerTo:(CGContextRef)context
@@ -27,31 +28,39 @@
} }
} }
- (void)releaseCachedPath
{
CGPathRelease(_cache);
_cache = nil;
}
- (CGPathRef)getPath:(CGContextRef)context - (CGPathRef)getPath:(CGContextRef)context
{ {
if (!self.content) { if (_cache) {
return [self getGroupPath:context]; return _cache;
} }
[self setContextBoundingBox:CGContextGetClipBoundingBox(context)];
NSString *text = self.content;
if (!text) {
return [super getPath:context];
}
[self initialTextPath]; [self initialTextPath];
[self setContextBoundingBox:CGContextGetClipBoundingBox(context)];
CGMutablePathRef path = CGPathCreateMutable(); CGMutablePathRef path = CGPathCreateMutable();
if ([self.content isEqualToString:@""]) { // append spacing
RNSVGGlyphPoint computedPoint = [self getComputedGlyphPoint:0 glyphOffset:CGPointZero]; text = [text stringByAppendingString:@" "];
[self getTextRoot].lastX = computedPoint.x;
[self getTextRoot].lastY = computedPoint.y; CTFontRef font = [self getFontFromContext];
return path;
}
CTFontRef font = [self getComputedFont];
// Create a dictionary for this font // Create a dictionary for this font
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
(NSString *)kCTFontAttributeName: (__bridge id)font, (NSString *)kCTFontAttributeName: (__bridge id)font,
(NSString *)kCTForegroundColorFromContextAttributeName: @YES (NSString *)kCTForegroundColorFromContextAttributeName: @YES
}; };
CFStringRef string = (__bridge CFStringRef)self.content; CFStringRef string = (__bridge CFStringRef)text;
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
CTLineRef line = CTLineCreateWithAttributedString(attrString); CTLineRef line = CTLineCreateWithAttributedString(attrString);
@@ -63,17 +72,16 @@
CFRelease(attrString); CFRelease(attrString);
CFRelease(line); CFRelease(line);
CGPathRelease(linePath); CGPathRelease(linePath);
[self resetTextPathAttributes];
_cache = CGPathRetain(CGPathCreateCopy(path));
return (CGPathRef)CFAutorelease(path); return (CGPathRef)CFAutorelease(path);
} }
- (CGMutablePathRef)getLinePath:(CTLineRef)line - (CGMutablePathRef)getLinePath:(CTLineRef)line
{ {
CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0); [self pushGlyphContext];
CGMutablePathRef path = CGPathCreateMutable(); CTRunRef run = CFArrayGetValueAtIndex(CTLineGetGlyphRuns(line), 0);
CFArrayRef glyphRuns = CTLineGetGlyphRuns(line);
CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, 0);
CFIndex runGlyphCount = CTRunGetGlyphCount(run); CFIndex runGlyphCount = CTRunGetGlyphCount(run);
CGPoint positions[runGlyphCount]; CGPoint positions[runGlyphCount];
@@ -82,22 +90,17 @@
// Grab the glyphs, positions, and font // Grab the glyphs, positions, and font
CTRunGetPositions(run, CFRangeMake(0, 0), positions); CTRunGetPositions(run, CFRangeMake(0, 0), positions);
CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs); CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs);
CFDictionaryRef attributes = CTRunGetAttributes(run); CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);
CTFontRef runFont = CFDictionaryGetValue(attributes, kCTFontAttributeName); CGPoint glyphPoint;
CGMutablePathRef path = CGPathCreateMutable();
CGFloat lineStartX;
RNSVGGlyphPoint computedPoint;
for(CFIndex i = 0; i < runGlyphCount; i++) { 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); 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]) { if ([RNSVGBezierTransformer hasReachedEnd:textPathTransform]) {
break; break;
@@ -108,18 +111,17 @@
CGAffineTransform transform; CGAffineTransform transform;
if (_bezierTransformer) { if (_bezierTransformer) {
textPathTransform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(0, computedPoint.y), textPathTransform); textPathTransform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(0, glyphPoint.y), textPathTransform);
transform = CGAffineTransformScale(textPathTransform, 1.0, -1.0); transform = CGAffineTransformScale(textPathTransform, 1.0, -1.0);
} else { } else {
transform = CGAffineTransformTranslate(upsideDown, computedPoint.x, -computedPoint.y); transform = CGAffineTransformTranslate(CGAffineTransformMakeScale(1.0, -1.0), glyphPoint.x, -glyphPoint.y);
} }
CGPathAddPath(path, &transform, letter); CGPathAddPath(path, &transform, letter);
CGPathRelease(letter); CGPathRelease(letter);
} }
[self getTextRoot].lastX = computedPoint.x; [self popGlyphContext];
return path; return path;
} }
@@ -138,6 +140,22 @@
_bezierTransformer = bezierTransformer; _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 - (CGAffineTransform)getTextPathTransform:(CGFloat)distance
{ {
if (_bezierTransformer) { if (_bezierTransformer) {
+8 -6
View File
@@ -7,9 +7,9 @@
*/ */
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "RNSVGGlyphPoint.h"
#import "RNSVGGroup.h" #import "RNSVGGroup.h"
#import "RNSVGTextAnchor.h" #import "RNSVGTextAnchor.h"
#import "RNSVGGlyphContext.h"
@interface RNSVGText : RNSVGGroup @interface RNSVGText : RNSVGGroup
@@ -24,11 +24,13 @@
@property (nonatomic, assign) CGFloat lastY; @property (nonatomic, assign) CGFloat lastY;
@property (nonatomic, assign) NSUInteger lastIndex; @property (nonatomic, assign) NSUInteger lastIndex;
- (CTFontRef)getComputedFont;
- (RNSVGGlyphPoint)getComputedGlyphPoint:(NSUInteger *)index glyphOffset:(CGPoint)glyphOffset;
- (RNSVGText *)getTextRoot; - (RNSVGText *)getTextRoot;
- (CGPathRef)getGroupPath:(CGContextRef)context; - (void)releaseCachedPath;
- (void)resetTextPathAttributes;
- (void)traverseTextSuperviews:(BOOL (^)(__kindof RNSVGText *node))block; - (RNSVGGlyphContext *)getGlyphContext;
- (void)pushGlyphContext;
- (void)popGlyphContext;
- (CTFontRef)getFontFromContext;
- (CGPoint)getGlyphPointFromContext:(CGPoint)offset glyphWidth:(CGFloat)glyphWidth;
@end @end
+72 -171
View File
@@ -10,10 +10,12 @@
#import "RNSVGTextPath.h" #import "RNSVGTextPath.h"
#import <React/RCTFont.h> #import <React/RCTFont.h>
#import <CoreText/CoreText.h> #import <CoreText/CoreText.h>
#import "RNSVGGlyphContext.h"
@implementation RNSVGText @implementation RNSVGText
{ {
RNSVGText *_textRoot; RNSVGText *_textRoot;
RNSVGGlyphContext *_glyphContext;
} }
- (void)setTextAnchor:(RNSVGTextAnchor)textAnchor - (void)setTextAnchor:(RNSVGTextAnchor)textAnchor
@@ -24,206 +26,83 @@
- (void)renderLayerTo:(CGContextRef)context - (void)renderLayerTo:(CGContextRef)context
{ {
[self setContextBoundingBox:CGContextGetClipBoundingBox(context)];
_glyphContext = [[RNSVGGlyphContext alloc] initWithConverters:[self getWidthConverter]
heightConverter:[self getHeightConverter]];
[self clip:context]; [self clip:context];
CGContextSaveGState(context); CGContextSaveGState(context);
CGAffineTransform transform = CGAffineTransformMakeTranslation([self getShift:context path:nil], 0); CGAffineTransform transform = [self getAlignTransform:context];
CGContextConcatCTM(context, transform); CGContextConcatCTM(context, transform);
[self renderGroupTo:context]; [self renderGroupTo:context];
[self resetTextPathAttributes]; [self releaseCachedPath];
CGContextRestoreGState(context); CGContextRestoreGState(context);
} }
- (void)releaseCachedPath
{
[self traverseSubviews:^BOOL(__kindof RNSVGNode *node) {
RNSVGText *text = node;
[text releaseCachedPath];
return YES;
}];
}
- (CGPathRef)getPath:(CGContextRef)context - (CGPathRef)getPath:(CGContextRef)context
{ {
CGPathRef path = [self getGroupPath:context]; [self pushGlyphContext];
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
{
CGPathRef groupPath = [super getPath:context]; CGPathRef groupPath = [super getPath:context];
[self resetTextPathAttributes]; [self popGlyphContext];
return groupPath;
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 width = CGRectGetWidth(CGPathGetBoundingBox(path));
CGFloat x = 0;
switch ([self getComputedTextAnchor]) { switch ([self getComputedTextAnchor]) {
case kRNSVGTextAnchorMiddle: case kRNSVGTextAnchorMiddle:
return -width / 2; x = -width / 2;
break;
case kRNSVGTextAnchorEnd: case kRNSVGTextAnchorEnd:
return -width; x = -width;
default: break;
return 0;
} }
return CGAffineTransformMakeTranslation(x, 0);
} }
- (RNSVGTextAnchor)getComputedTextAnchor - (RNSVGTextAnchor)getComputedTextAnchor
{ {
RNSVGTextAnchor anchor = self.textAnchor; RNSVGTextAnchor anchor = self.textAnchor;
if (self.subviews.count > 0) { if (self.subviews.count > 0) {
RNSVGText *child = [self.subviews objectAtIndex:0]; RNSVGText *child = [self.subviews firstObject];
while (child.subviews.count && anchor == kRNSVGTextAnchorAuto) { while (child.subviews.count && anchor == kRNSVGTextAnchorAuto) {
anchor = child.textAnchor; anchor = child.textAnchor;
child = [child.subviews objectAtIndex:0]; child = [child.subviews firstObject];
} }
} }
return anchor; 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 - (RNSVGText *)getTextRoot
{ {
if (!_textRoot) { if (!_textRoot) {
@@ -240,17 +119,39 @@
return _textRoot; 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 - (CGPoint)getGlyphLineOffset
{ {
RNSVGText *text = [self getTextRoot]; RNSVGText *text = [self getTextRoot];
return CGPointMake(text.lastX, text.lastY); return CGPointMake(text.lastX, text.lastY);
} }
// reset Text path related attributes - (CTFontRef)getFontFromContext
- (void)resetTextPathAttributes
{ {
self.lastIndex = 0; return [[[self getTextRoot] getGlyphContext] getGlyphFont];
self.lastX = self.lastY = 0; }
- (CGPoint)getGlyphPointFromContext:(CGPoint)offset glyphWidth:(CGFloat)glyphWidth
{
return [[[self getTextRoot] getGlyphContext] getNextGlyphPoint:(CGPoint)offset glyphWidth:glyphWidth];
} }
@end @end
-5
View File
@@ -18,11 +18,6 @@
[self renderGroupTo:context]; [self renderGroupTo:context];
} }
- (CGPathRef)getPath:(CGContextRef)context
{
return [self getGroupPath:context];
}
- (RNSVGBezierTransformer *)getBezierTransformer - (RNSVGBezierTransformer *)getBezierTransformer
{ {
RNSVGSvgView *svg = [self getSvgView]; RNSVGSvgView *svg = [self getSvgView];
-23
View File
@@ -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;