diff --git a/Example/examples/Text.js b/Example/examples/Text.js
index f905c844..032192d8 100644
--- a/Example/examples/Text.js
+++ b/Example/examples/Text.js
@@ -9,7 +9,8 @@ import Svg, {
Defs,
Path,
G,
- TSpan
+ TSpan,
+ TextPath
} from 'react-native-svg';
class TextExample extends Component{
@@ -133,11 +134,21 @@ class TextPath extends Component{
height="60"
width="200"
>
+
+
+
We go up, then we go down, then up again
+ >
+
+ We go up, then we go down,
+ then up again
+
+
{
+ this.root.setNativeProps(...args);
};
render() {
- return null;
+ let props = this.props;
+ return {this.root = ele;}}
+ {...this.extractProps({
+ ...props,
+ x: null,
+ y: null
+ })}
+ {...extractText(props)}
+ />;
}
}
+const RNSVGTSpan = createReactNativeComponentClass({
+ validAttributes: TSpanAttibutes,
+ uiViewClassName: 'RNSVGTSpan'
+});
+
export default TSpan;
diff --git a/elements/Text.js b/elements/Text.js
index b8f894ea..95da276f 100644
--- a/elements/Text.js
+++ b/elements/Text.js
@@ -4,6 +4,7 @@ import extractText from '../lib/extract/extractText';
import {numberProp, pathProps, fontProps} from '../lib/props';
import {TextAttributes} from '../lib/attributes';
import Shape from './Shape';
+import TSpan from './TSpan';
class Text extends Shape {
static displayName = 'Text';
@@ -21,7 +22,7 @@ class Text extends Shape {
};
render() {
- let props = this.props;
+ const props = this.props;
return {this.root = ele;}}
@@ -30,7 +31,7 @@ class Text extends Shape {
x: null,
y: null
})}
- {...extractText(props)}
+ {...extractText(props, true)}
/>;
}
}
diff --git a/elements/TextPath.js b/elements/TextPath.js
new file mode 100644
index 00000000..d7476d69
--- /dev/null
+++ b/elements/TextPath.js
@@ -0,0 +1,52 @@
+import React, {PropTypes} from 'react';
+import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
+import {UseAttributes} from '../lib/attributes';
+import Shape from './Shape';
+import {pathProps, fontProps} from '../lib/props';
+
+const idExpReg = /^#(.+)$/;
+class TextPath extends Shape {
+ static displayName = 'Span';
+
+ static propTypes = {
+ ...pathProps,
+ ...fontProps,
+ href: PropTypes.string.isRequired,
+ textAnchor: PropTypes.oneOf(['start', 'middle', 'end'])
+ };
+
+ render() {
+ let {props} = this;
+ let matched = props.href.match(idExpReg);
+ let href;
+
+ if (matched) {
+ href = matched[1];
+ }
+
+ if (!href) {
+ console.warn('Invalid `href` prop for `TextPath` element, expected a href like `"#id"`, but got: "' + props.href + '"');
+ }
+
+ return ;
+ }
+}
+
+const RNSVGTextPath = createReactNativeComponentClass({
+ validAttributes: UseAttributes,
+ uiViewClassName: 'RNSVGTextPath'
+});
+
+export default TextPath;
diff --git a/index.js b/index.js
index 86fb4451..02107e05 100644
--- a/index.js
+++ b/index.js
@@ -17,6 +17,7 @@ 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,
@@ -37,7 +38,8 @@ export {
RadialGradient,
Stop,
ClipPath,
- TSpan
+ TSpan,
+ TextPath
};
export default Svg;
diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj
index e410f955..0f9f69d0 100644
--- a/ios/RNSVG.xcodeproj/project.pbxproj
+++ b/ios/RNSVG.xcodeproj/project.pbxproj
@@ -23,8 +23,8 @@
1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2901CE71EC2001E90A8 /* RNSVGText.m */; };
1039D2A01CE72177001E90A8 /* RCTConvert+RNSVG.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D29C1CE72177001E90A8 /* RCTConvert+RNSVG.m */; };
1039D2B01CE72F27001E90A8 /* RNSVGPercentageConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2AF1CE72F27001E90A8 /* RNSVGPercentageConverter.m */; };
- 107CD53E1D7166B700F0A7AC /* RNSVGSpanManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 107CD53D1D7166B700F0A7AC /* RNSVGSpanManager.m */; };
- 107CD5401D71672E00F0A7AC /* RNSVGSpan.m in Sources */ = {isa = PBXBuildFile; fileRef = 107CD53F1D71672E00F0A7AC /* RNSVGSpan.m */; };
+ 107CD53E1D7166B700F0A7AC /* RNSVGTSpanManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 107CD53D1D7166B700F0A7AC /* RNSVGTSpanManager.m */; };
+ 107CD5401D71672E00F0A7AC /* RNSVGTSpan.m in Sources */ = {isa = PBXBuildFile; fileRef = 107CD53F1D71672E00F0A7AC /* RNSVGTSpan.m */; };
10ABC7331D435915006CCF6E /* RNSVGViewBoxManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ABC7321D435915006CCF6E /* RNSVGViewBoxManager.m */; };
10ABC7361D43595E006CCF6E /* RNSVGViewBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ABC7351D43595E006CCF6E /* RNSVGViewBox.m */; };
10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D1D1CE74E3100887C2B /* RNSVGCircleManager.m */; };
@@ -101,10 +101,10 @@
1039D2A11CE721A7001E90A8 /* RNSVGContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGContainer.h; sourceTree = ""; };
1039D2AE1CE72F27001E90A8 /* RNSVGPercentageConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGPercentageConverter.h; path = Utils/RNSVGPercentageConverter.h; sourceTree = ""; };
1039D2AF1CE72F27001E90A8 /* RNSVGPercentageConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPercentageConverter.m; path = Utils/RNSVGPercentageConverter.m; sourceTree = ""; };
- 107CD53B1D7166A000F0A7AC /* RNSVGSpan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGSpan.h; path = Text/RNSVGSpan.h; sourceTree = ""; };
- 107CD53C1D7166B700F0A7AC /* RNSVGSpanManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSpanManager.h; sourceTree = ""; };
- 107CD53D1D7166B700F0A7AC /* RNSVGSpanManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSpanManager.m; sourceTree = ""; };
- 107CD53F1D71672E00F0A7AC /* RNSVGSpan.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGSpan.m; path = Text/RNSVGSpan.m; sourceTree = ""; };
+ 107CD53B1D7166A000F0A7AC /* RNSVGTSpan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTSpan.h; path = Text/RNSVGTSpan.h; sourceTree = ""; };
+ 107CD53C1D7166B700F0A7AC /* RNSVGTSpanManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGTSpanManager.h; sourceTree = ""; };
+ 107CD53D1D7166B700F0A7AC /* RNSVGTSpanManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGTSpanManager.m; sourceTree = ""; };
+ 107CD53F1D71672E00F0A7AC /* RNSVGTSpan.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGTSpan.m; path = Text/RNSVGTSpan.m; sourceTree = ""; };
10ABC7311D435915006CCF6E /* RNSVGViewBoxManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGViewBoxManager.h; sourceTree = ""; };
10ABC7321D435915006CCF6E /* RNSVGViewBoxManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGViewBoxManager.m; sourceTree = ""; };
10ABC7341D43595E006CCF6E /* RNSVGViewBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGViewBox.h; sourceTree = ""; };
@@ -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 = ""; };
+ 7FF070191DC249BE000E28A0 /* RNSVGTextAnchor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextAnchor.h; path = Utils/RNSVGTextAnchor.h; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -222,8 +223,6 @@
0CF68AF81AF0549300FF9E5C /* ViewManagers */ = {
isa = PBXGroup;
children = (
- 107CD53C1D7166B700F0A7AC /* RNSVGSpanManager.h */,
- 107CD53D1D7166B700F0A7AC /* RNSVGSpanManager.m */,
10ABC7311D435915006CCF6E /* RNSVGViewBoxManager.h */,
10ABC7321D435915006CCF6E /* RNSVGViewBoxManager.m */,
10BEC1BE1D3F680F00FDCB19 /* RNSVGLinearGradientManager.h */,
@@ -258,6 +257,8 @@
10BA0D311CE74E3100887C2B /* RNSVGSvgViewManager.m */,
10BA0D321CE74E3100887C2B /* RNSVGTextManager.h */,
10BA0D331CE74E3100887C2B /* RNSVGTextManager.m */,
+ 107CD53C1D7166B700F0A7AC /* RNSVGTSpanManager.h */,
+ 107CD53D1D7166B700F0A7AC /* RNSVGTSpanManager.m */,
);
path = ViewManagers;
sourceTree = "";
@@ -282,8 +283,8 @@
children = (
103371331D41D3400028AF13 /* RNSVGBezierPath.h */,
103371311D41C5C90028AF13 /* RNSVGBezierPath.m */,
- 107CD53B1D7166A000F0A7AC /* RNSVGSpan.h */,
- 107CD53F1D71672E00F0A7AC /* RNSVGSpan.m */,
+ 107CD53B1D7166A000F0A7AC /* RNSVGTSpan.h */,
+ 107CD53F1D71672E00F0A7AC /* RNSVGTSpan.m */,
1039D28F1CE71EC2001E90A8 /* RNSVGText.h */,
1039D2901CE71EC2001E90A8 /* RNSVGText.m */,
);
@@ -319,6 +320,7 @@
isa = PBXGroup;
children = (
10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */,
+ 7FF070191DC249BE000E28A0 /* RNSVGTextAnchor.h */,
10ABC7371D439779006CCF6E /* RNSVGCGFCRule.h */,
1039D2AE1CE72F27001E90A8 /* RNSVGPercentageConverter.h */,
1039D2AF1CE72F27001E90A8 /* RNSVGPercentageConverter.m */,
@@ -400,7 +402,7 @@
1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */,
10BA0D3B1CE74E3100887C2B /* RNSVGRectManager.m in Sources */,
0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */,
- 107CD53E1D7166B700F0A7AC /* RNSVGSpanManager.m in Sources */,
+ 107CD53E1D7166B700F0A7AC /* RNSVGTSpanManager.m in Sources */,
1039D2891CE71EB7001E90A8 /* RNSVGGroup.m in Sources */,
10ED4A9E1CF0656A0078BC02 /* RNSVGClipPathManager.m in Sources */,
10BEC1C61D3F7BD300FDCB19 /* RNSVGBrushConverter.m in Sources */,
@@ -409,7 +411,7 @@
10BA0D3E1CE74E3100887C2B /* RNSVGSvgViewManager.m in Sources */,
0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColorBrush.m in Sources */,
10BA0D3A1CE74E3100887C2B /* RNSVGPathManager.m in Sources */,
- 107CD5401D71672E00F0A7AC /* RNSVGSpan.m in Sources */,
+ 107CD5401D71672E00F0A7AC /* RNSVGTSpan.m in Sources */,
103371321D41C5C90028AF13 /* RNSVGBezierPath.m in Sources */,
10BA0D3C1CE74E3100887C2B /* RNSVGRenderableManager.m in Sources */,
10BEC1BD1D3F66F500FDCB19 /* RNSVGRadialGradient.m in Sources */,
diff --git a/ios/Text/RNSVGTSpan.h b/ios/Text/RNSVGTSpan.h
new file mode 100644
index 00000000..caaff504
--- /dev/null
+++ b/ios/Text/RNSVGTSpan.h
@@ -0,0 +1,24 @@
+/**
+ * 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 "RNSVGPath.h"
+#import "RNSVGText.h"
+
+@interface RNSVGTSpan : RNSVGPath
+
+@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
new file mode 100644
index 00000000..21281c91
--- /dev/null
+++ b/ios/Text/RNSVGTSpan.m
@@ -0,0 +1,104 @@
+/**
+ * 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 "RNSVGTSpan.h"
+#import "RNSVGBezierPath.h"
+
+@class RNSVGText;
+@implementation RNSVGTSpan
+
+- (CGPathRef)getPath:(CGContextRef)context
+{
+ [self setBoundingBox: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;
+// }
+
+ return (CGPathRef)CFAutorelease(path);
+}
+
+- (CGMutablePathRef)setLinePath:(CTLineRef)line
+{
+ CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0);
+ CGMutablePathRef path = CGPathCreateMutable();
+
+ CFArrayRef glyphRuns = CTLineGetGlyphRuns(line);
+ CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, 0);
+
+ CFIndex runGlyphCount = CTRunGetGlyphCount(run);
+ CGPoint positions[runGlyphCount];
+ CGGlyph glyphs[runGlyphCount];
+
+ // 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(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);
+ }
+
+ CGPathRelease(letter);
+ }
+
+ return path;
+}
+
+
+@end
diff --git a/ios/Text/RNSVGText.h b/ios/Text/RNSVGText.h
index d1dc0e20..3e1b16c5 100644
--- a/ios/Text/RNSVGText.h
+++ b/ios/Text/RNSVGText.h
@@ -8,11 +8,17 @@
#import
#import "RNSVGGroup.h"
+#import "RNSVGTextAnchor.h"
@interface RNSVGText : RNSVGGroup
-@property (nonatomic, assign) CTTextAlignment alignment;
-@property (nonatomic, copy) NSArray *path;
+@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, assign) CGFloat offsetX;
@property (nonatomic, assign) CGFloat offsetY;
diff --git a/ios/Text/RNSVGText.m b/ios/Text/RNSVGText.m
index 8c3e12d9..dc18451f 100644
--- a/ios/Text/RNSVGText.m
+++ b/ios/Text/RNSVGText.m
@@ -8,49 +8,92 @@
#import "RNSVGText.h"
#import "RNSVGBezierPath.h"
+#import "RCTConvert+RNSVG.h"
#import
@implementation RNSVGText
-- (void)setAlignment:(CTTextAlignment)alignment
+- (void)setTextAnchor:(RNSVGTextAnchor)textAnchor
{
[self invalidate];
- _alignment = alignment;
+ _textAnchor = textAnchor;
}
-- (void)setPath:(NSArray *)path
-{
- if (path == _path) {
- return;
- }
- [self invalidate];
- _path = path;
-}
-
-- (void)renderLayerTo:(CGContextRef)context
-{
- CGFloat shift = [self getShift:context path:nil];
+//- (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);
-}
+ //CGContextSaveGState(context);
+ //CGContextConcatCTM(context, CGAffineTransformMakeTranslation(-shift, 0));
+ //[super renderLayerTo:context];
+ //CGContextRestoreGState(context);
+//}
-- (CGPathRef)getPath:(CGContextRef)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
{
-
+ [self setBoundingBox:CGContextGetClipBoundingBox(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);
+// 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 (CGPathRef)CFAutorelease(path);
+ return path;
}
- (CGPathRef)getPathFromSuper:(CGContextRef)context
@@ -61,27 +104,27 @@
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 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;
+//}
@end
diff --git a/ios/Text/RNSVGTextPath.h b/ios/Text/RNSVGTextPath.h
new file mode 100644
index 00000000..293e1ab9
--- /dev/null
+++ b/ios/Text/RNSVGTextPath.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 "RNSVGPath.h"
+#import "RNSVGText.h"
+
+@interface RNSVGTextPath : RNSVGPath
+
+@property (nonatomic, strong) NSString *positionX;
+@property (nonatomic, strong) NSString *positionY;
+@property (nonatomic, assign) CTFontRef font;
+@property (nonatomic, strong) NSString *content;
+
+@end
diff --git a/ios/Text/RNSVGTextPath.m b/ios/Text/RNSVGTextPath.m
new file mode 100644
index 00000000..87e81c26
--- /dev/null
+++ b/ios/Text/RNSVGTextPath.m
@@ -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.
+ */
+
+
+#import "RNSVGTextPath.h"
+#import "RNSVGBezierPath.h"
+
+@class RNSVGText;
+@implementation RNSVGTextPath
+
+- (CGPathRef)getPath:(CGContextRef)context
+{
+ CGMutablePathRef path = CGPathCreateMutable();
+
+ return (CGPathRef)CFAutorelease(path);
+}
+
+@end
diff --git a/ios/Utils/RCTConvert+RNSVG.h b/ios/Utils/RCTConvert+RNSVG.h
index a8e284df..b3510344 100644
--- a/ios/Utils/RCTConvert+RNSVG.h
+++ b/ios/Utils/RCTConvert+RNSVG.h
@@ -12,14 +12,17 @@
#import "RNSVGCGFloatArray.h"
#import "RCTConvert.h"
#import "RNSVGCGFCRule.h"
+#import "RNSVGVBMOS.h"
+#import "RNSVGTextAnchor.h"
@class RNSVGBrush;
@interface RCTConvert (RNSVG)
+ (CGPathRef)CGPath:(id)json;
-+ (CTTextAlignment)CTTextAlignment:(id)json;
++ (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 4e0bd45a..c17131b8 100644
--- a/ios/Utils/RCTConvert+RNSVG.m
+++ b/ios/Utils/RCTConvert+RNSVG.m
@@ -12,8 +12,6 @@
#import "RNSVGPattern.h"
#import "RNSVGSolidColorBrush.h"
#import "RCTLog.h"
-#import "RNSVGCGFCRule.h"
-#import "RNSVGVBMOS.h"
#import "RCTFont.h"
@implementation RCTConvert (RNSVG)
@@ -65,14 +63,6 @@
return (CGPathRef)CFAutorelease(path);
}
-RCT_ENUM_CONVERTER(CTTextAlignment, (@{
- @"auto": @(kCTTextAlignmentNatural),
- @"left": @(kCTTextAlignmentLeft),
- @"center": @(kCTTextAlignmentCenter),
- @"right": @(kCTTextAlignmentRight),
- @"justify": @(kCTTextAlignmentJustified),
- }), kCTTextAlignmentNatural, integerValue)
-
RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
@"evenodd": @(kRNSVGCGFCRuleEvenodd),
@"nonzero": @(kRNSVGCGFCRuleNonzero),
@@ -84,16 +74,34 @@ RCT_ENUM_CONVERTER(RNSVGVBMOS, (@{
@"none": @(kRNSVGVBMOSNone)
}), kRNSVGVBMOSMeet, intValue)
+RCT_ENUM_CONVERTER(RNSVGTextAnchor, (@{
+ @"auto": @(kRNSVGTextAnchorAuto),
+ @"start": @(kRNSVGTextAnchorStart),
+ @"middle": @(kRNSVGTextAnchorMiddle),
+ @"end": @(kRNSVGTextAnchorEnd)
+ }), kRNSVGTextAnchorAuto, intValue)
+ (CTFontRef)RNSVGFont:(id)json
{
NSDictionary *dict = [self NSDictionary:json];
NSString *fontFamily = dict[@"fontFamily"];
- if (![[UIFont familyNames] containsObject:fontFamily]) {
- fontFamily = nil;
+ 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;
+
return (__bridge CTFontRef)[RCTFont updateFont:nil withFamily:fontFamily size:dict[@"fontSize"] weight:dict[@"fontWeight"] style:dict[@"fontStyle"] variant:nil scaleMultiplier:1.0];
}
diff --git a/ios/Utils/RNSVGTextAnchor.h b/ios/Utils/RNSVGTextAnchor.h
new file mode 100644
index 00000000..06eded9d
--- /dev/null
+++ b/ios/Utils/RNSVGTextAnchor.h
@@ -0,0 +1,14 @@
+/**
+ * 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 CF_ENUM(int32_t, RNSVGTextAnchor) {
+ kRNSVGTextAnchorAuto,
+ kRNSVGTextAnchorStart,
+ kRNSVGTextAnchorMiddle,
+ kRNSVGTextAnchorEnd
+};
diff --git a/ios/ViewManagers/RNSVGImageManager.m b/ios/ViewManagers/RNSVGImageManager.m
index 218d463d..8df08ae8 100644
--- a/ios/ViewManagers/RNSVGImageManager.m
+++ b/ios/ViewManagers/RNSVGImageManager.m
@@ -7,7 +7,7 @@
*/
#import "RNSVGImageManager.h"
-
+#import "RNSVGVBMOS.h"
#import "RNSVGImage.h"
#import "RCTConvert+RNSVG.h"
diff --git a/ios/ViewManagers/RNSVGTSpanManager.h b/ios/ViewManagers/RNSVGTSpanManager.h
new file mode 100644
index 00000000..fa4c5f0d
--- /dev/null
+++ b/ios/ViewManagers/RNSVGTSpanManager.h
@@ -0,0 +1,13 @@
+/**
+ * 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 "RNSVGTextManager.h"
+
+@interface RNSVGTSpanManager : RNSVGTextManager
+
+@end
diff --git a/ios/ViewManagers/RNSVGTSpanManager.m b/ios/ViewManagers/RNSVGTSpanManager.m
new file mode 100644
index 00000000..d9ddc646
--- /dev/null
+++ b/ios/ViewManagers/RNSVGTSpanManager.m
@@ -0,0 +1,26 @@
+/**
+ * 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 "RNSVGTSpanManager.h"
+
+#import "RNSVGTSpan.h"
+#import "RNSVGTextAnchor.h"
+#import "RCTConvert+RNSVG.h"
+
+@implementation RNSVGTSpanManager
+
+RCT_EXPORT_MODULE()
+
+- (RNSVGRenderable *)node
+{
+ return [RNSVGTSpan new];
+}
+
+RCT_EXPORT_VIEW_PROPERTY(content, NSString)
+
+@end
diff --git a/ios/ViewManagers/RNSVGTextManager.m b/ios/ViewManagers/RNSVGTextManager.m
index 9786ddc0..3cd1befc 100644
--- a/ios/ViewManagers/RNSVGTextManager.m
+++ b/ios/ViewManagers/RNSVGTextManager.m
@@ -20,7 +20,11 @@ RCT_EXPORT_MODULE()
return [RNSVGText new];
}
-RCT_EXPORT_VIEW_PROPERTY(alignment, CTTextAlignment)
-RCT_EXPORT_VIEW_PROPERTY(path, RNSVGBezier)
+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(font, NSDictionary)
@end
diff --git a/lib/attributes.js b/lib/attributes.js
index 2be83ddb..f26b2e9c 100644
--- a/lib/attributes.js
+++ b/lib/attributes.js
@@ -92,21 +92,20 @@ const PathAttributes = merge({
}
}, RenderableAttributes);
-const SpanAttributes = merge({
+const TextAttributes = merge({
font: {
diff: fontDiffer
},
- content: true,
- dx: true,
- dy: true,
- px: true,
- py: true
+ textAnchor: true,
+ deltaX: arrayDiffer,
+ deltaY: arrayDiffer,
+ positionX: true,
+ positionY: true
}, RenderableAttributes);
-
-const TextAttributes = merge({
- alignment: true
-}, RenderableAttributes);
+const TSpanAttibutes = merge({
+ content: true
+}, TextAttributes);
const ClipPathAttributes = {
name: true
@@ -178,7 +177,7 @@ const RectAttributes = merge({
export {
PathAttributes,
TextAttributes,
- SpanAttributes,
+ TSpanAttibutes,
GroupAttributes,
ClipPathAttributes,
CircleAttributes,
diff --git a/lib/extract/extractText.js b/lib/extract/extractText.js
index b9c1cb9f..159f6cd1 100644
--- a/lib/extract/extractText.js
+++ b/lib/extract/extractText.js
@@ -2,18 +2,20 @@ import SerializablePath from '../SerializablePath';
import _ from 'lodash';
import React, {Children} from 'react';
import {fontAndRenderPropsKeys, fontPropsKeys} from '../props';
-import Span from '../../elements/Span';
+import TSpan from '../../elements/TSpan';
const fontRegExp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?)[ptexm%]*(?:\s*\/.*?)?\s+)?\s*"?([^"]*)/i;
const fontFamilyPrefix = /^[\s"']*/;
const fontFamilySuffix = /[\s"']*$/;
const spaceReg = /\s+/;
+const deltaReg = /^((-?\d*(\.\d+)?)+\s*)+$/;
const commaReg = /,/;
const anchors = {
- end: 1,
+ auto: 0,
+ start: 1,
middle: 2,
- start: 0
+ end: 3
};
let cachedFontObjectsFromString = {};
@@ -67,161 +69,56 @@ function extractFont(props) {
return _.defaults(ownedFont, font);
}
-function parseText(props, inheritedProps = {}, deltas = []) {
- let {
- children,
- dx = '',
- dy = '',
+function parseDelta(delta) {
+ if (typeof delta === 'string' && deltaReg.test(delta)) {
+ return delta.trim().split(spaceReg).map(d => +d);
+ } else if (typeof delta === 'number') {
+ return [delta];
+ } else {
+ [];
+ }
+}
+
+export default function(props, isText) {
+ const {
x,
- y
+ y,
+ dx,
+ dy,
+ textAnchor
} = props;
- const spanArray = [];
- const deltaXArray = parseDelta(dx);
- const deltaYArray = parseDelta(dy);
- const maxDeltaLength = Math.max(deltaXArray.length, deltaYArray.length);
-
- for (let i = 0; i < maxDeltaLength; i++) {
- let result = {};
-
- if (deltaXArray.length > i && deltaXArray[i]) {
- result.x = deltaXArray[i];
- } else {
- let inheritedDeltaX = _.get(deltas, `${i}.x`);
- if (inheritedDeltaX) {
- result.x = inheritedDeltaX;
- }
- }
-
- if (deltaYArray.length > i && deltaYArray[i]) {
- result.y = deltaYArray[i];
- } else {
- let inheritedDeltaY = _.get(deltas, `${i}.y`);
- if (inheritedDeltaY) {
- result.y = inheritedDeltaY;
- }
- }
-
- deltas[i] = result;
- }
+ const deltaX = parseDelta(dx);
+ const deltaY = parseDelta(dy);
+ let { children } = props;
+ let content = null;
if (typeof children === 'string') {
-
- let computedProps = _.reduce(inheritedProps, (prev, value, name) => {
- if (!prev.hasOwnProperty(name)) {
- prev[name] = value;
- }
-
- return prev;
- }, _.pick(props, fontAndRenderPropsKeys));
-
- let delta = deltas.shift();
-
- while (delta) {
- let text;
- if (deltas.length) {
- text = children.slice(0, 1);
- children = children.slice(1);
+ if (isText) {
+ children = {children};
+ } else {
+ content = children;
+ children = null;
+ }
+ } else if (Children.count(children) > 1) {
+ children = Children.map(children, child => {
+ if (typeof child === 'string') {
+ return {child};
} else {
- text = children;
- }
-
- spanArray.push({
- content: text,
- props: computedProps,
- deltaX: +delta.x || 0,
- deltaY: +delta.y || 0,
- positionX: x || null,
- positionY: y || null
- });
-
- x = y = null;
-
- if (!text) {
- return spanArray;
- } else {
- delta = deltas.shift();
- }
- };
-
- } else {
- inheritedProps = _.assign({}, inheritedProps);
- fontAndRenderPropsKeys.forEach(inheritablePropName => {
- if (props.hasOwnProperty(inheritablePropName)) {
- inheritedProps[inheritablePropName] = props[inheritablePropName];
+ return child;
}
});
-
- Children.forEach(children, child => {
- spanArray.push(...parseText(child.props, inheritedProps, deltas));
- })
}
-
- return spanArray;
-}
-
-function parseDelta(delta) {
- return delta.toString().trim().split(spaceReg);
-}
-
-export default function(props) {
- let frames = parseText(props);
- let alignment;
-
- if (frames[0]) {
- let firstSpan = frames[0];
-
- if (firstSpan.positionX === null && props.hasOwnProperty('x')) {
- firstSpan.positionX = props.x;
- }
-
- if (firstSpan.positionY === null && props.hasOwnProperty('y')) {
- firstSpan.positionY = props.y;
- }
-
- if (firstSpan.props.hasOwnProperty('textAnchor')) {
- alignment = anchors[firstSpan.props.textAnchor];
- } else if (anchors[props.textAnchor]) {
- alignment = anchors[props.textAnchor];
- }
-
- if (!alignment) {
- alignment = 0;
- }
- }
-
- let font = {
- fontFamily: 'Helvetica Neue',
- fontSize: 12,
- fontStyle: 'normal',
- fontWeight: 'normal',
- ...extractFont(props)
- }
-
-
- let children = frames.map(frame => {
- let spanProps = {
- content: frame.content,
- dx: frame.deltaX,
- dy: frame.deltaY,
- px: frame.positionX,
- py: frame.positionY,
- font: {
- ...font,
- ...extractFont(frame.props)
- }
- };
-
- return ;
- });
-
return {
- alignment,
- children
+ textAnchor: anchors[textAnchor] || 0,
+ font: extractFont(props),
+ children,
+ content,
+ deltaX,
+ deltaY,
+ positionX: x || null,
+ positionY: y || null
}
}