Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use tag and some attributes were not supporting. Needed for node-qrcode #81

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
239 changes: 123 additions & 116 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@ import PropTypes from 'prop-types'
import xmldom from 'xmldom';
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';

import Svg,{
Circle,
Ellipse,
G ,
LinearGradient,
RadialGradient,
Line,
Path,
Polygon,
Polyline,
Rect,
Text,
TSpan,
Defs,
Stop
import Svg, {
Circle,
Ellipse,
G,
LinearGradient,
RadialGradient,
Line,
Path,
Polygon,
Polyline,
Rect,
Text,
TSpan,
Defs,
Stop,
Use
} from 'react-native-svg';

import * as utils from './utils';
Expand All @@ -38,12 +39,13 @@ const ACCEPTED_SVG_ELEMENTS = [
'polygon',
'polyline',
'text',
'tspan'
'tspan',
'use'
];

// Attributes from SVG elements that are mapped directly.
const SVG_ATTS = ['viewBox', 'width', 'height'];
const G_ATTS = ['id'];
const G_ATTS = [];

const CIRCLE_ATTS = ['cx', 'cy', 'r'];
const PATH_ATTS = ['d'];
Expand All @@ -58,14 +60,14 @@ const TEXT_ATTS = ['fontFamily', 'fontSize', 'fontWeight']

const POLYGON_ATTS = ['points'];
const POLYLINE_ATTS = ['points'];

const COMMON_ATTS = ['fill', 'fillOpacity', 'stroke', 'strokeWidth', 'strokeOpacity', 'opacity',
'strokeLinecap', 'strokeLinejoin',
'strokeDasharray', 'strokeDashoffset', 'x', 'y', 'rotate', 'scale', 'origin', 'originX', 'originY'];
const USE_ATTS = ['xlink:href', 'href'];
const COMMON_ATTS = ['fill', 'fillOpacity', 'fill-opacity', 'stroke', 'strokeWidth', 'strokeOpacity', 'opacity',
'strokeLinecap', 'strokeLinejoin',
'strokeDasharray', 'strokeDashoffset', 'x', 'y', 'rotate', 'scale', 'origin', 'originX', 'originY', 'id'];

let ind = 0;

function fixYPosition (y, node) {
function fixYPosition(y, node) {
if (node.attributes) {
const fontSizeAttr = Object.keys(node.attributes).find(a => node.attributes[a].name === 'font-size');
if (fontSizeAttr) {
Expand All @@ -78,19 +80,19 @@ function fixYPosition (y, node) {
return fixYPosition(y, node.parentNode)
}

class SvgUri extends Component{
class SvgUri extends Component {

constructor(props){
constructor(props) {
super(props);

this.state = {fill: props.fill, svgXmlData: props.svgXmlData};

this.createSVGElement = this.createSVGElement.bind(this);
this.obtainComponentAtts = this.obtainComponentAtts.bind(this);
this.inspectNode = this.inspectNode.bind(this);
this.fetchSVGData = this.fetchSVGData.bind(this);
this.createSVGElement = this.createSVGElement.bind(this);
this.obtainComponentAtts = this.obtainComponentAtts.bind(this);
this.inspectNode = this.inspectNode.bind(this);
this.fetchSVGData = this.fetchSVGData.bind(this);

this.isComponentMounted = false;
this.isComponentMounted = false;

// Gets the image data from an URL or a static file
if (props.source) {
Expand All @@ -103,118 +105,123 @@ class SvgUri extends Component{
this.isComponentMounted = true;
}

componentWillReceiveProps (nextProps){
componentWillReceiveProps(nextProps) {
if (nextProps.source) {
const source = resolveAssetSource(nextProps.source) || {};
const oldSource = resolveAssetSource(this.props.source) || {};
if(source.uri !== oldSource.uri){
if (source.uri !== oldSource.uri) {
this.fetchSVGData(source.uri);
}
}

if (nextProps.svgXmlData !== this.props.svgXmlData) {
this.setState({ svgXmlData: nextProps.svgXmlData });
this.setState({svgXmlData: nextProps.svgXmlData});
}

if (nextProps.fill !== this.props.fill) {
this.setState({ fill: nextProps.fill });
this.setState({fill: nextProps.fill});
}
}

componentWillUnmount() {
this.isComponentMounted = false
}

async fetchSVGData(uri){
async fetchSVGData(uri) {
let responseXML = null;
try {
const response = await fetch(uri);
responseXML = await response.text();
} catch(e) {
} catch (e) {
console.error("ERROR SVG", e);
} finally {
if (this.isComponentMounted) {
this.setState({svgXmlData:responseXML});
this.setState({svgXmlData: responseXML});
}
}

return responseXML;
}
// Remove empty strings from children array

// Remove empty strings from children array
trimElementChilden(children) {
for (child of children) {
if (typeof child === 'string') {
if (child.trim.length === 0)
children.splice(children.indexOf(child), 1);
children.splice(children.indexOf(child), 1);
}
}
}

createSVGElement(node, childs){
createSVGElement(node, childs) {
this.trimElementChilden(childs);
let componentAtts = {};
const i = ind++;
switch (node.nodeName) {
case 'svg':
componentAtts = this.obtainComponentAtts(node, SVG_ATTS);
if (this.props.width) {
componentAtts.width = this.props.width;
}
if (this.props.height) {
componentAtts.height = this.props.height;
}
case 'svg':
componentAtts = this.obtainComponentAtts(node, SVG_ATTS);
if (this.props.width) {
componentAtts.width = this.props.width;
}
if (this.props.height) {
componentAtts.height = this.props.height;
}

return <Svg key={i} {...componentAtts}>{childs}</Svg>;
case 'g':
componentAtts = this.obtainComponentAtts(node, G_ATTS);
return <G key={i} {...componentAtts}>{childs}</G>;
case 'path':
componentAtts = this.obtainComponentAtts(node, PATH_ATTS);
return <Path key={i} {...componentAtts}>{childs}</Path>;
case 'circle':
componentAtts = this.obtainComponentAtts(node, CIRCLE_ATTS);
return <Circle key={i} {...componentAtts}>{childs}</Circle>;
case 'rect':
componentAtts = this.obtainComponentAtts(node, RECT_ATTS);
return <Rect key={i} {...componentAtts}>{childs}</Rect>;
case 'line':
componentAtts = this.obtainComponentAtts(node, LINE_ATTS);
return <Line key={i} {...componentAtts}>{childs}</Line>;
case 'defs':
return <Defs key={i}>{childs}</Defs>;
case 'linearGradient':
componentAtts = this.obtainComponentAtts(node, LINEARG_ATTS);
return <LinearGradient key={i} {...componentAtts}>{childs}</LinearGradient>;
case 'radialGradient':
componentAtts = this.obtainComponentAtts(node, RADIALG_ATTS);
return <RadialGradient key={i} {...componentAtts}>{childs}</RadialGradient>;
case 'stop':
componentAtts = this.obtainComponentAtts(node, STOP_ATTS);
return <Stop key={i} {...componentAtts}>{childs}</Stop>;
case 'ellipse':
componentAtts = this.obtainComponentAtts(node, ELLIPSE_ATTS);
return <Ellipse key={i} {...componentAtts}>{childs}</Ellipse>;
case 'polygon':
componentAtts = this.obtainComponentAtts(node, POLYGON_ATTS);
return <Polygon key={i} {...componentAtts}>{childs}</Polygon>;
case 'polyline':
componentAtts = this.obtainComponentAtts(node, POLYLINE_ATTS);
return <Polyline key={i} {...componentAtts}>{childs}</Polyline>;
case 'text':
componentAtts = this.obtainComponentAtts(node, TEXT_ATTS);
if (componentAtts.y) {
componentAtts.y = fixYPosition(componentAtts.y, node)
}
return <Text key={i} {...componentAtts}>{childs}</Text>;
case 'tspan':
componentAtts = this.obtainComponentAtts(node, TEXT_ATTS);
if (componentAtts.y) {
componentAtts.y = fixYPosition(componentAtts.y, node)
}
return <TSpan key={i} {...componentAtts}>{childs}</TSpan>;
default:
return null;
return <Svg key={i} {...componentAtts}>{childs}</Svg>;
case 'g':
componentAtts = this.obtainComponentAtts(node, G_ATTS);
return <G key={i} {...componentAtts}>{childs}</G>;
case 'path':
componentAtts = this.obtainComponentAtts(node, PATH_ATTS);
return <Path key={i} {...componentAtts}>{childs}</Path>;
case 'circle':
componentAtts = this.obtainComponentAtts(node, CIRCLE_ATTS);
return <Circle key={i} {...componentAtts}>{childs}</Circle>;
case 'rect':
componentAtts = this.obtainComponentAtts(node, RECT_ATTS);
return <Rect key={i} {...componentAtts}>{childs}</Rect>;
case 'line':
componentAtts = this.obtainComponentAtts(node, LINE_ATTS);
return <Line key={i} {...componentAtts}>{childs}</Line>;
case 'defs':
return <Defs key={i}>{childs}</Defs>;
case 'use':
componentAtts = this.obtainComponentAtts(node, USE_ATTS);
if (undefined !== componentAtts['xlink:href']) //react-native-svg may not know about xlink
componentAtts.href = componentAtts['xlink:href'];
return <Use key={i} {...componentAtts} />;
case 'linearGradient':
componentAtts = this.obtainComponentAtts(node, LINEARG_ATTS);
return <LinearGradient key={i} {...componentAtts}>{childs}</LinearGradient>;
case 'radialGradient':
componentAtts = this.obtainComponentAtts(node, RADIALG_ATTS);
return <RadialGradient key={i} {...componentAtts}>{childs}</RadialGradient>;
case 'stop':
componentAtts = this.obtainComponentAtts(node, STOP_ATTS);
return <Stop key={i} {...componentAtts}>{childs}</Stop>;
case 'ellipse':
componentAtts = this.obtainComponentAtts(node, ELLIPSE_ATTS);
return <Ellipse key={i} {...componentAtts}>{childs}</Ellipse>;
case 'polygon':
componentAtts = this.obtainComponentAtts(node, POLYGON_ATTS);
return <Polygon key={i} {...componentAtts}>{childs}</Polygon>;
case 'polyline':
componentAtts = this.obtainComponentAtts(node, POLYLINE_ATTS);
return <Polyline key={i} {...componentAtts}>{childs}</Polyline>;
case 'text':
componentAtts = this.obtainComponentAtts(node, TEXT_ATTS);
if (componentAtts.y) {
componentAtts.y = fixYPosition(componentAtts.y, node)
}
return <Text key={i} {...componentAtts}>{childs}</Text>;
case 'tspan':
componentAtts = this.obtainComponentAtts(node, TEXT_ATTS);
if (componentAtts.y) {
componentAtts.y = fixYPosition(componentAtts.y, node)
}
return <TSpan key={i} {...componentAtts}>{childs}</TSpan>;
default:
return null;
}
}

Expand All @@ -228,7 +235,7 @@ class SvgUri extends Component{
}));
});

const componentAtts = Array.from(attributes)
const componentAtts = Array.from(attributes)
.map(utils.camelCaseNodeName)
.map(utils.removePixelsFromNodeValue)
.filter(utils.getEnabledAttributes(enabledAttributes.concat(COMMON_ATTS)))
Expand All @@ -241,7 +248,7 @@ class SvgUri extends Component{
return componentAtts;
}

inspectNode(node){
inspectNode(node) {
// Only process accepted elements
if (!ACCEPTED_SVG_ELEMENTS.includes(node.nodeName)) {
return null;
Expand All @@ -252,24 +259,24 @@ class SvgUri extends Component{

// if have children process them.
// Recursive function.
if (node.childNodes && node.childNodes.length > 0){
for (let i = 0; i < node.childNodes.length; i++){
const isTextValue = node.childNodes[i].nodeValue
if (isTextValue) {
arrayElements.push(node.childNodes[i].nodeValue)
} else {
const nodo = this.inspectNode(node.childNodes[i]);
if (nodo != null) {
arrayElements.push(nodo);
}
if (node.childNodes && node.childNodes.length > 0) {
for (let i = 0; i < node.childNodes.length; i++) {
const isTextValue = node.childNodes[i].nodeValue
if (isTextValue) {
arrayElements.push(node.childNodes[i].nodeValue)
} else {
const nodo = this.inspectNode(node.childNodes[i]);
if (nodo != null) {
arrayElements.push(nodo);
}
}
}
}

return this.createSVGElement(node, arrayElements);
}

render () {
render() {
try {
if (this.state.svgXmlData == null) {
return null;
Expand All @@ -284,12 +291,12 @@ class SvgUri extends Component{

const rootSVG = this.inspectNode(doc.childNodes[0]);

return(
<View style={this.props.style}>
{rootSVG}
</View>
return (
<View style={this.props.style}>
{rootSVG}
</View>
);
} catch(e){
} catch (e) {
console.error("ERROR SVG", e);
return null;
}
Expand Down