diff --git a/Example/examples.js b/Example/examples.js
index bf0f1a5b..4b519b11 100644
--- a/Example/examples.js
+++ b/Example/examples.js
@@ -29,8 +29,8 @@ export {
Text,
Stroking,
G,
- //Use,
- //Symbol,
+ Use,
+ Symbol,
Gradients,
Clipping,
Image,
diff --git a/Example/examples/G.js b/Example/examples/G.js
index 1d118ffb..655bd6e4 100644
--- a/Example/examples/G.js
+++ b/Example/examples/G.js
@@ -68,6 +68,7 @@ class GTransform extends Component{
Text grouped with shapes
-
+
;
}
}
@@ -106,30 +107,35 @@ const icon = ;
diff --git a/Example/examples/Stroking.js b/Example/examples/Stroking.js
index 453a0a23..a7772196 100644
--- a/Example/examples/Stroking.js
+++ b/Example/examples/Stroking.js
@@ -19,7 +19,7 @@ class StrokeExample extends Component{
static title = 'The stroke property defines the color of a line, text or outline of an element';
render() {
return ;
}
}
@@ -43,9 +43,9 @@ class UseShapes extends Component{
-
-
-
+
+
+
;
}
}
@@ -60,7 +60,7 @@ const icon =
-
+
;
const samples = [UseExample, UseShapes];
diff --git a/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java b/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java
index 9f5f98d7..a08748d3 100644
--- a/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java
+++ b/android/src/main/java/com/horcrux/svg/RNSVGVirtualNode.java
@@ -49,7 +49,6 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
protected @Nullable Path mClipPath;
protected @Nullable String mClipPathRef;
- private static final int PATH_TYPE_ARC = 4;
private static final int PATH_TYPE_CLOSE = 1;
private static final int PATH_TYPE_CURVETO = 3;
private static final int PATH_TYPE_LINETO = 2;
@@ -233,30 +232,6 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
data[i++] * mScale,
data[i++] * mScale);
break;
- case PATH_TYPE_ARC:
- {
- float x = data[i++] * mScale;
- float y = data[i++] * mScale;
- float r = data[i++] * mScale;
- float start = (float) Math.toDegrees(data[i++]);
- float end = (float) Math.toDegrees(data[i++]);
-
- boolean clockwise = data[i++] == 1f;
- float sweep = end - start;
- if (Math.abs(sweep) > 360) {
- sweep = 360;
- } else {
- sweep = modulus(sweep, 360);
- }
- if (!clockwise && sweep < 360) {
- start = end;
- sweep = 360 - sweep;
- }
-
- RectF oval = new RectF(x - r, y - r, x + r, y + r);
- path.addArc(oval, start, sweep);
- break;
- }
default:
throw new JSApplicationIllegalArgumentException(
"Unrecognized drawing instruction " + type);
diff --git a/elements/G.js b/elements/G.js
index 79935f3e..8def1065 100644
--- a/elements/G.js
+++ b/elements/G.js
@@ -4,7 +4,6 @@ import createReactNativeComponentClass from 'react/lib/createReactNativeComponen
import {transformProps} from '../lib/props';
import {GroupAttributes} from '../lib/attributes';
import extractProps from '../lib/extract/extractProps';
-import reusableProps from '../lib/reusableProps';
class G extends Component{
static displayName = 'G';
@@ -27,7 +26,6 @@ class G extends Component{
return this.root = ele}
- mergeList={reusableProps(extractedProps, props)}
>
{this.props.children}
;
diff --git a/elements/Symbol.js b/elements/Symbol.js
index 510eb8fb..70908f3a 100644
--- a/elements/Symbol.js
+++ b/elements/Symbol.js
@@ -1,7 +1,8 @@
import React, {Component, PropTypes} from 'react';
import ViewBox from './ViewBox';
-import Defs from './Defs';
+import G from './G';
+
class SymbolElement extends Component{
static displayName = 'Symbol';
static propType = {
@@ -9,10 +10,8 @@ class SymbolElement extends Component{
};
render() {
let {props} = this;
- return
+
+ return
{props.children}
- ;
+ ;
}
}
diff --git a/elements/Use.js b/elements/Use.js
index 84c40397..ae5a862b 100644
--- a/elements/Use.js
+++ b/elements/Use.js
@@ -5,7 +5,6 @@ import Shape from './Shape';
import React from 'react';
import patternReg from '../lib/extract/patternReg';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
-import reusableProps from '../lib/reusableProps';
import _ from 'lodash';
class Defs extends Shape {
@@ -44,7 +43,6 @@ class Defs extends Shape {
return this.root = ele}
{...extractedProps}
- mergeList={reusableProps(extractedProps, props)}
href={href}
>{props.children};
}
diff --git a/elements/ViewBox.js b/elements/ViewBox.js
index ace5300d..018f7f4c 100644
--- a/elements/ViewBox.js
+++ b/elements/ViewBox.js
@@ -18,6 +18,8 @@ class ViewBox extends Component{
y = viewbox.y;
}
+
+ console.log(viewbox);
return
{(!scaleX || !scaleY) ? null : this.props.children}
;
diff --git a/ios/Elements/RNSVGGroup.h b/ios/Elements/RNSVGGroup.h
index 9091519f..12b035d5 100644
--- a/ios/Elements/RNSVGGroup.h
+++ b/ios/Elements/RNSVGGroup.h
@@ -11,11 +11,9 @@
#import "RNSVGContainer.h"
#import "RNSVGCGFCRule.h"
#import "RNSVGSvgView.h"
-#import "RNSVGNode.h"
+#import "RNSVGRenderable.h"
-@interface RNSVGGroup : RNSVGNode
-
-@property (nonatomic, copy) NSArray *mergeList;
+@interface RNSVGGroup : RNSVGRenderable
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m
index ba1b8e91..fab47103 100644
--- a/ios/Elements/RNSVGGroup.m
+++ b/ios/Elements/RNSVGGroup.m
@@ -17,7 +17,7 @@
[self clip:context];
for (RNSVGNode *node in self.subviews) {
- //[node mergeProperties:self mergeList:self.mergeList];
+ [node inheritProperties:self inheritedList:self.propList];
[node renderTo:context];
if (node.responsible && !svg.responsible) {
@@ -79,4 +79,11 @@
}
}
+- (void)inheritProperties:(__kindof RNSVGNode *)parent inheritedList:(NSArray *)inheritedList;
+{
+ for (NSString *key in inheritedList) {
+ [self inheritProperty:parent propName:key];
+ }
+}
+
@end
diff --git a/ios/Elements/RNSVGPath.m b/ios/Elements/RNSVGPath.m
index 3692d18c..3b14b7be 100644
--- a/ios/Elements/RNSVGPath.m
+++ b/ios/Elements/RNSVGPath.m
@@ -66,6 +66,7 @@
}
}
}
+
if (self.stroke) {
CGContextSetLineWidth(context, self.strokeWidth);
CGContextSetLineCap(context, self.strokeLinecap);
diff --git a/ios/Elements/RNSVGUse.h b/ios/Elements/RNSVGUse.h
index 91413cf8..54a4578f 100644
--- a/ios/Elements/RNSVGUse.h
+++ b/ios/Elements/RNSVGUse.h
@@ -15,6 +15,5 @@
@interface RNSVGUse : RNSVGRenderable
@property (nonatomic, strong) NSString *href;
-@property (nonatomic, copy) NSArray *mergeList;
@end
diff --git a/ios/Elements/RNSVGUse.m b/ios/Elements/RNSVGUse.m
index 9efe19c6..df38956a 100644
--- a/ios/Elements/RNSVGUse.m
+++ b/ios/Elements/RNSVGUse.m
@@ -10,22 +10,24 @@
@implementation RNSVGUse
-- (void)setMergeList:(NSArray *)mergeList
+- (void)setHref:(NSString *)href
{
- if (mergeList == _mergeList) {
+ if (href == _href) {
return;
}
- _mergeList = mergeList;
+
[self invalidate];
+ _href = href;
}
+
- (void)renderLayerTo:(CGContextRef)context
{
RNSVGNode* template = [[self getSvgView] getDefinedTemplate:self.href];
if (template) {
[self beginTransparencyLayer:context];
[self clip:context];
- [template mergeProperties:self mergeList:self.mergeList];
+ [template mergeProperties:self mergeList:self.propList];
[template renderTo:context];
[template resetProperties];
[self endTransparencyLayer:context];
diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj
index fa74592f..17d0490d 100644
--- a/ios/RNSVG.xcodeproj/project.pbxproj
+++ b/ios/RNSVG.xcodeproj/project.pbxproj
@@ -15,12 +15,12 @@
1023B4901D3DF4C40051496D /* RNSVGDefination.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B48F1D3DF4C40051496D /* RNSVGDefination.m */; };
1023B4931D3DF5060051496D /* RNSVGUse.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B4921D3DF5060051496D /* RNSVGUse.m */; };
1023B4961D3DF57D0051496D /* RNSVGUseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B4951D3DF57D0051496D /* RNSVGUseManager.m */; };
+ 103371321D41C5C90028AF13 /* RNSVGBezierPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 103371311D41C5C90028AF13 /* RNSVGBezierPath.m */; };
1039D2891CE71EB7001E90A8 /* RNSVGGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */; };
1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2841CE71EB7001E90A8 /* RNSVGImage.m */; };
1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2861CE71EB7001E90A8 /* RNSVGPath.m */; };
1039D28C1CE71EB7001E90A8 /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2881CE71EB7001E90A8 /* RNSVGSvgView.m */; };
1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2901CE71EC2001E90A8 /* RNSVGText.m */; };
- 1039D2961CE71EC2001E90A8 /* UIBezierPath-Points.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2931CE71EC2001E90A8 /* UIBezierPath-Points.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 */; };
10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D1D1CE74E3100887C2B /* RNSVGCircleManager.m */; };
@@ -79,6 +79,8 @@
1023B4921D3DF5060051496D /* RNSVGUse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGUse.m; path = Elements/RNSVGUse.m; sourceTree = ""; };
1023B4941D3DF57D0051496D /* RNSVGUseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGUseManager.h; sourceTree = ""; };
1023B4951D3DF57D0051496D /* RNSVGUseManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGUseManager.m; sourceTree = ""; };
+ 103371311D41C5C90028AF13 /* RNSVGBezierPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGBezierPath.m; path = Text/RNSVGBezierPath.m; sourceTree = ""; };
+ 103371331D41D3400028AF13 /* RNSVGBezierPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGBezierPath.h; path = Text/RNSVGBezierPath.h; sourceTree = ""; };
1039D2811CE71EB7001E90A8 /* RNSVGGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGGroup.h; path = Elements/RNSVGGroup.h; sourceTree = ""; };
1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGGroup.m; path = Elements/RNSVGGroup.m; sourceTree = ""; };
1039D2831CE71EB7001E90A8 /* RNSVGImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGImage.h; path = Elements/RNSVGImage.h; sourceTree = ""; };
@@ -90,8 +92,6 @@
1039D28F1CE71EC2001E90A8 /* RNSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGText.h; path = Text/RNSVGText.h; sourceTree = ""; };
1039D2901CE71EC2001E90A8 /* RNSVGText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGText.m; path = Text/RNSVGText.m; sourceTree = ""; };
1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextFrame.h; path = Text/RNSVGTextFrame.h; sourceTree = ""; };
- 1039D2921CE71EC2001E90A8 /* UIBezierPath-Points.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIBezierPath-Points.h"; path = "Text/UIBezierPath-Points.h"; sourceTree = ""; };
- 1039D2931CE71EC2001E90A8 /* UIBezierPath-Points.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIBezierPath-Points.m"; path = "Text/UIBezierPath-Points.m"; sourceTree = ""; };
1039D29B1CE72177001E90A8 /* RCTConvert+RNSVG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "RCTConvert+RNSVG.h"; path = "Utils/RCTConvert+RNSVG.h"; sourceTree = ""; };
1039D29C1CE72177001E90A8 /* RCTConvert+RNSVG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RCTConvert+RNSVG.m"; path = "Utils/RCTConvert+RNSVG.m"; sourceTree = ""; };
1039D29D1CE72177001E90A8 /* RNSVGCGFCRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGCGFCRule.h; path = Utils/RNSVGCGFCRule.h; sourceTree = ""; };
@@ -262,11 +262,11 @@
1039D27F1CE71D9B001E90A8 /* Text */ = {
isa = PBXGroup;
children = (
+ 103371331D41D3400028AF13 /* RNSVGBezierPath.h */,
+ 103371311D41C5C90028AF13 /* RNSVGBezierPath.m */,
1039D28F1CE71EC2001E90A8 /* RNSVGText.h */,
1039D2901CE71EC2001E90A8 /* RNSVGText.m */,
1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */,
- 1039D2921CE71EC2001E90A8 /* UIBezierPath-Points.h */,
- 1039D2931CE71EC2001E90A8 /* UIBezierPath-Points.m */,
);
name = Text;
sourceTree = "";
@@ -386,7 +386,7 @@
10BA0D3E1CE74E3100887C2B /* RNSVGSvgViewManager.m in Sources */,
0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColorBrush.m in Sources */,
10BA0D3A1CE74E3100887C2B /* RNSVGPathManager.m in Sources */,
- 1039D2961CE71EC2001E90A8 /* UIBezierPath-Points.m in Sources */,
+ 103371321D41C5C90028AF13 /* RNSVGBezierPath.m in Sources */,
10BA0D3C1CE74E3100887C2B /* RNSVGRenderableManager.m in Sources */,
10BEC1BD1D3F66F500FDCB19 /* RNSVGRadialGradient.m in Sources */,
10BEC1C31D3F680F00FDCB19 /* RNSVGRadialGradientManager.m in Sources */,
diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h
index 029262f3..b588c167 100644
--- a/ios/RNSVGNode.h
+++ b/ios/RNSVGNode.h
@@ -67,15 +67,22 @@
- (void)removeDefination;
/**
- * Just for template node to merge target node`s properties into owned properties
+ * just for template node to merge target node`s properties into owned properties
*/
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray *)mergeList;
/**
- * Just for template node to reset all owned properties once after rendered.
+ * just for template node to reset all owned properties once after rendered.
*/
- (void)resetProperties;
+/**
+ * inherit properties from parent g element
+ */
+- (void)inheritProperties:(__kindof RNSVGNode *)parent inheritedList:(NSArray *)inheritedList;
+
+- (void)inheritProperty:(__kindof RNSVGNode *)parent propName:(NSString *)propName;
+
- (void)beginTransparencyLayer:(CGContextRef)context;
- (void)endTransparencyLayer:(CGContextRef)context;
diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m
index 6f901719..3a184d2f 100644
--- a/ios/RNSVGNode.m
+++ b/ios/RNSVGNode.m
@@ -211,6 +211,16 @@
// abstract
}
+- (void)inheritProperty:(__kindof RNSVGNode *)parent propName:(NSString *)propName
+{
+ // abstract
+}
+
+- (void)inheritProperties:(__kindof RNSVGNode *)parent inheritedList:(NSArray *)inheritedList;
+{
+ // abstract
+}
+
- (void)dealloc
{
CGPathRelease(_clipPath);
diff --git a/ios/RNSVGRenderable.h b/ios/RNSVGRenderable.h
index 1049b042..77a9de6a 100644
--- a/ios/RNSVGRenderable.h
+++ b/ios/RNSVGRenderable.h
@@ -27,6 +27,7 @@
@property (nonatomic, assign) RNSVGCGFloatArray strokeDasharray;
@property (nonatomic, assign) CGFloat strokeDashoffset;
@property (nonatomic, assign) CGMutablePathRef hitArea;
+@property (nonatomic, copy) NSArray *propList;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m
index 66f8105e..aa7366a2 100644
--- a/ios/RNSVGRenderable.m
+++ b/ios/RNSVGRenderable.m
@@ -88,6 +88,15 @@
_strokeMiterlimit = strokeMiterlimit;
}
+- (void)setPropList:(NSArray *)propList
+{
+ if (propList == _propList) {
+ return;
+ }
+ _propList = propList;
+ [self invalidate];
+}
+
- (void)dealloc
{
CGPathRelease(_hitArea);
@@ -131,16 +140,27 @@
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray *)mergeList
{
+ [self mergeProperties:target mergeList:mergeList inherited:NO];
+}
+
+- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray *)mergeList inherited:(BOOL)inherited
+{
if (mergeList.count == 0) {
return;
}
- originProperties = [[NSMutableDictionary alloc] init];
+ if (!inherited) {
+ originProperties = [[NSMutableDictionary alloc] init];
+ changedList = mergeList;
+ }
- changedList = mergeList;
for (NSString *key in mergeList) {
- [originProperties setValue:[self valueForKey:key] forKey:key];
- [self setValue:[target valueForKey:key] forKey:key];
+ if (inherited) {
+ [self inheritProperty:target propName:key];
+ } else {
+ [originProperties setValue:[self valueForKey:key] forKey:key];
+ [self setValue:[target valueForKey:key] forKey:key];
+ }
}
}
@@ -155,6 +175,23 @@
changedList = nil;
}
+- (void)inheritProperty:(__kindof RNSVGNode *)parent propName:(NSString *)propName
+{
+ if (![self.propList containsObject:propName]) {
+ // add prop to propList
+ NSMutableArray *copy = [self.propList mutableCopy];
+ [copy addObject:propName];
+ self.propList = [copy copy];
+
+ [self setValue:[parent valueForKey:propName] forKey:propName];
+ }
+}
+
+- (void)inheritProperties:(__kindof RNSVGNode *)parent inheritedList:(NSArray *)inheritedList
+{
+ [self mergeProperties:parent mergeList:inheritedList inherited:YES];
+}
+
- (void)renderLayerTo:(CGContextRef)context
{
// abstract
diff --git a/ios/Text/RNSVGBezierPath.h b/ios/Text/RNSVGBezierPath.h
new file mode 100644
index 00000000..bd34196a
--- /dev/null
+++ b/ios/Text/RNSVGBezierPath.h
@@ -0,0 +1,17 @@
+/**
+ * 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
+
+@interface RNSVGBezierPath : NSObject
+
+- (instancetype)initWithBezierCurves:(NSArray *)bezierCurves;
+- (CGAffineTransform)transformAtDistance:(CGFloat)distance;
+
+@end
diff --git a/ios/Text/RNSVGBezierPath.m b/ios/Text/RNSVGBezierPath.m
new file mode 100644
index 00000000..f2a0416d
--- /dev/null
+++ b/ios/Text/RNSVGBezierPath.m
@@ -0,0 +1,147 @@
+/**
+ * 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.
+ */
+
+/**
+ * based on
+ */
+
+#import "RNSVGBezierPath.h"
+#import
+#import
+
+@implementation RNSVGBezierPath
+{
+ NSArray *_bezierCurves;
+ NSUInteger _bezierIndex;
+ CGFloat _offset;
+ CGFloat _lastX;
+ CGFloat _lastDistance;
+ CGPoint _lastPoint;
+ CGPoint _P0;
+ CGPoint _P1;
+ CGPoint _P2;
+ CGPoint _P3;
+}
+
+- (instancetype)initWithBezierCurves:(NSArray *)bezierCurves
+{
+ if (self = [super init]) {
+ _bezierCurves = bezierCurves;
+ _bezierIndex = 0;
+ _offset = 0;
+ _lastX = 0;
+ _lastDistance = 0;
+ }
+ return self;
+}
+
+
+- (instancetype)initWithControlPoints:(CGPoint)P0 P1:(CGPoint)P1 P2:(CGPoint)P2 P3:(CGPoint)P3
+{
+ if (self = [super init]) {
+ _P0 = P0;
+ _P1 = P1;
+ _P2 = P2;
+ _P3 = P3;
+ }
+ return self;
+}
+
+static CGFloat Bezier(CGFloat t, CGFloat P0, CGFloat P1, CGFloat P2, CGFloat P3) {
+ return (1-t)*(1-t)*(1-t)*P0+3*(1-t)*(1-t)*t*P1+3*(1-t)*t*t*P2+t*t*t*P3;
+}
+
+- (CGPoint)pointForOffset:(CGFloat)t {
+ CGFloat x = Bezier(t, _P0.x, _P1.x, _P2.x, _P3.x);
+ CGFloat y = Bezier(t, _P0.y, _P1.y, _P2.y, _P3.y);
+ return CGPointMake(x, y);
+}
+
+static CGFloat BezierPrime(CGFloat t, CGFloat P0, CGFloat P1, CGFloat P2, CGFloat P3) {
+ return -3*(1-t)*(1-t)*P0+(3*(1-t)*(1-t)*P1)-(6*t*(1-t)*P1)-(3*t*t*P2)+(6*t*(1-t)*P2)+3*t*t*P3;
+}
+
+- (CGFloat)angleForOffset:(CGFloat)t {
+ CGFloat dx = BezierPrime(t, _P0.x, _P1.x, _P2.x, _P3.x);
+ CGFloat dy = BezierPrime(t, _P0.y, _P1.y, _P2.y, _P3.y);
+ return atan2(dy, dx);
+}
+
+static CGFloat Distance(CGPoint a, CGPoint b) {
+ CGFloat dx = a.x - b.x;
+ CGFloat dy = a.y - b.y;
+ return hypot(dx, dy);
+}
+
+// Simplistic routine to find the offset along Bezier that is
+// `distance` away from `point`. `offset` is the offset used to
+// generate `point`, and saves us the trouble of recalculating it
+// This routine just walks forward until it finds a point at least
+// `distance` away. Good optimizations here would reduce the number
+// of guesses, but this is tricky since if we go too far out, the
+// curve might loop back on leading to incorrect results. Tuning
+// kStep is good start.
+- (CGFloat)offsetAtDistance:(CGFloat)distance
+ fromPoint:(CGPoint)point
+ offset:(CGFloat)offset {
+ const CGFloat kStep = 0.0005; // 0.0001 - 0.001 work well
+ CGFloat newDistance = 0;
+ CGFloat newOffset = offset + kStep;
+ while (newDistance <= distance && newOffset < 1.0) {
+ newOffset += kStep;
+ newDistance = Distance(point, [self pointForOffset:newOffset]);
+ }
+
+ _lastDistance = newDistance;
+ return newOffset;
+}
+
+- (void)setControlPoints
+{
+ NSArray *bezier = [_bezierCurves objectAtIndex:_bezierIndex];
+ _bezierIndex++;
+ if (bezier.count == 1) {
+ _lastPoint = _P0 = [(NSValue *)[bezier objectAtIndex:0] CGPointValue];
+ [self setControlPoints];
+ } else if (bezier.count == 3) {
+ _P1 = [(NSValue *)[bezier objectAtIndex:0] CGPointValue];
+ _P2 = [(NSValue *)[bezier objectAtIndex:1] CGPointValue];
+ _P3 = [(NSValue *)[bezier objectAtIndex:2] CGPointValue];
+ }
+}
+
+- (CGAffineTransform)transformAtDistance:(CGFloat)distance
+{
+ if (_offset == 0) {
+ [self setControlPoints];
+ }
+
+ CGFloat offset = [self offsetAtDistance:distance - _lastX
+ fromPoint:_lastPoint
+ offset:_offset];
+ CGPoint glyphPoint = [self pointForOffset:offset];
+ CGFloat angle = [self angleForOffset:offset];
+
+ if (offset < 1) {
+ _offset = offset;
+ _lastPoint = glyphPoint;
+ } else {
+ _offset = 0;
+ _lastPoint = _P0 = _P3;
+ _lastX += _lastDistance;
+ return [self transformAtDistance:distance];
+ }
+
+ _lastX = distance;
+ CGAffineTransform transform = CGAffineTransformMakeTranslation(glyphPoint.x, glyphPoint.y);
+ transform = CGAffineTransformRotate(transform, angle);
+
+ return transform;
+}
+
+@end
diff --git a/ios/Text/RNSVGText.h b/ios/Text/RNSVGText.h
index 05e249e1..8c5da647 100644
--- a/ios/Text/RNSVGText.h
+++ b/ios/Text/RNSVGText.h
@@ -7,7 +7,6 @@
*/
#import
-#import "UIBezierPath-Points.h"
#import "RNSVGPath.h"
#import "RNSVGTextFrame.h"
@@ -15,6 +14,6 @@
@property (nonatomic, assign) CTTextAlignment alignment;
@property (nonatomic, assign) RNSVGTextFrame textFrame;
-@property (nonatomic, assign) CGPathRef path;
+@property (nonatomic, copy) NSArray *path;
@end
diff --git a/ios/Text/RNSVGText.m b/ios/Text/RNSVGText.m
index d7984667..d4c294b3 100644
--- a/ios/Text/RNSVGText.m
+++ b/ios/Text/RNSVGText.m
@@ -7,7 +7,7 @@
*/
#import "RNSVGText.h"
-
+#import "RNSVGBezierPath.h"
#import
@implementation RNSVGText
@@ -39,19 +39,17 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
_textFrame = frame;
}
-- (void)setPath:(CGPathRef)path
+- (void)setPath:(NSArray *)path
{
if (path == _path) {
return;
}
[self invalidate];
- CGPathRelease(_path);
- _path = CGPathRetain(path);
+ _path = path;
}
- (void)dealloc
{
- CGPathRelease(_path);
RNSVGFreeTextFrame(_textFrame);
}
@@ -95,59 +93,41 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
{
CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0);
CGMutablePathRef path = CGPathCreateMutable();
- CTLineGetGlyphRuns(line);
- CFArrayRef glyphRuns = CTLineGetGlyphRuns(line);
- CFIndex runCount = CFArrayGetCount(glyphRuns);
- CFIndex glyphIndex = 0;
- for(CFIndex i = 0; i < runCount; ++i) {
- // For each run, we need to get the glyphs, their font (to get the path) and their locations.
- CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, i);
- CFIndex runGlyphCount = CTRunGetGlyphCount(run);
- CGPoint positions[runGlyphCount];
- CGGlyph glyphs[runGlyphCount];
+ 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);
+
+ RNSVGBezierPath *bezierPath = [[RNSVGBezierPath alloc] initWithBezierCurves:self.path];
+
+ for(CFIndex i = 0; i < runGlyphCount; ++i) {
+ CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[i], nil);
+ CGPoint point = positions[i];
- // 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 j = 0; j < runGlyphCount; ++j, ++glyphIndex) {
- CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[j], nil);
- CGPoint point = positions[j];
+ if (letter) {
+ CGAffineTransform transform;
- if (letter) {
- CGAffineTransform transform;
-
- // draw glyphs along path
- if (self.path) {
- CGPoint slope;
- CGRect bounding = CGPathGetBoundingBox(letter);
- UIBezierPath* pathAlong = [UIBezierPath bezierPathWithCGPath:self.path];
- CGFloat percentConsumed = (point.x + bounding.size.width) / pathAlong.length;
- if (percentConsumed >= 1.0f) {
- CGPathRelease(letter);
- continue;
- }
-
- CGPoint targetPoint = [pathAlong pointAtPercent:percentConsumed withSlope: &slope];
- float angle = atan(slope.y / slope.x); // + M_PI;
- if (slope.x < 0) {
- angle += M_PI; // going left, update the angle
- }
- transform = CGAffineTransformMakeTranslation(targetPoint.x - bounding.size.width, targetPoint.y);
- transform = CGAffineTransformRotate(transform, angle);
- transform = CGAffineTransformScale(transform, 1.0, -1.0);
- } else {
- transform = CGAffineTransformTranslate(upsideDown, point.x, point.y);
- }
-
- CGPathAddPath(path, &transform, letter);
+ // draw glyphs along path
+ if (self.path) {
+ transform = [bezierPath transformAtDistance:point.x];
+ transform = CGAffineTransformScale(transform, 1.0, -1.0);
+ } else {
+ transform = CGAffineTransformTranslate(upsideDown, point.x, point.y);
}
- CGPathRelease(letter);
+ CGPathAddPath(path, &transform, letter);
}
+
+ CGPathRelease(letter);
}
return path;
diff --git a/ios/Text/UIBezierPath-Points.h b/ios/Text/UIBezierPath-Points.h
deleted file mode 100644
index 7f77604c..00000000
--- a/ios/Text/UIBezierPath-Points.h
+++ /dev/null
@@ -1,27 +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.
- */
-
-/*
- Erica Sadun, http://ericasadun.com
- iPhone Developer's Cookbook, 6.x Edition
- BSD License, Use at your own risk
- */
-
-#import
-#import
-
-@interface UIBezierPath (Points)
-@property (nonatomic, readonly) NSArray *points;
-@property (nonatomic, readonly) NSArray *bezierElements;
-@property (nonatomic, readonly) CGFloat length;
-
-- (NSArray *) pointPercentArray;
-- (CGPoint) pointAtPercent: (CGFloat) percent withSlope: (CGPoint *) slope;
-+ (UIBezierPath *) pathWithPoints: (NSArray *) points;
-+ (UIBezierPath *) pathWithElements: (NSArray *) elements;
-@end
\ No newline at end of file
diff --git a/ios/Text/UIBezierPath-Points.m b/ios/Text/UIBezierPath-Points.m
deleted file mode 100644
index 17a3c996..00000000
--- a/ios/Text/UIBezierPath-Points.m
+++ /dev/null
@@ -1,219 +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.
- */
-
-/*
- Erica Sadun, http://ericasadun.com
- iPhone Developer's Cookbook, 6.x Edition
- BSD License, Use at your own risk
- */
-
-#import "UIBezierPath-Points.h"
-
-#define POINTSTRING(_CGPOINT_) (NSStringFromCGPoint(_CGPOINT_))
-#define VALUE(_INDEX_) [NSValue valueWithCGPoint:points[_INDEX_]]
-#define POINT(_INDEX_) [(NSValue *)[points objectAtIndex:_INDEX_] CGPointValue]
-
-// Return distance between two points
-static float distance (CGPoint p1, CGPoint p2)
-{
- float dx = p2.x - p1.x;
- float dy = p2.y - p1.y;
-
- return sqrt(dx*dx + dy*dy);
-}
-
-@implementation UIBezierPath (Points)
-void getPointsFromBezier(void *info, const CGPathElement *element)
-{
- NSMutableArray *bezierPoints = (__bridge NSMutableArray *)info;
- CGPathElementType type = element->type;
- CGPoint *points = element->points;
- if (type != kCGPathElementCloseSubpath)
- {
- if ((type == kCGPathElementAddLineToPoint) ||
- (type == kCGPathElementMoveToPoint))
- [bezierPoints addObject:VALUE(0)];
- else if (type == kCGPathElementAddQuadCurveToPoint)
- [bezierPoints addObject:VALUE(1)];
- else if (type == kCGPathElementAddCurveToPoint)
- [bezierPoints addObject:VALUE(2)];
- }
-}
-
-- (NSArray *)points
-{
- NSMutableArray *points = [NSMutableArray array];
- CGPathApply(self.CGPath, (__bridge void *)points, getPointsFromBezier);
- return points;
-}
-
-// Return a Bezier path buit with the supplied points
-+ (UIBezierPath *) pathWithPoints: (NSArray *) points
-{
- UIBezierPath *path = [UIBezierPath bezierPath];
- if (points.count == 0) return path;
- [path moveToPoint:POINT(0)];
- for (int i = 1; i < points.count; i++)
- [path addLineToPoint:POINT(i)];
- return path;
-}
-
-- (CGFloat) length
-{
- NSArray *points = self.points;
- float totalPointLength = 0.0f;
- for (int i = 1; i < points.count; i++)
- totalPointLength += distance(POINT(i), POINT(i-1));
- return totalPointLength;
-}
-
-- (NSArray *) pointPercentArray
-{
- // Use total length to calculate the percent of path consumed at each control point
- NSArray *points = self.points;
- NSUInteger pointCount = points.count;
-
- float totalPointLength = self.length;
- float distanceTravelled = 0.0f;
-
- NSMutableArray *pointPercentArray = [NSMutableArray array];
- [pointPercentArray addObject:@(0.0)];
-
- for (int i = 1; i < pointCount; i++)
- {
- distanceTravelled += distance(POINT(i), POINT(i-1));
- [pointPercentArray addObject:@(distanceTravelled / totalPointLength)];
- }
-
- // Add a final item just to stop with. Probably not needed.
- [pointPercentArray addObject:[NSNumber numberWithFloat:1.1f]]; // 110%
-
- return pointPercentArray;
-}
-
-- (CGPoint) pointAtPercent: (CGFloat) percent withSlope: (CGPoint *) slope
-{
- NSArray *points = self.points;
- NSArray *percentArray = self.pointPercentArray;
- CFIndex lastPointIndex = points.count - 1;
-
- if (!points.count) {
- return CGPointZero;
- }
-
- // Check for 0% and 100%
- if (percent <= 0.0f) {
- return POINT(0);
- }
- if (percent >= 1.0f) {
- return POINT(lastPointIndex);
- }
-
- // Find a corresponding pair of points in the path
- CFIndex index = 1;
- while ((index < percentArray.count) &&
- (percent > ((NSNumber *)percentArray[index]).floatValue)) {
- index++;
- }
-
- // This should not happen.
- if (index > lastPointIndex) {
- return POINT(lastPointIndex);
- }
-
- // Calculate the intermediate distance between the two points
- CGPoint point1 = POINT(index -1);
- CGPoint point2 = POINT(index);
-
- float percent1 = [[percentArray objectAtIndex:index - 1] floatValue];
- float percent2 = [[percentArray objectAtIndex:index] floatValue];
- float percentOffset = (percent - percent1) / (percent2 - percent1);
-
- float dx = point2.x - point1.x;
- float dy = point2.y - point1.y;
-
- // Store dy, dx for retrieving arctan
- if (slope) {
- *slope = CGPointMake(dx, dy);
- }
-
- // Calculate new point
- CGFloat newX = point1.x + (percentOffset * dx);
- CGFloat newY = point1.y + (percentOffset * dy);
- CGPoint targetPoint = CGPointMake(newX, newY);
-
- return targetPoint;
-}
-
-void getBezierElements(void *info, const CGPathElement *element)
-{
- NSMutableArray *bezierElements = (__bridge NSMutableArray *)info;
- CGPathElementType type = element->type;
- CGPoint *points = element->points;
-
- switch (type)
- {
- case kCGPathElementCloseSubpath:
- [bezierElements addObject:@[@(type)]];
- break;
- case kCGPathElementMoveToPoint:
- case kCGPathElementAddLineToPoint:
- [bezierElements addObject:@[@(type), VALUE(0)]];
- break;
- case kCGPathElementAddQuadCurveToPoint:
- [bezierElements addObject:@[@(type), VALUE(0), VALUE(1)]];
- break;
- case kCGPathElementAddCurveToPoint:
- [bezierElements addObject:@[@(type), VALUE(0), VALUE(1), VALUE(2)]];
- break;
- }
-}
-
-- (NSArray *) bezierElements
-{
- NSMutableArray *elements = [NSMutableArray array];
- CGPathApply(self.CGPath, (__bridge void *)elements, getBezierElements);
- return elements;
-}
-
-+ (UIBezierPath *) pathWithElements: (NSArray *) elements
-{
- UIBezierPath *path = [UIBezierPath bezierPath];
- if (elements.count == 0) return path;
-
- for (NSArray *points in elements)
- {
- if (!points.count) continue;
- CGPathElementType elementType = [points[0] integerValue];
- switch (elementType)
- {
- case kCGPathElementCloseSubpath:
- [path closePath];
- break;
- case kCGPathElementMoveToPoint:
- if (points.count == 2)
- [path moveToPoint:POINT(1)];
- break;
- case kCGPathElementAddLineToPoint:
- if (points.count == 2)
- [path addLineToPoint:POINT(1)];
- break;
- case kCGPathElementAddQuadCurveToPoint:
- if (points.count == 3)
- [path addQuadCurveToPoint:POINT(2) controlPoint:POINT(1)];
- break;
- case kCGPathElementAddCurveToPoint:
- if (points.count == 4)
- [path addCurveToPoint:POINT(3) controlPoint1:POINT(1) controlPoint2:POINT(2)];
- break;
- }
- }
-
- return path;
-}
-@end
diff --git a/ios/Utils/RCTConvert+RNSVG.h b/ios/Utils/RCTConvert+RNSVG.h
index 9a8c265e..744b1a43 100644
--- a/ios/Utils/RCTConvert+RNSVG.h
+++ b/ios/Utils/RCTConvert+RNSVG.h
@@ -24,6 +24,7 @@
+ (RNSVGCGFloatArray)RNSVGCGFloatArray:(id)json;
+ (RNSVGBrush *)RNSVGBrush:(id)json;
++ (NSArray *)RNSVGBezier:(id)json;
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset;
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset;
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset;
diff --git a/ios/Utils/RCTConvert+RNSVG.m b/ios/Utils/RCTConvert+RNSVG.m
index 7638f078..e9c65c5c 100644
--- a/ios/Utils/RCTConvert+RNSVG.m
+++ b/ios/Utils/RCTConvert+RNSVG.m
@@ -19,14 +19,14 @@
+ (CGPathRef)CGPath:(id)json
{
NSArray *arr = [self NSNumberArray:json];
-
+
NSUInteger count = [arr count];
-
+
#define NEXT_VALUE [self double:arr[i++]]
-
+
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
-
+
@try {
NSUInteger i = 0;
while (i < count) {
@@ -44,9 +44,6 @@
case 3:
CGPathAddCurveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE);
break;
- case 4:
- CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0);
- break;
default:
RCTLogError(@"Invalid CGPath type %zd at element %zd of %@", type, i, arr);
CGPathRelease(path);
@@ -59,7 +56,7 @@
CGPathRelease(path);
return NULL;
}
-
+
return (CGPathRef)CFAutorelease(path);
}
@@ -83,25 +80,25 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
NSDictionary *dict = [self NSDictionary:json];
RNSVGTextFrame frame;
frame.count = 0;
-
+
NSArray *lines = [self NSArray:dict[@"lines"]];
NSUInteger lineCount = [lines count];
if (lineCount == 0) {
return frame;
}
-
+
NSDictionary *fontDict = dict[@"font"];
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] scaleMultiplier:1.0];
if (!font) {
return frame;
}
-
+
// Create a dictionary for this font
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
(NSString *)kCTFontAttributeName: (__bridge id)font,
(NSString *)kCTForegroundColorFromContextAttributeName: @YES
};
-
+
// Set up text frame with font metrics
CGFloat size = CTFontGetSize(font);
frame.count = lineCount;
@@ -109,18 +106,18 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
frame.lineHeight = size * 1.1; // Base on RNSVG canvas line height estimate
frame.lines = malloc(sizeof(CTLineRef) * lineCount);
frame.widths = malloc(sizeof(CGFloat) * lineCount);
-
+
[lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) {
-
+
CFStringRef string = (__bridge CFStringRef)text;
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
CTLineRef line = CTLineCreateWithAttributedString(attrString);
CFRelease(attrString);
-
+
frame.lines[i] = line;
frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
}];
-
+
return frame;
}
@@ -128,11 +125,11 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
{
NSArray *arr = [self NSNumberArray:json];
NSUInteger count = arr.count;
-
+
RNSVGCGFloatArray array;
array.count = count;
array.array = NULL;
-
+
if (count) {
// Ideally, these arrays should already use the same memory layout.
// In that case we shouldn't need this new malloc.
@@ -141,7 +138,7 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
array.array[i] = [arr[i] doubleValue];
}
}
-
+
return array;
}
@@ -149,7 +146,7 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
{
NSArray *arr = [self NSArray:json];
NSUInteger type = [self NSUInteger:arr.firstObject];
-
+
switch (type) {
case 0: // solid color
// These are probably expensive allocations since it's often the same value.
@@ -163,6 +160,63 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
}
}
++ (NSArray *)RNSVGBezier:(id)json
+{
+ NSArray *arr = [self NSNumberArray:json];
+
+ NSMutableArray *beziers = [[NSMutableArray alloc] init];
+
+ NSUInteger count = [arr count];
+
+#define NEXT_VALUE [self double:arr[i++]]
+ @try {
+ NSValue *startPoint = [NSValue valueWithCGPoint: CGPointMake(0, 0)];
+ NSUInteger i = 0;
+ while (i < count) {
+ NSUInteger type = [arr[i++] unsignedIntegerValue];
+ switch (type) {
+ case 0:
+ {
+ startPoint = [NSValue valueWithCGPoint: CGPointMake(NEXT_VALUE, NEXT_VALUE)];
+ [beziers addObject: @[startPoint]];
+ break;
+ }
+ case 1:
+ [beziers addObject: @[]];
+ break;
+ case 2:
+ {
+ double x = NEXT_VALUE;
+ double y = NEXT_VALUE;
+ NSValue * destination = [NSValue valueWithCGPoint:CGPointMake(x, y)];
+ [beziers addObject: @[
+ destination,
+ startPoint,
+ destination
+ ]];
+ break;
+ }
+ case 3:
+ [beziers addObject: @[
+ [NSValue valueWithCGPoint:CGPointMake(NEXT_VALUE, NEXT_VALUE)],
+ [NSValue valueWithCGPoint:CGPointMake(NEXT_VALUE, NEXT_VALUE)],
+ [NSValue valueWithCGPoint:CGPointMake(NEXT_VALUE, NEXT_VALUE)],
+ ]];
+ break;
+ default:
+ RCTLogError(@"Invalid RNSVGBezier type %zd at element %zd of %@", type, i, arr);
+ return NULL;
+ }
+ }
+ }
+ @catch (NSException *exception) {
+ RCTLogError(@"Invalid RNSVGBezier format: %@", arr);
+ return NULL;
+ }
+
+ return beziers;
+}
+
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset
{
NSArray *arr = [self NSArray:json];
diff --git a/ios/ViewManagers/RNSVGGroupManager.m b/ios/ViewManagers/RNSVGGroupManager.m
index 02635f74..bb2ef64b 100644
--- a/ios/ViewManagers/RNSVGGroupManager.m
+++ b/ios/ViewManagers/RNSVGGroupManager.m
@@ -19,6 +19,4 @@ RCT_EXPORT_MODULE()
return [RNSVGGroup new];
}
-RCT_EXPORT_VIEW_PROPERTY(mergeList, NSArray)
-
@end
diff --git a/ios/ViewManagers/RNSVGRenderableManager.m b/ios/ViewManagers/RNSVGRenderableManager.m
index ae3c594d..4291886b 100644
--- a/ios/ViewManagers/RNSVGRenderableManager.m
+++ b/ios/ViewManagers/RNSVGRenderableManager.m
@@ -31,5 +31,6 @@ RCT_EXPORT_VIEW_PROPERTY(strokeLinejoin, CGLineJoin)
RCT_EXPORT_VIEW_PROPERTY(strokeDasharray, RNSVGCGFloatArray)
RCT_EXPORT_VIEW_PROPERTY(strokeDashoffset, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat)
+RCT_EXPORT_VIEW_PROPERTY(propList, NSArray)
@end
diff --git a/ios/ViewManagers/RNSVGTextManager.m b/ios/ViewManagers/RNSVGTextManager.m
index 76280e35..7df0b2eb 100644
--- a/ios/ViewManagers/RNSVGTextManager.m
+++ b/ios/ViewManagers/RNSVGTextManager.m
@@ -22,6 +22,6 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(alignment, CTTextAlignment)
RCT_REMAP_VIEW_PROPERTY(frame, textFrame, RNSVGTextFrame)
-RCT_EXPORT_VIEW_PROPERTY(path, CGPath)
+RCT_EXPORT_VIEW_PROPERTY(path, RNSVGBezier)
@end
diff --git a/ios/ViewManagers/RNSVGUseManager.m b/ios/ViewManagers/RNSVGUseManager.m
index 920effe5..59e8ae9a 100644
--- a/ios/ViewManagers/RNSVGUseManager.m
+++ b/ios/ViewManagers/RNSVGUseManager.m
@@ -19,6 +19,5 @@ RCT_EXPORT_MODULE()
}
RCT_EXPORT_VIEW_PROPERTY(href, NSString)
-RCT_EXPORT_VIEW_PROPERTY(mergeList, NSArray)
@end
diff --git a/lib/SerializablePath.js b/lib/SerializablePath.js
index e0e9904b..2b78c59f 100644
--- a/lib/SerializablePath.js
+++ b/lib/SerializablePath.js
@@ -38,7 +38,7 @@ export default class SerializablePath {
case 's': this.curve(p[i++], p[i++], null, null, p[i++], p[i++]); break;
case 'q': this.curve(p[i++], p[i++], p[i++], p[i++]); break;
case 't': this.curve(p[i++], p[i++]); break;
- case 'a': this.arc(p[i + 5], p[i + 6], p[i], p[i + 1], p[i + 3], !+p[i + 4], p[i + 2]); i += 7; break;
+ case 'a': this.arc(p[i + 5], p[i + 6], p[i], p[i + 1], p[i + 3], !+p[i + 4], +p[i + 2]); i += 7; break;
case 'h': this.line(p[i++], 0); break;
case 'v': this.line(0, p[i++]); break;
@@ -48,7 +48,7 @@ export default class SerializablePath {
case 'S': this.curveTo(p[i++], p[i++], null, null, p[i++], p[i++]); break;
case 'Q': this.curveTo(p[i++], p[i++], p[i++], p[i++]); break;
case 'T': this.curveTo(p[i++], p[i++]); break;
- case 'A': this.arcTo(p[i + 5], p[i + 6], p[i], p[i + 1], p[i + 3], !+p[i + 4], p[i + 2]); i += 7; break;
+ case 'A': this.arcTo(p[i + 5], p[i + 6], p[i], p[i + 1], p[i + 3], !+p[i + 4], +p[i + 2]); i += 7; break;
case 'H': this.lineTo(p[i++], this.penY); break;
case 'V': this.lineTo(this.penX, p[i++]); break;
@@ -276,12 +276,9 @@ export default class SerializablePath {
};
onArc = (sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation) => {
- if (rx !== ry || rotation) {
- return this._arcToBezier(
- sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation
- );
- }
- this.path.push(ARC, cx, cy, rx, sa, ea, ccw ? 0 : 1);
+ return this._arcToBezier(
+ sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation
+ );
};
onClose = () => {
diff --git a/lib/attributes.js b/lib/attributes.js
index d183b75b..bc582ff2 100644
--- a/lib/attributes.js
+++ b/lib/attributes.js
@@ -52,6 +52,9 @@ const NodeAttributes = {
clipPath: {
diff: arrayDiffer
},
+ propList: {
+ diff: arrayDiffer
+ },
responsible: true
};
@@ -77,16 +80,9 @@ const RenderableOnlyAttributes = {
const RenderableAttributes = merge({}, NodeAttributes, RenderableOnlyAttributes);
-const GroupAttributes = merge({
- mergeList: {
- diff: arrayDiffer
- }
-}, NodeAttributes);
+const GroupAttributes = RenderableAttributes;
const UseAttributes = merge({
- mergeList: {
- diff: arrayDiffer
- },
href: true
}, RenderableAttributes);
diff --git a/lib/extract/extractBrush.js b/lib/extract/extractBrush.js
index fa05b099..7e1c4778 100644
--- a/lib/extract/extractBrush.js
+++ b/lib/extract/extractBrush.js
@@ -3,20 +3,22 @@ import _ from 'lodash';
import patternReg from './patternReg';
export default function (colorOrBrush) {
- if (colorOrBrush === 'none') {
+ if (colorOrBrush === 'none' || _.isNil(colorOrBrush)) {
return null;
- } else if (_.isNil(colorOrBrush)) {
- colorOrBrush = '#000';
}
- let matched = colorOrBrush.match(patternReg);
-
- // brush
- if (matched) {
- return [1, matched[1]];
- //todo:
- } else { // solid color
- let c = new Color(colorOrBrush).rgbaArray();
- return [0, c[0] / 255, c[1] / 255, c[2] / 255, c[3]];
+ try {
+ let matched = colorOrBrush.match(patternReg);
+ // brush
+ if (matched) {
+ return [1, matched[1]];
+ //todo:
+ } else { // solid color
+ let c = new Color(colorOrBrush).rgbaArray();
+ return [0, c[0] / 255, c[1] / 255, c[2] / 255, c[3]];
+ }
+ } catch(err) {
+ console.warn(`"${colorOrBrush}" is not a valid color or brush`);
+ return null;
}
}
diff --git a/lib/extract/extractFill.js b/lib/extract/extractFill.js
index 0e8ebfe2..dc26ab94 100644
--- a/lib/extract/extractFill.js
+++ b/lib/extract/extractFill.js
@@ -1,5 +1,6 @@
import extractBrush from './extractBrush';
import extractOpacity from './extractOpacity';
+import _ from 'lodash';
const fillRules = {
evenodd: 0,
@@ -8,7 +9,8 @@ const fillRules = {
export default function(props) {
return {
- fill: extractBrush(props.fill),
+ // default fill is black
+ fill: extractBrush(_.isNil(props.fill) ? '#000' : props.fill),
fillOpacity: extractOpacity(props.fillOpacity),
fillRule: fillRules[props.fillRule] === 0 ? 0 : 1
};
diff --git a/lib/extract/extractProps.js b/lib/extract/extractProps.js
index ecdd15be..f8f1d079 100644
--- a/lib/extract/extractProps.js
+++ b/lib/extract/extractProps.js
@@ -4,11 +4,29 @@ import extractTransform from './extractTransform';
import extractClipping from './extractClipping';
import extractResponder from './extractResponder';
import extractOpacity from './extractOpacity';
+import {RenderableOnlyAttributes} from '../attributes';
import _ from 'lodash';
export default function(props, options = {stroke: true, transform: true, fill: true, responder: true}) {
+ let propList = [];
+ Object.keys(RenderableOnlyAttributes).forEach(name => {
+ if (!_.isNil(props[name])) {
+ // clipPath prop may provide `clipPathRef` as native prop
+ if (name === 'clipPath') {
+ if (extractedProps[name]) {
+ propList.push(name);
+ } else if (extractedProps.clipPathRef) {
+ propList.push('clipPathRef');
+ }
+ } else {
+ propList.push(name);
+ }
+ }
+ });
+
let extractedProps = {
- opacity: extractOpacity(props.opacity)
+ opacity: extractOpacity(props.opacity),
+ propList
};
if (props.id) {
diff --git a/lib/extract/extractStroke.js b/lib/extract/extractStroke.js
index 671cd649..94b08568 100644
--- a/lib/extract/extractStroke.js
+++ b/lib/extract/extractStroke.js
@@ -17,16 +17,11 @@ const joins = {
export default function(props) {
let {stroke} = props;
- if (!stroke) {
- return null;
- }
let strokeWidth = +props.strokeWidth;
if (_.isNil(props.strokeWidth)) {
- strokeWidth = 1;
- } else if (!strokeWidth) {
- return;
+ strokeWidth = null;
}
let strokeDasharray = props.strokeDasharray;
@@ -40,10 +35,6 @@ export default function(props) {
strokeDasharray.push(strokeDasharray[0]);
}
- if (!stroke) {
- stroke = '#000';
- }
-
return {
stroke: extractBrush(stroke),
strokeOpacity: extractOpacity(props.strokeOpacity),
diff --git a/lib/extract/extractText.js b/lib/extract/extractText.js
index 4fe0b561..514e6284 100644
--- a/lib/extract/extractText.js
+++ b/lib/extract/extractText.js
@@ -1,4 +1,4 @@
-import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath';
+import SerializablePath from '../SerializablePath';
import _ from 'lodash';
const newLine = /\n/g;
const defaultFontFamily = '"Helvetica Neue", "Helvetica", Arial';
@@ -82,6 +82,7 @@ const anchord = {
};
export default function(props) {
+
return {
alignment: anchord[props.textAnchor] || 0,
frame: extractFontAndLines(
diff --git a/lib/reusableProps.js b/lib/reusableProps.js
deleted file mode 100644
index 07be0dc2..00000000
--- a/lib/reusableProps.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import {RenderableOnlyAttributes} from '../lib/attributes';
-import _ from 'lodash';
-
-export default function (extractedProps, originProps) {
- let reusableProps = [];
- Object.keys(RenderableOnlyAttributes).forEach(name => {
- if (!_.isNil(originProps[name])) {
- // clipPath prop may provide `clipPathRef` as native prop
- if (name === 'clipPath') {
- if (extractedProps[name]) {
- reusableProps.push(name);
- } else if (extractedProps.clipPathRef) {
- reusableProps.push('clipPathRef');
- }
- } else {
- reusableProps.push(name);
- }
- }
- });
-
- return reusableProps;
-}