Created
February 26, 2019 16:11
-
-
Save scottmas/1f662871ed60043bb14a9af4a3fcd3b0 to your computer and use it in GitHub Desktop.
Hack to support vector-effect: non-scaling-stroke
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/**** | |
* | |
* A super clunky svg file to react component converter very similar in spirit to the somewhat popular svgr library (https://www.npmjs.com/package/svgr/v/1.4.0) | |
* But we have the additional requirement of needing to scale the svg while __preserving__ the stroke width. And unfortunately | |
* react-native-svg does not support the only config value that would enable this. Namely, the property `vector-effect="non-scaling-stroke"`. | |
* Follow the issue here: https://github.com/react-native-community/react-native-svg/issues/885 | |
* Eventually, once the issue is resolved, we should move to svgr and simply use the svg `scale` property | |
*/ | |
const path = require("path"); | |
const cheerio = require("cheerio"); | |
const _ = require("lodash"); | |
const fs = require("fs"); | |
const filename = process.argv[2]; | |
if (!path.isAbsolute(filename)) { | |
throw new Error("Must pass absolute path"); | |
} | |
if (path.extname(filename).toLowerCase() !== ".svg") { | |
throw new Error("File must be an svg file"); | |
} | |
const svgString = String(fs.readFileSync(filename)); | |
const START_DELIM = "STARRRRRRTTTT"; | |
const END_DELIM = "ENDDDDDDDDD"; | |
const Y_SCALE = "yScaleFactor"; | |
const X_SCALE = "xScaleFactor"; | |
const $ = cheerio.load(svgString, { | |
xmlMode: true, | |
recognizeSelfClosing: true, | |
lowerCaseTags: false, | |
lowerCaseAttributeNames: false | |
}); | |
$("*").each(function(i, elem) { | |
elem.tagName = elem.tagName[0].toUpperCase() + elem.tagName.slice(1); | |
elem.attribs = Object.keys(elem.attribs).reduce((acc, attr) => { | |
let val = elem.attribs[attr]; | |
if (attr === "width" || attr === "x") { | |
val = `\${${X_SCALE} * ${val}}`; | |
} | |
if (attr === "height" || attr === "y") { | |
val = `\${${Y_SCALE} * ${val}}`; | |
} | |
if (attr === "viewBox") { | |
val = val | |
.split(" ") | |
.map((d, i) => { | |
const scale = i % 2 === 0 ? X_SCALE : Y_SCALE; | |
return `\${${d} * ${scale}}`; | |
}) | |
.join(" "); | |
} | |
if (attr === "transform") { | |
val = val | |
.split(/(\(.+?\))/) | |
.filter(a => a) | |
.map((d, i, arr) => { | |
if (i % 2 === 0) { | |
return d; | |
} | |
const transformType = arr[i - 1]; | |
if (transformType === "matrix") { | |
console.error("matrix transforms are not supported. Too tricky for my blood..."); | |
} | |
if (transformType === "rotate") { | |
const points = d.substr(1, d.length - 2).split(" "); | |
return `(${points[0]} \${${points[1]} * ${X_SCALE}} \${${points[2] || 0} * ${Y_SCALE}})`; | |
} | |
if (transformType === "scale") { | |
const points = d.substr(1, d.length - 2).split(" "); | |
return `(\${${points[0]} * ${X_SCALE}} \${${points[1] || points[0]} * ${Y_SCALE}})`; | |
} | |
if (transformType === "skewX") { | |
const points = d.substr(1, d.length - 2).split(" "); | |
return `(\${${points[0]} * ${X_SCALE}})`; | |
} | |
if (transformType === "skewY") { | |
const points = d.substr(1, d.length - 2).split(" "); | |
return `(\${${points[0]} * ${Y_SCALE}})`; | |
} | |
return d; | |
}) | |
.join(""); | |
} | |
if (attr === "d") { | |
const strings = val.split(/([a-zA-Z])/).filter(a => a); | |
const newDirectives = strings | |
.map((str, i) => { | |
if (i % 2 === 0) { | |
return str; | |
} | |
const directive = strings[i - 1]; | |
if (directive.toLowerCase() === "v") { | |
return `\${${Y_SCALE} * ${str}}`; | |
} | |
if (directive.toLowerCase() === "h") { | |
return `\${${X_SCALE} * ${str}}`; | |
} | |
const newStrVal = str | |
.split(" ") | |
.map((num, i) => { | |
const scale = i % 2 === 0 ? X_SCALE : Y_SCALE; | |
return `\${${scale} * ${num}}`; | |
}) | |
.join(" "); | |
return newStrVal; | |
}) | |
.join(""); | |
val = newDirectives; | |
} | |
acc[_.camelCase(attr)] = `${START_DELIM}${val}${END_DELIM}`; | |
return acc; | |
}, {}); | |
}); | |
const reactComponentStr = $.html() | |
.replace(new RegExp(`"${START_DELIM}`, "g"), "{`") | |
.replace(new RegExp(`${END_DELIM}"`, "g"), "`}"); | |
const basename = path.basename(filename).replace(/\.svg/i, ""); | |
const componentStr = ` | |
/** STOP! Do not edit this file. Instead generate it by running | |
* \`node generate-svg-component-from-file.js ${filename}\` | |
*/ | |
import React from "react"; | |
import Svg,{ Circle, Ellipse, G, Text, TSpan, TextPath, Path, Polygon, Polyline, Line, Rect, Use, Image, Symbol, Defs, LinearGradient, RadialGradient, Stop, ClipPath, Pattern, Mask } from "react-native-svg"; | |
export function ${basename}({${X_SCALE}, ${Y_SCALE}}){ | |
return ( | |
${reactComponentStr} | |
) | |
} | |
`; | |
fs.writeFileSync(`${path.dirname(filename)}/${basename}.js`, componentStr); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment