From f94e17e3be7ee023375e0379d84900b0aa5ad0ab Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Fri, 1 Dec 2017 03:32:16 +0200 Subject: [PATCH] Implement correct transform parser (#507) --- lib/extract/extractProps.js | 17 +-- lib/extract/extractTransform.js | 212 +++++++++++++++++++++++--------- package.json | 5 +- 3 files changed, 160 insertions(+), 74 deletions(-) diff --git a/lib/extract/extractProps.js b/lib/extract/extractProps.js index c1be5c44..edd1ad33 100644 --- a/lib/extract/extractProps.js +++ b/lib/extract/extractProps.js @@ -1,6 +1,6 @@ import extractFill from './extractFill'; import extractStroke from './extractStroke'; -import extractTransform, {props2transform, tp} from './extractTransform'; +import extractTransform, {props2transform} from './extractTransform'; import extractClipPath from './extractClipPath'; import extractResponder from './extractResponder'; import extractOpacity from './extractOpacity'; @@ -27,21 +27,6 @@ export default function(props, ref) { extractedProps.matrix = extractTransform(props); Object.assign(extractedProps, props2transform(props)); - let transform = props.transform; - if (transform) { - if (typeof transform === 'string') { - const transformParsed = tp.parse(transform); - if (transformParsed.matrix) { - // TODO: Extract scaling values for coordinate system - // Especially scaleY for calculating scaling of fontSize - } else { - let trans = props2transform(transformParsed); - if (typeof trans === 'object') { - Object.assign(extractedProps, trans); - } - } - } - } Object.assign(extractedProps, extractResponder(props, ref)); diff --git a/lib/extract/extractTransform.js b/lib/extract/extractTransform.js index 6224200f..44146ab2 100644 --- a/lib/extract/extractTransform.js +++ b/lib/extract/extractTransform.js @@ -1,6 +1,7 @@ import Matrix2D from '../Matrix2D'; import _ from 'lodash'; let pooledMatrix = new Matrix2D(); +import peg from 'pegjs'; function transformToMatrix(props, transform) { pooledMatrix.reset(); @@ -13,73 +14,172 @@ function transformToMatrix(props, transform) { return pooledMatrix.toArray(); } -const SPLIT_REGEX = /[\s*()|,]/; +const transformParser = peg.generate(` +{ + const deg2rad = Math.PI / 180; -class TransformParser { + /* + ╔═ ═╗ ╔═ ═╗ ╔═ ═╗ + ║ al cl el ║ ║ ar cr er ║ ║ a c e ║ + ║ bl dl fl ║ * ║ br dr fr ║ = ║ b d f ║ + ║ 0 0 1 ║ ║ 0 0 1 ║ ║ 0 0 1 ║ + ╚═ ═╝ ╚═ ═╝ ╚═ ═╝ + */ + function multiply_matrices(l, r) { + const [al, cl, el, bl, dl, fl] = l; + const [ar, cr, er, br, dr, fr] = r; - parse(transform) { - if (transform) { - const retval = {}; - let transLst = _.filter( - transform.split(SPLIT_REGEX), - (ele) => { - return ele !== ''; - } - ); - for (let i = 0; i < transLst.length; i++) { - let trans = transLst[i]; - switch (trans) { - case 'matrix': - if (i + 7 <= transLst.length) { - retval.matrix = _.map((transLst.slice(i + 1,i + 7)), parseFloat); - } - break; - case 'translate': - retval.translateX = transLst[i + 1]; - retval.translateY = (transLst.length === 3) ? transLst[i + 2] : 0; - break; - case 'scale': - retval.scaleX = transLst[i + 1]; - retval.scaleY = (transLst.length === 3) ? transLst[i + 2] : retval.scaleX; - break; - case 'rotate': - retval.rotation = transLst[i + 1]; - retval.originX = (transLst.length > 2) ? transLst[i + 2] : retval.originX; - retval.originY = (transLst.length > 3) ? transLst[i + 3] : retval.originY; - break; - case 'skewX': - retval.skewX = transLst[i + 1]; - break; - case 'skewY': - retval.skewY = transLst[i + 1]; - break; - default: - break; - } - } - return retval; - } + const a = al * ar + cl * br; + const c = al * cr + cl * dr; + const e = al * er + cl * fr + el; + const b = bl * ar + dl * br; + const d = bl * cr + dl * dr; + const f = bl * er + dl * fr + fl; + + return [a, c, e, b, d, f]; } } -export const tp = new TransformParser(); +transformList + = wsp* ts:transforms? wsp* { return ts; } +transforms + = t:transform commaWsp+ ts:transforms + { + return multiply_matrices(t, ts); + } + / t:transform + +transform + = matrix + / translate + / scale + / rotate + / skewX + / skewY + +matrix + = "matrix" wsp* "(" wsp* + a:number commaWsp + b:number commaWsp + c:number commaWsp + d:number commaWsp + e:number commaWsp + f:number wsp* ")" + { + return [ + a, c, e, + b, d, f + ]; + } + +translate + = "translate" wsp* "(" wsp* tx:number ty:commaWspNumber? wsp* ")" + { + return [ + 1, 0, tx, + 0, 1, ty || 0 + ]; + } + +scale + = "scale" wsp* "(" wsp* sx:number sy:commaWspNumber? wsp* ")" + { + return [ + sx, 0, 0, + 0, sy === null ? sx : sy, 0 + ]; + } + +rotate + = "rotate" wsp* "(" wsp* angle:number c:commaWspTwoNumbers? wsp* ")" + { + const cos = Math.cos(deg2rad * angle); + const sin = Math.sin(deg2rad * angle); + if (c !== null) { + const [x, y] = c; + return [ + cos, -sin, cos * -x + -sin * -y + x, + sin, cos, sin * -x + cos * -y + y + ]; + } + return [ + cos, -sin, 0, + sin, cos, 0 + ]; + } + +skewX + = "skewX" wsp* "(" wsp* angle:number wsp* ")" + { + return [ + 1, Math.tan(deg2rad * angle), 0, + 0, 1, 0 + ]; + } + +skewY + = "skewY" wsp* "(" wsp* angle:number wsp* ")" + { + return [ + 1, 0, 0, + Math.tan(deg2rad * angle), 1, 0 + ]; + } + +number + = f:(sign? floatingPointConstant) { return parseFloat(f.join("")); } + / i:(sign? integerConstant) { return parseInt(i.join("")); } + +commaWspNumber + = commaWsp n:number { return n; } + +commaWspTwoNumbers + = commaWsp n1:number commaWsp n2:number { return [n1, n2]; } + +commaWsp + = (wsp+ comma? wsp*) / (comma wsp*) + +comma + = "," + +integerConstant + = ds:digitSequence { return ds.join(""); } + +floatingPointConstant + = fractionalConstant exponent? + / digitSequence exponent + + fractionalConstant "fractionalConstant" + = d1:digitSequence? "." d2:digitSequence { return [d1 ? d1.join("") : null, ".", d2.join("")].join(""); } + / d:digitSequence "." { return d.join(""); } + +exponent + = [eE] sign? digitSequence + +sign + = [+-] + +digitSequence + = digit+ + +digit + = [0-9] + +wsp + = [\\u0020\\u0009\\u000D\\u000A] +`); function appendTransform(transform) { if (transform) { if (typeof transform === 'string') { - const transformParsed = tp.parse(transform); - if (transformParsed.matrix) { - pooledMatrix.append(...transformParsed.matrix); + try { + const [a, c, e, b, d, f] = transformParser.parse(transform); + pooledMatrix.append(...[a, b, c, d, e, f]); + } catch (e) { + console.error(e); } - else { - let trans = props2transform(transformParsed); - if (typeof trans !== 'string') { - transform = trans; - } - } - } - if (typeof transform !== 'string') { + } else { pooledMatrix .appendTransform( transform.x + transform.originX, diff --git a/package.json b/package.json index d7dfb389..9e67cde6 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ ], "scripts": { "lint": "eslint ./", - "postinstall" : "node scripts/install.js" + "postinstall": "node scripts/install.js" }, "peerDependencies": { "react-native": ">=0.50.0", @@ -29,8 +29,9 @@ }, "dependencies": { "color": "^2.0.1", + "github-download": "^0.5.0", "lodash": "^4.16.6", - "github-download": "^0.5.0" + "pegjs": "^0.10.0" }, "devDependencies": { "babel-eslint": "^6.1.2",