From 1b04e11ca47d07234d0046fb86c947ff1590fd2f Mon Sep 17 00:00:00 2001 From: Horcrux Date: Sun, 22 Jan 2017 17:51:47 +0800 Subject: [PATCH] Refactor Symbol on iOS --- elements/Image.js | 2 +- elements/Svg.js | 49 ++++----- elements/Symbol.js | 30 ++--- elements/ViewBox.js | 79 ------------- ios/Elements/RNSVGSvgView.h | 7 ++ ios/Elements/RNSVGSvgView.m | 71 +++++++++++- ios/Elements/RNSVGSymbol.h | 26 +++++ ios/Elements/RNSVGSymbol.m | 96 ++++++++++++++++ ios/Elements/RNSVGUse.m | 13 ++- ios/RNSVG.xcodeproj/project.pbxproj | 30 +++-- ios/RNSVGNode.h | 4 +- ios/RNSVGNode.m | 10 +- ios/RNSVGViewBox.h | 25 ----- ios/Utils/RNSVGViewBox.h | 16 +++ ios/{ => Utils}/RNSVGViewBox.m | 104 ------------------ ios/ViewManagers/RNSVGSvgViewManager.m | 6 + ...GViewBoxManager.h => RNSVGSymbolManager.h} | 4 +- ios/ViewManagers/RNSVGSymbolManager.m | 31 ++++++ ios/ViewManagers/RNSVGViewBoxManager.m | 29 ----- lib/attributes.js | 8 +- lib/extract/extractViewBox.js | 56 ++++++++++ 21 files changed, 385 insertions(+), 311 deletions(-) delete mode 100644 elements/ViewBox.js create mode 100644 ios/Elements/RNSVGSymbol.h create mode 100644 ios/Elements/RNSVGSymbol.m delete mode 100644 ios/RNSVGViewBox.h create mode 100644 ios/Utils/RNSVGViewBox.h rename ios/{ => Utils}/RNSVGViewBox.m (65%) rename ios/ViewManagers/{RNSVGViewBoxManager.h => RNSVGSymbolManager.h} (72%) create mode 100644 ios/ViewManagers/RNSVGSymbolManager.m delete mode 100644 ios/ViewManagers/RNSVGViewBoxManager.m create mode 100644 lib/extract/extractViewBox.js diff --git a/elements/Image.js b/elements/Image.js index f5fa6e0e..4a500e49 100644 --- a/elements/Image.js +++ b/elements/Image.js @@ -4,7 +4,7 @@ import {ImageAttributes} from '../lib/attributes'; import {numberProp, touchableProps, responderProps} from '../lib/props'; import Shape from './Shape'; import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; -import {meetOrSliceTypes, alignEnum} from './ViewBox'; +import {meetOrSliceTypes, alignEnum} from '../lib/extract/extractViewBox'; const spacesRegExp = /\s+/; class Image extends Shape { diff --git a/elements/Svg.js b/elements/Svg.js index 35f11315..f22b8960 100644 --- a/elements/Svg.js +++ b/elements/Svg.js @@ -11,8 +11,10 @@ import { NativeModules, Platform } from 'react-native'; -import ViewBox from './ViewBox'; +import extractViewBox from '../lib/extract/extractViewBox'; +import {ViewBoxAttributes} from '../lib/attributes'; import _ from 'lodash'; + const RNSVGSvgViewManager = NativeModules.RNSVGSvgViewManager; // Svg - Root node of all Svg elements @@ -36,6 +38,10 @@ class Svg extends Component{ preserveAspectRatio: PropTypes.string }; + static defaultProps = { + preserveAspectRatio: 'xMidYMid meet' + }; + constructor() { super(...arguments); id++; @@ -79,52 +85,39 @@ class Svg extends Component{ }; render() { - let {props} = this; - let opacity = +props.opacity; - let width = +props.width; - let height = +props.height; - let viewBox = props.viewBox; + const {opacity, width, height, viewBox, preserveAspectRatio, style, ...props} = this.props; let dimensions; if (width && height) { dimensions = { - width, - height, + width: +width, + height: +height, flex: 0 }; } - if (props.viewbox) { - viewBox = props.viewbox; - console.warn('Prop `viewbox` is deprecated. please use `viewBox` instead.'); - } - - let content = viewBox ? {props.children} : props.children; - - const nativeProps = _.omit(props, ['width', 'height', 'viewBox', 'preserveAspectRatio', 'opacity']); - return {this.root = ele;}} style={[ styles.svg, - props.style, - !isNaN(opacity) && { - opacity + style, + !isNaN(+opacity) && { + opacity: +opacity }, dimensions ]} onDataURL={this._onDataURL} - > - {content} - ; + />; } } -const NativeSvgView = requireNativeComponent('RNSVGSvgView', null); +const NativeSvgView = requireNativeComponent('RNSVGSvgView', null, { + nativeOnly: { + ...ViewBoxAttributes + } +}); export default Svg; diff --git a/elements/Symbol.js b/elements/Symbol.js index 74c78466..70dffdd8 100644 --- a/elements/Symbol.js +++ b/elements/Symbol.js @@ -1,7 +1,7 @@ import React, {Component, PropTypes} from 'react'; -import ViewBox from './ViewBox'; -import G from './G'; -import Defs from './Defs'; +import extractViewBox from '../lib/extract/extractViewBox'; +import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass'; +import {SymbolAttributes} from '../lib/attributes'; class SymbolElement extends Component{ static displayName = 'Symbol'; @@ -13,26 +13,18 @@ class SymbolElement extends Component{ render() { let {props} = this; - let viewBox = props.viewBox; - if (props.viewbox) { - viewBox = props.viewbox; - console.warn('Prop `viewbox` is deprecated. please use `viewBox` instead.'); - } - - let content = viewBox ? {props.children} - : - {props.children} - ; - - return - {content} - ; + ; } } +const RNSVGSymbol = createReactNativeComponentClass({ + validAttributes: SymbolAttributes, + uiViewClassName: 'RNSVGSymbol' +}); + export default SymbolElement; diff --git a/elements/ViewBox.js b/elements/ViewBox.js deleted file mode 100644 index 8f5f62ac..00000000 --- a/elements/ViewBox.js +++ /dev/null @@ -1,79 +0,0 @@ -import React, {Component, PropTypes} from 'react'; -import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass'; -import {ViewBoxAttributes} from '../lib/attributes'; -import G from './G'; - -const meetOrSliceTypes = { - meet: 0, - slice: 1, - none: 2 -}; - -const alignEnum = [ - 'xMinYMin', 'xMidYMin', 'xMaxYMin', - 'xMinYMid', 'xMidYMid', 'xMaxYMid', - 'xMinYMax', 'xMidYMax', 'xMaxYMax', - 'none' -].reduce((prev, name) => { - prev[name] = name; - return prev; -}, {}); - -const numberRegExp = /^\d*\.?\d*%?$/; -const spacesRegExp = /\s+/; - -class ViewBox extends Component{ - static displayName = 'ViewBox'; - - static propTypes = { - viewBox: PropTypes.string.isRequired, - preserveAspectRatio: PropTypes.string - }; - - static defaultProps = { - preserveAspectRatio: 'xMidYMid meet' - }; - - render() { - const {viewBox, preserveAspectRatio, name} = this.props; - - let params = viewBox.trim().split(spacesRegExp); - - if (params.length !== 4 || !params.some(param => param && numberRegExp.test(param))) { - console.warn('`viewBox` expected a string like `minX minY width height`, but got:' + viewBox); - return - {this.props.children} - ; - } - - let modes = preserveAspectRatio.trim().split(spacesRegExp); - - let meetOrSlice = meetOrSliceTypes[modes[1]] || 0; - let align = alignEnum[modes[0]] || 'xMidYMid'; - - return - {this.props.children} - ; - } -} - -const RNSVGViewBox = createReactNativeComponentClass({ - validAttributes: ViewBoxAttributes, - uiViewClassName: 'RNSVGViewBox' -}); - - -export default ViewBox; - -export { - meetOrSliceTypes, - alignEnum -}; diff --git a/ios/Elements/RNSVGSvgView.h b/ios/Elements/RNSVGSvgView.h index 3b82a1eb..d8df638a 100644 --- a/ios/Elements/RNSVGSvgView.h +++ b/ios/Elements/RNSVGSvgView.h @@ -9,11 +9,18 @@ #import #import "RNSVGBrushCOnverter.h" #import "RNSVGContainer.h" +#import "RNSVGVBMOS.h" @class RNSVGNode; @interface RNSVGSvgView : UIView +@property (nonatomic, assign) CGFloat minX; +@property (nonatomic, assign) CGFloat minY; +@property (nonatomic, assign) CGFloat vbWidth; +@property (nonatomic, assign) CGFloat vbHeight; +@property (nonatomic, strong) NSString *align; +@property (nonatomic, assign) RNSVGVBMOS meetOrSlice; @property (nonatomic, assign) BOOL responsible; /** diff --git a/ios/Elements/RNSVGSvgView.m b/ios/Elements/RNSVGSvgView.m index eca7f57d..e03556c2 100644 --- a/ios/Elements/RNSVGSvgView.m +++ b/ios/Elements/RNSVGSvgView.m @@ -7,7 +7,7 @@ */ #import "RNSVGSvgView.h" - +#import "RNSVGViewBox.h" #import "RNSVGNode.h" #import @@ -42,6 +42,66 @@ [self setNeedsDisplay]; } +- (void)setMinX:(CGFloat)minX +{ + if (minX == _minX) { + return; + } + + [self invalidate]; + _minX = minX; +} + +- (void)setMinY:(CGFloat)minY +{ + if (minY == _minY) { + return; + } + + [self invalidate]; + _minY = minY; +} + +- (void)setVbWidth:(CGFloat)vbWidth +{ + if (vbWidth == _vbWidth) { + return; + } + + [self invalidate]; + _vbWidth = vbWidth; +} + +- (void)setVbHeight:(CGFloat)vbHeight +{ + if (_vbHeight == vbHeight) { + return; + } + + [self invalidate]; + _vbHeight = vbHeight; +} + +- (void)setAlign:(NSString *)align +{ + if ([align isEqualToString:_align]) { + return; + } + + [self invalidate]; + _align = align; +} + +- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice +{ + if (meetOrSlice == _meetOrSlice) { + return; + } + + [self invalidate]; + _meetOrSlice = meetOrSlice; +} + - (void)drawRect:(CGRect)rect { clipPaths = nil; @@ -50,6 +110,15 @@ _boundingBox = rect; CGContextRef context = UIGraphicsGetCurrentContext(); + if (self.align) { + CGAffineTransform viewBoxTransform = [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight) + eRect:rect + align:self.align + meetOrSlice:self.meetOrSlice + fromSymbol:NO]; + CGContextConcatCTM(context, viewBoxTransform); + } + for (RNSVGNode *node in self.subviews) { if ([node isKindOfClass:[RNSVGNode class]]) { if (node.responsible && !self.responsible) { diff --git a/ios/Elements/RNSVGSymbol.h b/ios/Elements/RNSVGSymbol.h new file mode 100644 index 00000000..b9297b21 --- /dev/null +++ b/ios/Elements/RNSVGSymbol.h @@ -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 "RNSVGGroup.h" + +/** + * RNSVG defination are implemented as abstract UIViews for all elements inside Defs. + */ + +@interface RNSVGSymbol : RNSVGGroup + +@property (nonatomic, assign) CGFloat minX; +@property (nonatomic, assign) CGFloat minY; +@property (nonatomic, assign) CGFloat vbWidth; +@property (nonatomic, assign) CGFloat vbHeight; +@property (nonatomic, strong) NSString *align; +@property (nonatomic, assign) RNSVGVBMOS meetOrSlice; + +- (void)renderSymbolTo:(CGContextRef)context width:(CGFloat)width height:(CGFloat)height; + +@end diff --git a/ios/Elements/RNSVGSymbol.m b/ios/Elements/RNSVGSymbol.m new file mode 100644 index 00000000..e68e48ca --- /dev/null +++ b/ios/Elements/RNSVGSymbol.m @@ -0,0 +1,96 @@ +/** + * 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 "RNSVGSymbol.h" +#import "RNSVGViewBox.h" + +@class RNSVGNode; + +@implementation RNSVGSymbol + +- (void)setMinX:(CGFloat)minX +{ + if (minX == _minX) { + return; + } + + [self invalidate]; + _minX = minX; +} + +- (void)setMinY:(CGFloat)minY +{ + if (minY == _minY) { + return; + } + + [self invalidate]; + _minY = minY; +} + +- (void)setVbWidth:(CGFloat)vbWidth +{ + if (vbWidth == _vbWidth) { + return; + } + + [self invalidate]; + _vbWidth = vbWidth; +} + +- (void)setVbHeight:(CGFloat)vbHeight +{ + if (_vbHeight == vbHeight) { + return; + } + + [self invalidate]; + _vbHeight = vbHeight; +} + +- (void)setAlign:(NSString *)align +{ + if ([align isEqualToString:_align]) { + return; + } + + [self invalidate]; + _align = align; +} + +- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice +{ + if (meetOrSlice == _meetOrSlice) { + return; + } + + [self invalidate]; + _meetOrSlice = meetOrSlice; +} + +- (void)renderTo:(CGContextRef)context +{ + [self saveDefinition]; +} + +- (void)renderSymbolTo:(CGContextRef)context width:(CGFloat)width height:(CGFloat)height +{ + if (self.align) { + CGRect eRect = CGRectMake([self getContextLeft], [self getContextTop], width, height); + + CGAffineTransform viewBoxTransform = [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight) + eRect:eRect + align:self.align + meetOrSlice:self.meetOrSlice + fromSymbol:YES]; + CGContextConcatCTM(context, viewBoxTransform); + } + [self renderGroupTo:context]; +} + +@end + diff --git a/ios/Elements/RNSVGUse.m b/ios/Elements/RNSVGUse.m index 30e4e027..3c12fbaf 100644 --- a/ios/Elements/RNSVGUse.m +++ b/ios/Elements/RNSVGUse.m @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. */ #import "RNSVGUse.h" +#import "RNSVGSymbol.h" #import @implementation RNSVGUse @@ -27,8 +28,16 @@ if (template) { [self beginTransparencyLayer:context]; [self clip:context]; - [template mergeProperties:self]; - [template renderTo:context]; + + if ([template class] == [RNSVGSymbol class]) { + [template mergeProperties:self]; + RNSVGSymbol *symbol = template; + [symbol renderSymbolTo:context width:[self relativeOnWidth:self.width] height:[self relativeOnWidth:self.height]]; + } else { + [template mergeProperties:self]; + [template renderTo:context]; + } + [self endTransparencyLayer:context]; } else if (self.href) { // TODO: calling yellow box here diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj index bc02b45c..4f646da0 100644 --- a/ios/RNSVG.xcodeproj/project.pbxproj +++ b/ios/RNSVG.xcodeproj/project.pbxproj @@ -23,8 +23,6 @@ 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 */; }; - 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 */; }; 10BA0D351CE74E3100887C2B /* RNSVGEllipseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D1F1CE74E3100887C2B /* RNSVGEllipseManager.m */; }; 10BA0D361CE74E3100887C2B /* RNSVGGroupManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D211CE74E3100887C2B /* RNSVGGroupManager.m */; }; @@ -54,6 +52,9 @@ 7F08CEA01E23479700650F83 /* RNSVGTextPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE9D1E23479700650F83 /* RNSVGTextPath.m */; }; 7F08CEA11E23479700650F83 /* RNSVGTSpan.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F08CE9F1E23479700650F83 /* RNSVGTSpan.m */; }; 7F9CDAFA1E1F809C00E0C805 /* RNSVGPathParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */; }; + 7FC260CE1E3499BC00A39833 /* RNSVGViewBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FC260CD1E3499BC00A39833 /* RNSVGViewBox.m */; }; + 7FC260D11E34A12000A39833 /* RNSVGSymbol.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FC260D01E34A12000A39833 /* RNSVGSymbol.m */; }; + 7FC260D41E34A12A00A39833 /* RNSVGSymbolManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FC260D31E34A12A00A39833 /* RNSVGSymbolManager.m */; }; 7FFC4EA41E24E5AD00AD5BE5 /* RNSVGGlyphContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FFC4EA31E24E5AD00AD5BE5 /* RNSVGGlyphContext.m */; }; /* End PBXBuildFile section */ @@ -105,10 +106,6 @@ 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 = ""; }; - 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 = ""; }; - 10ABC7351D43595E006CCF6E /* RNSVGViewBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGViewBox.m; sourceTree = ""; }; 10ABC7371D439779006CCF6E /* RNSVGCGFCRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGCGFCRule.h; path = Utils/RNSVGCGFCRule.h; sourceTree = ""; }; 10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGVBMOS.h; path = Utils/RNSVGVBMOS.h; sourceTree = ""; }; 10BA0D1C1CE74E3100887C2B /* RNSVGCircleManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGCircleManager.h; sourceTree = ""; }; @@ -171,6 +168,12 @@ 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextAnchor.h; path = Utils/RNSVGTextAnchor.h; sourceTree = ""; }; 7F9CDAF81E1F809C00E0C805 /* RNSVGPathParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGPathParser.h; path = Utils/RNSVGPathParser.h; sourceTree = ""; }; 7F9CDAF91E1F809C00E0C805 /* RNSVGPathParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPathParser.m; path = Utils/RNSVGPathParser.m; sourceTree = ""; }; + 7FC260CC1E3499BC00A39833 /* RNSVGViewBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGViewBox.h; path = Utils/RNSVGViewBox.h; sourceTree = ""; }; + 7FC260CD1E3499BC00A39833 /* RNSVGViewBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGViewBox.m; path = Utils/RNSVGViewBox.m; sourceTree = ""; }; + 7FC260CF1E34A12000A39833 /* RNSVGSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGSymbol.h; path = Elements/RNSVGSymbol.h; sourceTree = ""; }; + 7FC260D01E34A12000A39833 /* RNSVGSymbol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGSymbol.m; path = Elements/RNSVGSymbol.m; sourceTree = ""; }; + 7FC260D21E34A12A00A39833 /* RNSVGSymbolManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSymbolManager.h; sourceTree = ""; }; + 7FC260D31E34A12A00A39833 /* RNSVGSymbolManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSymbolManager.m; sourceTree = ""; }; 7FFC4EA21E24E52500AD5BE5 /* RNSVGGlyphContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RNSVGGlyphContext.h; path = Text/RNSVGGlyphContext.h; sourceTree = ""; }; 7FFC4EA31E24E5AD00AD5BE5 /* RNSVGGlyphContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGGlyphContext.m; path = Text/RNSVGGlyphContext.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -200,8 +203,6 @@ 10ED4AA11CF078830078BC02 /* RNSVGNode.m */, 0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */, 0CF68AE21AF0549300FF9E5C /* RNSVGRenderable.m */, - 10ABC7341D43595E006CCF6E /* RNSVGViewBox.h */, - 10ABC7351D43595E006CCF6E /* RNSVGViewBox.m */, 0CF68AC21AF0540F00FF9E5C /* Products */, ); sourceTree = ""; @@ -235,12 +236,12 @@ 0CF68AF81AF0549300FF9E5C /* ViewManagers */ = { isa = PBXGroup; children = ( + 7FC260D21E34A12A00A39833 /* RNSVGSymbolManager.h */, + 7FC260D31E34A12A00A39833 /* RNSVGSymbolManager.m */, 7F08CE961E23476900650F83 /* RNSVGTextPathManager.h */, 7F08CE971E23476900650F83 /* RNSVGTextPathManager.m */, 7F08CE981E23476900650F83 /* RNSVGTSpanManager.h */, 7F08CE991E23476900650F83 /* RNSVGTSpanManager.m */, - 10ABC7311D435915006CCF6E /* RNSVGViewBoxManager.h */, - 10ABC7321D435915006CCF6E /* RNSVGViewBoxManager.m */, 10BEC1BE1D3F680F00FDCB19 /* RNSVGLinearGradientManager.h */, 10BEC1BF1D3F680F00FDCB19 /* RNSVGLinearGradientManager.m */, 10BEC1C01D3F680F00FDCB19 /* RNSVGRadialGradientManager.h */, @@ -312,6 +313,8 @@ 1039D2801CE71DCF001E90A8 /* Elements */ = { isa = PBXGroup; children = ( + 7FC260CF1E34A12000A39833 /* RNSVGSymbol.h */, + 7FC260D01E34A12000A39833 /* RNSVGSymbol.m */, 10BEC1B81D3F66F500FDCB19 /* RNSVGLinearGradient.h */, 10BEC1B91D3F66F500FDCB19 /* RNSVGLinearGradient.m */, 10BEC1BA1D3F66F500FDCB19 /* RNSVGRadialGradient.h */, @@ -337,6 +340,8 @@ 1039D29A1CE7212C001E90A8 /* Utils */ = { isa = PBXGroup; children = ( + 7FC260CC1E3499BC00A39833 /* RNSVGViewBox.h */, + 7FC260CD1E3499BC00A39833 /* RNSVGViewBox.m */, 7F08CEA31E23481F00650F83 /* RNSVGTextAnchor.h */, 1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */, 10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */, @@ -407,7 +412,6 @@ buildActionMask = 2147483647; files = ( 10BA0D3F1CE74E3100887C2B /* RNSVGTextManager.m in Sources */, - 10ABC7361D43595E006CCF6E /* RNSVGViewBox.m in Sources */, 1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */, 10BA0D4B1CE74E3D00887C2B /* RNSVGRect.m in Sources */, 10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */, @@ -415,7 +419,6 @@ 1039D2B01CE72F27001E90A8 /* RNSVGPercentageConverter.m in Sources */, 7FFC4EA41E24E5AD00AD5BE5 /* RNSVGGlyphContext.m in Sources */, 10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */, - 10ABC7331D435915006CCF6E /* RNSVGViewBoxManager.m in Sources */, 1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */, 7F08CEA01E23479700650F83 /* RNSVGTextPath.m in Sources */, 0CF68B0D1AF0549300FF9E5C /* RNSVGPattern.m in Sources */, @@ -438,16 +441,19 @@ 10BEC1C31D3F680F00FDCB19 /* RNSVGRadialGradientManager.m in Sources */, 10BA0D371CE74E3100887C2B /* RNSVGImageManager.m in Sources */, 10BA0D391CE74E3100887C2B /* RNSVGNodeManager.m in Sources */, + 7FC260D11E34A12000A39833 /* RNSVGSymbol.m in Sources */, 1023B4901D3DF4C40051496D /* RNSVGDefs.m in Sources */, 10BA0D381CE74E3100887C2B /* RNSVGLineManager.m in Sources */, 10BA0D481CE74E3D00887C2B /* RNSVGCircle.m in Sources */, 10BA0D351CE74E3100887C2B /* RNSVGEllipseManager.m in Sources */, 1039D2A01CE72177001E90A8 /* RCTConvert+RNSVG.m in Sources */, 0CF68B0B1AF0549300FF9E5C /* RNSVGBrush.m in Sources */, + 7FC260D41E34A12A00A39833 /* RNSVGSymbolManager.m in Sources */, 7F9CDAFA1E1F809C00E0C805 /* RNSVGPathParser.m in Sources */, 10BA0D361CE74E3100887C2B /* RNSVGGroupManager.m in Sources */, 7F08CE9A1E23476900650F83 /* RNSVGTextPathManager.m in Sources */, 7F08CE9B1E23476900650F83 /* RNSVGTSpanManager.m in Sources */, + 7FC260CE1E3499BC00A39833 /* RNSVGViewBox.m in Sources */, 7F08CEA11E23479700650F83 /* RNSVGTSpan.m in Sources */, 10BA0D4A1CE74E3D00887C2B /* RNSVGLine.m in Sources */, 10FDEEB21D3FB60500A5C46C /* RNSVGBaseBrush.m in Sources */, diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index 88273dcf..d55a1398 100644 --- a/ios/RNSVGNode.h +++ b/ios/RNSVGNode.h @@ -66,9 +66,9 @@ */ - (RNSVGSvgView *)getSvgView; -- (CGFloat)relativeOnWidth:(NSString *)position; +- (CGFloat)relativeOnWidth:(NSString *)length; -- (CGFloat)relativeOnHeight:(NSString *)position; +- (CGFloat)relativeOnHeight:(NSString *)length; - (CGFloat)getContextWidth; diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index 01371fa4..4e2a3ad3 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -139,7 +139,7 @@ } } -- (CGPathRef)getPath: (CGContextRef) context +- (CGPathRef)getPath: (CGContextRef)context { // abstract return nil; @@ -184,14 +184,14 @@ return _svgView; } -- (CGFloat)relativeOnWidth:(NSString *)position +- (CGFloat)relativeOnWidth:(NSString *)length { - return [RNSVGPercentageConverter stringToFloat:position relative:[self getContextWidth] offset:0]; + return [RNSVGPercentageConverter stringToFloat:length relative:[self getContextWidth] offset:0]; } -- (CGFloat)relativeOnHeight:(NSString *)position +- (CGFloat)relativeOnHeight:(NSString *)length { - return [RNSVGPercentageConverter stringToFloat:position relative:[self getContextHeight] offset:0]; + return [RNSVGPercentageConverter stringToFloat:length relative:[self getContextHeight] offset:0]; } - (CGFloat)getContextWidth diff --git a/ios/RNSVGViewBox.h b/ios/RNSVGViewBox.h deleted file mode 100644 index 7143b3ec..00000000 --- a/ios/RNSVGViewBox.h +++ /dev/null @@ -1,25 +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. - */ - -#import "RNSVGGroup.h" -#import "RNSVGVBMOS.h" - -@interface RNSVGViewBox : RNSVGGroup - -@property (nonatomic, strong) NSString *minX; -@property (nonatomic, strong) NSString *minY; -@property (nonatomic, strong) NSString *vbWidth; -@property (nonatomic, strong) NSString *vbHeight; -@property (nonatomic, strong) NSString *align; -@property (nonatomic, assign) RNSVGVBMOS meetOrSlice; -@property (nonatomic, strong) NSString *width; -@property (nonatomic, strong) NSString *height; - -+ (CGAffineTransform)getTransform:(CGRect)vbRect eRect:(CGRect)eRect align:(NSString *)align meetOrSlice:(RNSVGVBMOS)meetOrSlice fromSymbol:(BOOL)fromSymbol; - -@end diff --git a/ios/Utils/RNSVGViewBox.h b/ios/Utils/RNSVGViewBox.h new file mode 100644 index 00000000..9be8eefb --- /dev/null +++ b/ios/Utils/RNSVGViewBox.h @@ -0,0 +1,16 @@ +/** + * 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 "RNSVGVBMOS.h" + +@interface RNSVGViewBox : NSObject + ++ (CGAffineTransform)getTransform:(CGRect)vbRect eRect:(CGRect)eRect align:(NSString *)align meetOrSlice:(RNSVGVBMOS)meetOrSlice fromSymbol:(BOOL)fromSymbol; + +@end diff --git a/ios/RNSVGViewBox.m b/ios/Utils/RNSVGViewBox.m similarity index 65% rename from ios/RNSVGViewBox.m rename to ios/Utils/RNSVGViewBox.m index 45ae35e1..efe1faa3 100644 --- a/ios/RNSVGViewBox.m +++ b/ios/Utils/RNSVGViewBox.m @@ -11,92 +11,6 @@ #import "RNSVGUse.h" @implementation RNSVGViewBox -{ - BOOL _fromSymbol; -} - -- (void)setMinX:(NSString *)minX -{ - if (minX == _minX) { - return; - } - [self invalidate]; - _minX = minX; -} - -- (void)setMinY:(NSString *)minY -{ - if (minY == _minY) { - return; - } - [self invalidate]; - _minY = minY; -} - -- (void)setVbHeight:(NSString *)vbHeight -{ - if (vbHeight == _vbHeight) { - return; - } - [self invalidate]; - _vbHeight = vbHeight; -} - -- (void)setVbWidth:(NSString *)vbWidth -{ - if (vbWidth == _vbWidth) { - return; - } - [self invalidate]; - _vbWidth = vbWidth; -} - -- (void)setAlign:(NSString *)align -{ - if (align == _align) { - return; - } - [self invalidate]; - _align = align; -} - -- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice -{ - if (meetOrSlice == _meetOrSlice) { - return; - } - [self invalidate]; - _meetOrSlice = meetOrSlice; -} - -- (void)renderTo:(CGContextRef)context -{ - self.matrix = [self getTransformFromProps]; - [super renderTo:context]; -} - -- (CGAffineTransform)getTransformFromProps -{ - - CGFloat vbX = [self relativeOnWidth:_minX]; - CGFloat vbY = [self relativeOnWidth:_minY]; - CGFloat vbWidth = [self relativeOnWidth:_vbWidth]; - CGFloat vbHeight = [self relativeOnWidth:_vbHeight]; - - CGFloat eX = [self getContextLeft]; - CGFloat eY = [self getContextTop]; - - - CGFloat eWidth = self.width ? [self relativeOnWidth:self.width] : [self getContextWidth]; - CGFloat eHeight = self.height ? [self relativeOnWidth:self.height] : [self getContextHeight]; - - - return [RNSVGViewBox getTransform:CGRectMake(vbX, vbY, vbWidth, vbHeight) - eRect:CGRectMake(eX, eY, eWidth, eHeight) - align:self.align - meetOrSlice:self.meetOrSlice - fromSymbol:_fromSymbol]; -} + (CGAffineTransform)getTransform:(CGRect)vbRect eRect:(CGRect)eRect align:(NSString *)align meetOrSlice:(RNSVGVBMOS)meetOrSlice fromSymbol:(BOOL)fromSymbol { @@ -180,22 +94,4 @@ return CGAffineTransformTranslate(transform, -translateX * (fromSymbol ? scaleX : 1), -translateY * (fromSymbol ? scaleY : 1)); } -- (void)mergeProperties:(__kindof RNSVGNode *)target -{ - if ([target isKindOfClass:[RNSVGUse class]]) { - RNSVGUse *use = target; - _fromSymbol = YES; - self.width = use.width; - self.height = use.height; - } -} - -- (void)resetProperties -{ - if (_fromSymbol) { - self.width = self.height = nil; - _fromSymbol = NO; - } -} - @end diff --git a/ios/ViewManagers/RNSVGSvgViewManager.m b/ios/ViewManagers/RNSVGSvgViewManager.m index b606d3bd..1cfd6345 100644 --- a/ios/ViewManagers/RNSVGSvgViewManager.m +++ b/ios/ViewManagers/RNSVGSvgViewManager.m @@ -20,6 +20,12 @@ RCT_EXPORT_MODULE() return [RNSVGSvgView new]; } +RCT_EXPORT_VIEW_PROPERTY(minX, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(minY, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(vbWidth, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(vbHeight, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(align, NSString) +RCT_EXPORT_VIEW_PROPERTY(meetOrSlice, RNSVGVBMOS) RCT_EXPORT_METHOD(toDataURL:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) { diff --git a/ios/ViewManagers/RNSVGViewBoxManager.h b/ios/ViewManagers/RNSVGSymbolManager.h similarity index 72% rename from ios/ViewManagers/RNSVGViewBoxManager.h rename to ios/ViewManagers/RNSVGSymbolManager.h index f7647bec..60013444 100644 --- a/ios/ViewManagers/RNSVGViewBoxManager.h +++ b/ios/ViewManagers/RNSVGSymbolManager.h @@ -6,8 +6,8 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNSVGGroupManager.h" +#import "RNSVGNodeManager.h" -@interface RNSVGViewBoxManager : RNSVGGroupManager +@interface RNSVGSymbolManager : RNSVGNodeManager @end diff --git a/ios/ViewManagers/RNSVGSymbolManager.m b/ios/ViewManagers/RNSVGSymbolManager.m new file mode 100644 index 00000000..81353a66 --- /dev/null +++ b/ios/ViewManagers/RNSVGSymbolManager.m @@ -0,0 +1,31 @@ +/** + * 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 "RNSVGSymbolManager.h" +#import "RNSVGRenderable.h" +#import "RNSVGSymbol.h" +#import "RCTConvert+RNSVG.h" +#import "RNSVGVBMOS.h" + +@implementation RNSVGSymbolManager + +RCT_EXPORT_MODULE() + +- (RNSVGRenderable *)node +{ + return [RNSVGSymbol new]; +} + +RCT_EXPORT_VIEW_PROPERTY(minX, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(minY, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(vbWidth, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(vbHeight, CGFloat) +RCT_EXPORT_VIEW_PROPERTY(align, NSString) +RCT_EXPORT_VIEW_PROPERTY(meetOrSlice, RNSVGVBMOS) + +@end diff --git a/ios/ViewManagers/RNSVGViewBoxManager.m b/ios/ViewManagers/RNSVGViewBoxManager.m deleted file mode 100644 index 03e1df7b..00000000 --- a/ios/ViewManagers/RNSVGViewBoxManager.m +++ /dev/null @@ -1,29 +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. - */ - -#import "RNSVGViewBoxManager.h" -#import "RNSVGViewBox.h" -#import "RNSVGVBMOS.h" - -@implementation RNSVGViewBoxManager - -RCT_EXPORT_MODULE() - -- (RNSVGViewBox *)node -{ - return [RNSVGViewBox new]; -} - -RCT_EXPORT_VIEW_PROPERTY(minX, NSString) -RCT_EXPORT_VIEW_PROPERTY(minY, NSString) -RCT_EXPORT_VIEW_PROPERTY(vbWidth, NSString) -RCT_EXPORT_VIEW_PROPERTY(vbHeight, NSString) -RCT_EXPORT_VIEW_PROPERTY(align, NSString) -RCT_EXPORT_VIEW_PROPERTY(meetOrSlice, RNSVGVBMOS) - -@end diff --git a/lib/attributes.js b/lib/attributes.js index c19f867f..86695445 100644 --- a/lib/attributes.js +++ b/lib/attributes.js @@ -33,8 +33,7 @@ const ViewBoxAttributes = { vbWidth: true, vbHeight: true, align: true, - meetOrSlice: true, - name: true + meetOrSlice: true }; const NodeAttributes = { @@ -81,6 +80,10 @@ const UseAttributes = merge({ height: true }, RenderableAttributes); +const SymbolAttributes = merge({ + name: true +}, ViewBoxAttributes); + const PathAttributes = merge({ d: true }, RenderableAttributes); @@ -185,6 +188,7 @@ export { LineAttributes, RectAttributes, UseAttributes, + SymbolAttributes, LinearGradientAttributes, RadialGradientAttributes, ViewBoxAttributes diff --git a/lib/extract/extractViewBox.js b/lib/extract/extractViewBox.js new file mode 100644 index 00000000..2730e89a --- /dev/null +++ b/lib/extract/extractViewBox.js @@ -0,0 +1,56 @@ + +import React, {Component, PropTypes} from 'react'; +import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass'; +import {ViewBoxAttributes} from '../attributes'; + +const meetOrSliceTypes = { + meet: 0, + slice: 1, + none: 2 +}; + +const alignEnum = [ + 'xMinYMin', 'xMidYMin', 'xMaxYMin', + 'xMinYMid', 'xMidYMid', 'xMaxYMid', + 'xMinYMax', 'xMidYMax', 'xMaxYMax', + 'none' +].reduce((prev, name) => { + prev[name] = name; + return prev; +}, {}); + +const spacesRegExp = /\s+/; + +export default function (props) { + const {viewBox, preserveAspectRatio} = props; + + if (!viewBox) { + return null; + } + + let params = viewBox.trim().split(spacesRegExp); + + if (params.length === 4 && params.every(param => !isNaN(+params))) { + console.warn('Invalid `viewBox` prop:' + viewBox); + return null; + } + + let modes = preserveAspectRatio ? preserveAspectRatio.trim().split(spacesRegExp) : []; + + let meetOrSlice = meetOrSliceTypes[modes[1]] || 0; + let align = alignEnum[modes[0]] || 'xMidYMid'; + + return { + minX: +params[0], + minY: +params[1], + vbWidth: +params[2], + vbHeight: +params[3], + align, + meetOrSlice + } +} + +export { + meetOrSliceTypes, + alignEnum +};