diff --git a/Example/examples/Clipping.js b/Example/examples/Clipping.js
index 7b8fcb46..7a3573bb 100644
--- a/Example/examples/Clipping.js
+++ b/Example/examples/Clipping.js
@@ -213,7 +213,7 @@ const icon = ;
-const samples = [ClipPathAttr, ClipRule, ClipPathElement, TextClipping];
+const samples = [ClipPathAttr, ClipRule];//, ClipPathElement, TextClipping
export {
icon,
diff --git a/elements/Circle.js b/elements/Circle.js
index ea11e311..b60f9c94 100644
--- a/elements/Circle.js
+++ b/elements/Circle.js
@@ -2,34 +2,32 @@ import React, {
Component,
PropTypes
} from 'react-native';
-import Ellipse from './Ellipse';
-let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
-class Circle extends Component{
+import Shape, {CIRCLE} from './Shape';
+import {circleProps, pathProps} from '../lib/props';
+import _ from 'lodash';
+
+class Circle extends Shape{
static displayName = 'Circle';
static propTypes = {
- cx: propType,
- cy: propType,
- r: propType
+ ...pathProps,
+ ...circleProps
};
+
static defaultProps = {
cx: 0,
- cy: 0
+ cy: 0,
+ r: 0
};
- static getPath = props => Ellipse.getPath({
- cx: props.cx,
- cy: props.cy,
- rx: props.r,
- ry: props.r
- });
+ static contextTypes = {
+ ...circleProps,
+ isInGroup: PropTypes.bool
+ };
- render() {
- return
- }
+ constructor() {
+ super(...arguments);
+ this.type = CIRCLE;
+ };
}
export default Circle;
diff --git a/elements/Ellipse.js b/elements/Ellipse.js
index 3fcdbc1c..290973cc 100644
--- a/elements/Ellipse.js
+++ b/elements/Ellipse.js
@@ -2,35 +2,32 @@ import React, {
Component,
PropTypes
} from 'react-native';
-import Path from './Path';
-let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
-class Ellipse extends Component{
+import Shape, {ELLIPSE} from './Shape';
+import {ellipseProps, pathProps} from '../lib/props';
+
+class Ellipse extends Shape{
static displayName = 'Ellipse';
static propTypes = {
- cx: propType,
- cy: propType,
- rx: propType,
- ry: propType
+ ...pathProps,
+ ...ellipseProps
};
- static getPath = props => {
- let {cx, cy, rx, ry} = props;
- return `
- M ${cx - rx} ${cy}
- a ${rx}, ${ry} 0 1, 0 ${rx * 2}, 0
- a ${rx}, ${ry} 0 1, 0 ${-rx * 2}, 0
- Z
- `;
+ static defaultProps = {
+ cx: 0,
+ cy: 0,
+ rx: 0,
+ ry: 0
};
- render() {
- let {props} = this;
- let d = Ellipse.getPath(this.props);
- return ;
- }
+ static contextTypes = {
+ ...ellipseProps,
+ isInGroup: PropTypes.bool
+ };
+
+ constructor() {
+ super(...arguments);
+ this.type = ELLIPSE;
+ };
}
export default Ellipse;
diff --git a/elements/G.js b/elements/G.js
index 60593eb8..d56c7fd6 100644
--- a/elements/G.js
+++ b/elements/G.js
@@ -2,42 +2,34 @@ import React, {
Component,
Children,
cloneElement,
+ PropTypes,
requireNativeComponent
} from 'react-native';
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
import Defs from './Defs';
+import _ from 'lodash';
import {GroupAttributes} from '../lib/attributes';
-
-const transformProps = {
- scale: null,
- scaleX: null,
- scaleY: null,
- rotate: null,
- transform: null,
- x: null,
- y: null,
- originX: null,
- originY: null
-};
-
-const clipProps = {
- clipPath: null,
- clipRule: null
-};
+import {numberProp, shapeProps} from '../lib/props';
import extractProps from '../lib/extract/extractProps';
class G extends Component{
static displayName = 'G';
- getChildren = () => {
- return Children.map(this.props.children, child => cloneElement(child, {
- ...this.props,
- ...transformProps,
- ...clipProps,
- ...child.props,
- id: null
- }));
+ static childContextTypes = {
+ svgId: numberProp,
+ isInGroup: PropTypes.bool,
+ ...shapeProps
+ };
+
+ getChildContext = () => {
+ return _.reduce(shapeProps, (props, value, key) => {
+ props[key] = this.props[key];
+ return props;
+ }, {
+ svgId: this.props.svgId,
+ isInGroup: true
+ });
};
render() {
@@ -56,7 +48,7 @@ class G extends Component{
return
- {this.getChildren()}
+ {this.props.children}
;
}
}
diff --git a/elements/Line.js b/elements/Line.js
index 6e6becd0..8720c056 100644
--- a/elements/Line.js
+++ b/elements/Line.js
@@ -2,30 +2,32 @@ import React, {
Component,
PropTypes
} from 'react-native';
-import Path from './Path';
+import Shape, {LINE} from './Shape';
+import {lineProps, pathProps} from '../lib/props';
-let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
-
-class Line extends Component{
+class Line extends Shape{
static displayName = 'Line';
static propTypes = {
- x1: propType,
- x2: propType,
- y1: propType,
- y2: propType,
- strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round'])
+ ...pathProps,
+ ...lineProps
};
- static getPath = (props) => (
- `M${props.x1},${props.y1} L${props.x2},${props.y2}`
- );
+ static defaultProps = {
+ x1: 0,
+ x2: 0,
+ y1: 0,
+ y2: 0
+ };
- render() {
- return ;
- }
+ static contextTypes = {
+ ...lineProps,
+ isInGroup: PropTypes.bool
+ };
+
+ constructor() {
+ super(...arguments);
+ this.type = LINE;
+ };
}
export default Line;
diff --git a/elements/LinearGradient.js b/elements/LinearGradient.js
index ce27d2fd..ba30e2c3 100644
--- a/elements/LinearGradient.js
+++ b/elements/LinearGradient.js
@@ -5,7 +5,7 @@ import React, {
} from 'react-native';
import stopsOpacity from '../lib/stopsOpacity';
-import numberProp from '../lib/numberProp';
+import {numberProp} from '../lib/props';
import Gradient from './Gradient';
import {LINEAR_GRADIENT} from '../lib/extract/extractBrush';
import insertColorStopsIntoArray from '../lib/insertProcessor';
diff --git a/elements/Path.js b/elements/Path.js
index f50b1c0d..d6ac7dfe 100644
--- a/elements/Path.js
+++ b/elements/Path.js
@@ -1,8 +1,7 @@
import React, {
Component,
PropTypes,
- requireNativeComponent,
- cloneElement
+ requireNativeComponent
} from 'react-native';
import Defs from './Defs';
@@ -11,24 +10,23 @@ import calculateBoundingBox from '../lib/calculateBoundingBox';
import extractProps from '../lib/extract/extractProps';
import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath';
import {PathAttributes} from '../lib/attributes';
-
+import {pathProps} from '../lib/props';
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
class Path extends Component{
static displayName = 'Path';
+
static propTypes = {
visible: PropTypes.bool,
d: PropTypes.string,
- x: propType,
- y: propType,
- strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
- strokeCap: PropTypes.oneOf(['butt', 'square', 'round']),
- strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round']),
- strokeJoin: PropTypes.oneOf(['miter', 'bevel', 'round']),
- strokeDasharray: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.number)])
+ ...pathProps
+ };
+
+ static contextTypes = {
+ ...pathProps,
+ isInGroup: PropTypes.bool
};
- static getPath = props => props.d;
_dimensions = null;
@@ -38,16 +36,15 @@ class Path extends Component{
}
};
- getBoundingBox = () => {
- if (!this._dimensions) {
- this._dimensions = calculateBoundingBox(this.props.d);
- }
-
- return this._dimensions;
- };
-
render() {
let {props} = this;
+
+ if (this.context.isInGroup) {
+ props = _.defaults(this.context, props, {
+ isInGroup: null
+ });
+ }
+
if (props.id) {
return
);
diff --git a/elements/RadialGradient.js b/elements/RadialGradient.js
index 2ae506e7..4d42d28d 100644
--- a/elements/RadialGradient.js
+++ b/elements/RadialGradient.js
@@ -4,7 +4,7 @@ import React, {
Children
} from 'react-native';
import stopsOpacity from '../lib/stopsOpacity';
-import numberProp from '../lib/numberProp';
+import {numberProp} from '../lib/props';
import Gradient from './Gradient';
import {RADIAL_GRADIENT} from '../lib/extract/extractBrush';
import insertColorStopsIntoArray from '../lib/insertProcessor';
diff --git a/elements/Rect.js b/elements/Rect.js
index f8838b19..b9da6091 100644
--- a/elements/Rect.js
+++ b/elements/Rect.js
@@ -2,115 +2,36 @@ import React, {
Component,
PropTypes
} from 'react-native';
+import Shape, {RECT} from './Shape';
+import {rectProps, pathProps} from '../lib/props';
-import Path from './Path';
-
-let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
-
-function processRadius(radius) {
- radius = +radius;
- return radius || 0;
-}
-
-function getR(props) {
- let {
- width,
- height,
- rx,
- ry
- } = props;
-
- rx = processRadius(rx);
- ry = processRadius(ry);
-
- if ((rx && !ry) || (ry && !rx)) {
- if (rx) {
- ry = rx;
- } else {
- rx = ry;
- }
- }
-
- if (rx > width / 2) {
- rx = width / 2;
- }
- if (ry > height / 2) {
- ry = height / 2;
- }
-
- return {
- rx,
- ry
- };
-}
-
-class Rect extends Component{
+class Rect extends Shape{
static displayName = 'Rect';
static propTypes = {
- x: propType,
- y: propType,
- width: propType,
- height: propType,
- rx: propType,
- ry: propType,
- strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
- strokeCap: PropTypes.oneOf(['butt', 'square', 'round']),
- strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round']),
- strokeJoin: PropTypes.oneOf(['miter', 'bevel', 'round'])
+ ...pathProps,
+ ...rectProps
};
+
static defaultProps = {
x: 0,
y: 0,
+ width: 0,
+ height: 0,
rx: 0,
ry: 0
};
- static getPath = (props, r) => {
- let {
- x,
- y,
- width,
- height
- } = props;
- if (!r) {
- r = getR(props);
-
- }
-
- let {rx, ry} = r;
-
-
- return (rx || ry) ? `
- M ${x}, ${y}
- h ${width - 2 * rx}
- a ${rx},${ry} 0 0 1 ${rx},${ry}
- v ${height - 2 * ry}
- a ${rx},${ry} 0 0 1 ${-rx},${ry}
- h ${2 * rx - width}
- a ${rx},${ry} 0 0 1 ${-rx},${-ry}
- v ${2 * ry - height}
- a ${rx},${ry} 0 0 1 ${rx},${-ry}
- Z
- ` : `
- M ${x}, ${y}
- h ${width}
- v ${height}
- h ${-width}
- Z
- `;
+ static contextTypes = {
+ ...rectProps,
+ isInGroup: PropTypes.bool
};
- render() {
- let r = getR(this.props);
- return ;
- }
+ constructor() {
+ super(...arguments);
+ this.type = RECT;
+ };
+
+
}
export default Rect;
diff --git a/elements/Shape.js b/elements/Shape.js
index 4446b51a..6e5b755b 100644
--- a/elements/Shape.js
+++ b/elements/Shape.js
@@ -3,32 +3,43 @@ import React, {
PropTypes
} from 'react-native';
import Path from './Path';
+import _ from 'lodash';
import extractProps from '../lib/extract/extractProps';
import {ShapeAttributes} from '../lib/attributes';
+import SerializableShape from '../lib/SerializableShape';
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
+import {circleProps, ellipseProps, lineProps, rectProps} from '../lib/props';
/**
* Circle shape type
*/
const CIRCLE = 0;
+/**
+ * ELLIPSE shape type
+ */
+const ELLIPSE = 1;
+/**
+ * LINE shape type
+ */
+const LINE = 2;
+/**
+ * RECT shape type
+ */
+const RECT = 3;
/**
* coord props
- *
- * algorithm for radius in percentage
- * radius = Math.sqrt(Math.pow((width*percent), 2) + Math.pow((height*percent), 2)) / Math.sqrt(2);
*/
-const CIRCLE_COORDS = ['cx', 'cy', 'r'];
-
-const ELLIPSE = 1;
-const ELLIPSE_COORDS = ['cx', 'cy', 'rx', 'ry'];
-
-const LINE = 2;
-const LINE_COORDS = ['x1', 'y1', 'x2', 'y2'];
-
-const RECT = 5;
-const RECT_COORDS = ['x', 'y', 'width', 'height'];
-
+const COORD_PROPS = {
+ /**
+ * algorithm for radius in percentage
+ * radius = Math.sqrt(Math.pow((width*percent), 2) + Math.pow((height*percent), 2)) / Math.sqrt(2);
+ */
+ [CIRCLE]: Object.keys(circleProps),
+ [ELLIPSE]: Object.keys(ellipseProps),
+ [LINE]: Object.keys(lineProps),
+ [RECT]: Object.keys(rectProps)
+};
/**
* virtualNode component for shape elements
@@ -37,14 +48,29 @@ class Shape extends Component{
static displayName = 'Shape';
render() {
- let {props} = this;
+ let props = this.props;
+
+ if (this.context.isInGroup) {
+ props = _.defaults(this.context, props, {
+ isInGroup: null
+ });
+ }
+
+ let shape = new SerializableShape(props, COORD_PROPS[this.type]).toJSON();
return ;
- }
+ };
}
let NativePath = createReactNativeComponentClass({
@@ -53,3 +79,10 @@ let NativePath = createReactNativeComponentClass({
});
export default Shape;
+
+export {
+ CIRCLE,
+ ELLIPSE,
+ LINE,
+ RECT
+};
diff --git a/elements/Text.js b/elements/Text.js
index 27a5ee46..e4a247d0 100644
--- a/elements/Text.js
+++ b/elements/Text.js
@@ -6,7 +6,7 @@ import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/
import extractProps from '../lib/extract/extractProps';
import extractText from '../lib/extract/extractText';
import {TextAttributes} from '../lib/attributes';
-import numberProp from '../lib/numberProp';
+import {numberProp} from '../lib/props';
class Text extends Component{
static displayName = 'Text';
diff --git a/elements/Use.js b/elements/Use.js
index 10eb27d6..8860f755 100644
--- a/elements/Use.js
+++ b/elements/Use.js
@@ -1,7 +1,6 @@
import React, {
Component,
- PropTypes,
- cloneElement
+ PropTypes
} from 'react-native';
import Defs from './Defs';
class Use extends Component{
diff --git a/ios/RCTConvert+RNSVG.m b/ios/RCTConvert+RNSVG.m
index eadaf585..1348630d 100644
--- a/ios/RCTConvert+RNSVG.m
+++ b/ios/RCTConvert+RNSVG.m
@@ -20,14 +20,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) {
@@ -60,7 +60,7 @@
CGPathRelease(path);
return NULL;
}
-
+
return (CGPathRef)CFAutorelease(path);
}
@@ -84,25 +84,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;
@@ -110,18 +110,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;
}
@@ -129,11 +129,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.
@@ -142,7 +142,7 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
array.array[i] = [arr[i] doubleValue];
}
}
-
+
return array;
}
@@ -150,7 +150,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.
diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj
index e330791f..ec2ad54f 100644
--- a/ios/RNSVG.xcodeproj/project.pbxproj
+++ b/ios/RNSVG.xcodeproj/project.pbxproj
@@ -25,6 +25,8 @@
10A062FF1CC732020000CEEF /* RNSVGSvgViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A062FD1CC732020000CEEF /* RNSVGSvgViewManager.m */; };
10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063011CC7320C0000CEEF /* RNSVGPath.m */; };
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063031CC7320C0000CEEF /* RNSVGSvgView.m */; };
+ 10C068671CCF0F87007C6982 /* RNSVGShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 10C068651CCF0F87007C6982 /* RNSVGShape.m */; };
+ 10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10C068691CCF1061007C6982 /* RNSVGShapeManager.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -80,6 +82,10 @@
10A063011CC7320C0000CEEF /* RNSVGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGPath.m; sourceTree = ""; };
10A063021CC7320C0000CEEF /* RNSVGSvgView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSvgView.h; sourceTree = ""; };
10A063031CC7320C0000CEEF /* RNSVGSvgView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSvgView.m; sourceTree = ""; };
+ 10C068641CCF0F87007C6982 /* RNSVGShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGShape.h; sourceTree = SOURCE_ROOT; };
+ 10C068651CCF0F87007C6982 /* RNSVGShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGShape.m; sourceTree = SOURCE_ROOT; };
+ 10C068681CCF1061007C6982 /* RNSVGShapeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGShapeManager.h; sourceTree = ""; };
+ 10C068691CCF1061007C6982 /* RNSVGShapeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGShapeManager.m; sourceTree = ""; };
10FEAC6A1CC7D05200F1C23C /* RNSVGCGFCRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGCGFCRule.h; sourceTree = ""; };
/* End PBXFileReference section */
@@ -104,10 +110,12 @@
0CF68ADC1AF0549300FF9E5C /* RNSVGContainer.h */,
0CF68ADD1AF0549300FF9E5C /* RNSVGGroup.h */,
0CF68ADE1AF0549300FF9E5C /* RNSVGGroup.m */,
- 0CF68ADF1AF0549300FF9E5C /* RNSVGNode.h */,
- 0CF68AE01AF0549300FF9E5C /* RNSVGNode.m */,
+ 10C068641CCF0F87007C6982 /* RNSVGShape.h */,
+ 10C068651CCF0F87007C6982 /* RNSVGShape.m */,
10A063001CC7320C0000CEEF /* RNSVGPath.h */,
10A063011CC7320C0000CEEF /* RNSVGPath.m */,
+ 0CF68ADF1AF0549300FF9E5C /* RNSVGNode.h */,
+ 0CF68AE01AF0549300FF9E5C /* RNSVGNode.m */,
10A063021CC7320C0000CEEF /* RNSVGSvgView.h */,
10A063031CC7320C0000CEEF /* RNSVGSvgView.m */,
0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */,
@@ -149,6 +157,8 @@
0CF68AF81AF0549300FF9E5C /* ViewManagers */ = {
isa = PBXGroup;
children = (
+ 10C068681CCF1061007C6982 /* RNSVGShapeManager.h */,
+ 10C068691CCF1061007C6982 /* RNSVGShapeManager.m */,
10A062FA1CC732020000CEEF /* RNSVGPathManager.h */,
10A062FB1CC732020000CEEF /* RNSVGPathManager.m */,
10A062FC1CC732020000CEEF /* RNSVGSvgViewManager.h */,
@@ -220,6 +230,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 10C068671CCF0F87007C6982 /* RNSVGShape.m in Sources */,
0CF68B161AF0549300FF9E5C /* RNSVGTextManager.m in Sources */,
10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */,
0CF68B111AF0549300FF9E5C /* RNSVGGroupManager.m in Sources */,
@@ -233,6 +244,7 @@
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */,
0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */,
0CF68B101AF0549300FF9E5C /* RCTConvert+RNSVG.m in Sources */,
+ 10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */,
10A062FE1CC732020000CEEF /* RNSVGPathManager.m in Sources */,
0CF68B061AF0549300FF9E5C /* RNSVGNode.m in Sources */,
0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColor.m in Sources */,
diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h
index 4627ffdd..40c64830 100644
--- a/ios/RNSVGNode.h
+++ b/ios/RNSVGNode.h
@@ -18,8 +18,8 @@
@interface RNSVGNode : UIView
+@property (nonatomic, assign) CGRect rect;
@property (nonatomic, assign) CGFloat opacity;
-
@property (nonatomic, assign) CGPathRef clipPath;
@property (nonatomic, assign) RNSVGCGFCRule clipRule;
diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m
index 94f59c84..533c601c 100644
--- a/ios/RNSVGNode.m
+++ b/ios/RNSVGNode.m
@@ -80,7 +80,6 @@
_clipPath = CGPathRetain(clipPath);
}
-
- (void)dealloc
{
CGPathRelease(_clipPath);
diff --git a/ios/RNSVGShape.h b/ios/RNSVGShape.h
new file mode 100644
index 00000000..17a28727
--- /dev/null
+++ b/ios/RNSVGShape.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
+
+#import "RNSVGRenderable.h"
+#import "RNSVGPath.h"
+
+@interface RNSVGShape : RNSVGPath
+@property (nonatomic, strong) NSDictionary* shape;
+
+@end
diff --git a/ios/RNSVGShape.m b/ios/RNSVGShape.m
new file mode 100644
index 00000000..ca28f342
--- /dev/null
+++ b/ios/RNSVGShape.m
@@ -0,0 +1,115 @@
+/**
+ * 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 "RNSVGShape.h"
+#import "RCTLog.h"
+
+@implementation RNSVGShape
+
+- (void)dealloc
+{
+
+}
+
+- (void)renderLayerTo:(CGContextRef)context
+{
+ int type = [[self.shape objectForKey:@"type"] intValue];
+ CGRect box = CGContextGetClipBoundingBox(context);
+ CGMutablePathRef path = CGPathCreateMutable();
+
+ float height = CGRectGetHeight(box);
+ float width = CGRectGetWidth(box);
+ switch (type) {
+ case 0:
+ {
+ // draw circle
+ CGFloat cx = [self getActualProp:@"cx" relative:width];
+ CGFloat cy = [self getActualProp:@"cy" relative:height];
+ CGFloat r = [self getActualProp:@"r" relative:height];
+ CGPathAddArc(path, nil, cx, cy, r, 0, 2*M_PI, YES);
+ break;
+ }
+ case 1:
+ {
+ // draw ellipse
+ CGFloat cx = [self getActualProp:@"cx" relative:width];
+ CGFloat cy = [self getActualProp:@"cy" relative:height];
+ CGFloat rx = [self getActualProp:@"rx" relative:height];
+ CGFloat ry = [self getActualProp:@"ry" relative:height];
+ CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx / 2, cy - ry / 2, rx * 2, ry * 2));
+ break;
+ }
+ case 2:
+ {
+ // draw line
+ CGFloat x1 = [self getActualProp:@"x1" relative:height];
+ CGFloat y1 = [self getActualProp:@"y1" relative:height];
+ CGFloat x2 = [self getActualProp:@"x2" relative:height];
+ CGFloat y2 = [self getActualProp:@"y2" relative:height];
+ CGPathMoveToPoint(path, nil, x1, y1);
+ CGPathAddLineToPoint(path, nil, x2, y2);
+ break;
+ }
+ case 3:
+ {
+ // draw rect
+ CGPathMoveToPoint(path, NULL, 0, 0);
+ CGFloat x = [self getActualProp:@"x" relative:width];
+ CGFloat y = [self getActualProp:@"y" relative:height];
+ CGFloat w = [self getActualProp:@"width" relative:width];
+ CGFloat h = [self getActualProp:@"height" relative:height];
+ CGFloat rx = [self getActualProp:@"rx" relative:width];
+ CGFloat ry = [self getActualProp:@"ry" relative:height];
+
+
+ if (rx != 0 || ry != 0) {
+ if (rx == 0) {
+ rx = ry;
+ } else if (ry == 0) {
+ ry = rx;
+ }
+
+ if (rx > w / 2) {
+ rx = w / 2;
+ }
+
+ if (ry > h / 2) {
+ ry = h / 2;
+ }
+
+ CGPathAddRoundedRect(path, nil, CGRectMake(x, y, w, h), rx, ry);
+ } else {
+ CGPathAddRect(path, nil, CGRectMake(x, y, w, h));
+ }
+ break;
+ }
+ default:
+ RCTLogError(@"Invalid Shape type %d at %@", type, self.shape);
+ //CGPathRelease(path);
+
+
+ }
+
+ self.d = path;
+ [super renderLayerTo:context];
+ //NSLog(@"%@", NSStringFromCGRect(box));
+ //NSLog(@"%@", self.shape);
+}
+
+- (CGFloat)getActualProp:(NSString *)name relative:(float)relative
+{
+ NSDictionary *prop = [self.shape objectForKey:name];
+ CGFloat value = [[prop objectForKey:@"value"] floatValue];
+ if ([[prop objectForKey:@"percentage"] integerValue] == 1) {
+ return relative * value;
+ } else {
+ return value;
+ }
+}
+
+@end
diff --git a/ios/RNSVGSvgView.m b/ios/RNSVGSvgView.m
index 4f789206..053b84c9 100644
--- a/ios/RNSVGSvgView.m
+++ b/ios/RNSVGSvgView.m
@@ -22,7 +22,7 @@
{
CGContextRef context = UIGraphicsGetCurrentContext();
for (RNSVGNode *node in self.subviews) {
- [node renderTo:context];
+ [node renderTo:context];
}
}
diff --git a/ios/ViewManagers/RNSVGShapeManager.h b/ios/ViewManagers/RNSVGShapeManager.h
new file mode 100644
index 00000000..70455e82
--- /dev/null
+++ b/ios/ViewManagers/RNSVGShapeManager.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 "RNSVGRenderableManager.h"
+
+@interface RNSVGShapeManager : RNSVGRenderableManager
+
+@end
diff --git a/ios/ViewManagers/RNSVGShapeManager.m b/ios/ViewManagers/RNSVGShapeManager.m
new file mode 100644
index 00000000..80f5e545
--- /dev/null
+++ b/ios/ViewManagers/RNSVGShapeManager.m
@@ -0,0 +1,25 @@
+/**
+ * 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 "RNSVGShapeManager.h"
+
+#import "RNSVGShape.h"
+#import "RCTConvert+RNSVG.h"
+
+@implementation RNSVGShapeManager
+
+RCT_EXPORT_MODULE()
+
+- (RNSVGRenderable *)node
+{
+ return [RNSVGShape new];
+}
+
+RCT_EXPORT_VIEW_PROPERTY(shape, NSDictionary)
+
+@end
diff --git a/lib/SerializableShape.js b/lib/SerializableShape.js
index fea01a8d..81f4a290 100644
--- a/lib/SerializableShape.js
+++ b/lib/SerializableShape.js
@@ -1,10 +1,55 @@
-export default class {
- constructor(props) {
+/**
+ * Format potential percentage props
+ *
+ * convert somet props like those
+ * width="50%"
+ * height="500"
+ *
+ * to
+ * {
+ * width: {
+ * percentage: true,
+ * value: 0.5
+ * },
+ * height: {
+ * percentage: false,
+ * value: 500
+ * }
+ * }
+ *
+ *
+ */
+import percentToFloat from './percentToFloat';
+function percentageTransform(value) {
+ if (typeof value === 'number') {
+ return {
+ percentage: false,
+ value
+ };
}
- toArray = () => {
+ let float = percentToFloat(value);
+ return {
+ percentage: float !== +value,
+ value: float
+ }
+}
+
+export default class {
+ constructor(props, list) {
+ this.shape = {};
+ list.forEach(name => {
+ if (props[name] != null) {
+ this.shape[name] = percentageTransform(props[name]);
+ }
+
+ });
+ }
+
+ toJSON = () => {
+ return this.shape;
};
};
diff --git a/lib/attributes.js b/lib/attributes.js
index 9e12d216..cc4623e2 100644
--- a/lib/attributes.js
+++ b/lib/attributes.js
@@ -6,7 +6,7 @@ function arrayDiffer(a, b) {
if (a.length !== b.length) {
return true;
}
- for (var i = 0; i < a.length; i++) {
+ for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return true;
}
@@ -14,6 +14,22 @@ function arrayDiffer(a, b) {
return false;
}
+function shapeDiffer(a, b) {
+ if (a === b) {
+ return false;
+ }
+ for (let key in a) {
+ if (a.hasOwnProperty(key)) {
+ if (key === 'type' && a.type !== b.type) {
+ return true;
+ } else if (a[key].percentage !== b[key].percentage || a[key].value !== b[key].value) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
function fontAndLinesDiffer(a, b) {
if (a === b) {
return false;
@@ -92,7 +108,7 @@ const TextAttributes = {
const ShapeAttributes = {
shape: {
- diff: arrayDiffer
+ diff: shapeDiffer
},
...RenderableAttributes
};
diff --git a/lib/extract/extractText.js b/lib/extract/extractText.js
index 1ad1ae0f..3ba752a8 100644
--- a/lib/extract/extractText.js
+++ b/lib/extract/extractText.js
@@ -75,7 +75,7 @@ function extractFont(font) {
};
}
-const alignments = {
+const anchord = {
right: 1,
center: 2,
left: 0
@@ -83,7 +83,7 @@ const alignments = {
export default function(props) {
return {
- alignment: alignments[props.textAnchor] || 0,
+ alignment: anchord[props.textAnchor] || 0,
frame: extractFontAndLines(
props,
childrenAsString(props.children)
diff --git a/lib/props.js b/lib/props.js
new file mode 100644
index 00000000..0d77fbb3
--- /dev/null
+++ b/lib/props.js
@@ -0,0 +1,103 @@
+import {
+ PropTypes
+} from 'react-native';
+
+const numberProp = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
+
+const fillProps = {
+ fill: PropTypes.string,
+ fillOpacity: numberProp,
+ fillRule: PropTypes.oneOf(['evenodd', 'nonzero'])
+};
+
+const clipProps = {
+ clipRule: PropTypes.oneOf(['evenodd', 'nonzero']),
+ clipPath: PropTypes.string
+};
+
+const strokeProps = {
+ stroke: PropTypes.string,
+ strokeWidth: numberProp,
+ strokeOpacity: numberProp,
+ strokeDasharray: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.string]),
+ strokeDashoffset: numberProp,
+ strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
+ strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round'])
+};
+
+const textProps = {
+ textAnchor: PropTypes.oneOf(['right', 'center', 'left']),
+ path: PropTypes.string
+};
+
+const transformProps = {
+ scale: numberProp,
+ scaleX: numberProp,
+ scaleY: numberProp,
+ rotate: numberProp,
+ x: numberProp,
+ y: numberProp,
+ originX: numberProp,
+ originY: numberProp,
+ transform: PropTypes.string
+};
+
+const pathProps = {
+ ...fillProps,
+ ...strokeProps,
+ ...clipProps,
+ ...transformProps
+};
+
+const circleProps = {
+ cx: numberProp,
+ cy: numberProp,
+ r: numberProp
+};
+
+const ellipseProps = {
+ cx: numberProp,
+ cy: numberProp,
+ rx: numberProp,
+ ry: numberProp
+};
+
+const lineProps = {
+ x1: numberProp,
+ x2: numberProp,
+ y1: numberProp,
+ y2: numberProp
+};
+
+const rectProps = {
+ x: numberProp,
+ y: numberProp,
+ width: numberProp,
+ height: numberProp,
+ rx: numberProp,
+ ry: numberProp
+};
+
+const shapeProps = {
+ ...circleProps,
+ ...ellipseProps,
+ ...lineProps,
+ ...rectProps,
+ ...fillProps,
+ ...strokeProps
+};
+
+export {
+ numberProp,
+ fillProps,
+ clipProps,
+ strokeProps,
+ transformProps,
+ textProps,
+ circleProps,
+ ellipseProps,
+ lineProps,
+ rectProps,
+ shapeProps,
+ pathProps
+}