Skip to content

Commit

Permalink
-For certain elements (most notably "path"), modify approach to build…
Browse files Browse the repository at this point in the history
… new "d" attribute string as code iterates through the points on the element, rather than storing the new point in s.x and s.y. This is because the setter for s.x and s.y in the pathsegpolyfill utility is very expensive and leads to massive slowdown on large/complex paths.

-Support transformations for clipPath tags (including those defined within "def" tags)
-Support preservation of ids and classes through transformation
-Support transformations for line elements
  • Loading branch information
Povolny, Johnny authored and Povolny, Johnny committed Jan 3, 2019
1 parent 285ae61 commit 55ff0d5
Showing 1 changed file with 85 additions and 19 deletions.
104 changes: 85 additions & 19 deletions svgparser.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@
transformString = globalTransform + transformString;

var transform, scale, rotate;

if(transformString && transformString.length > 0){
var transform = this.transformParse(transformString);
}

if(!transform){
transform = new Matrix();
}
Expand All @@ -226,15 +226,18 @@
// decompose affine matrix to rotate, scale components (translate is just the 3rd column)
var rotate = Math.atan2(tarray[1], tarray[3])*180/Math.PI;
var scale = Math.sqrt(tarray[0]*tarray[0]+tarray[2]*tarray[2]);
if(element.tagName == 'g' || element.tagName == 'svg'){

if(element.tagName == 'g' || element.tagName == 'svg' || element.tagName == 'defs' || element.tagName == 'clipPath' ||){
element.removeAttribute('transform');
var children = Array.prototype.slice.call(element.children);
for(var i=0; i<children.length; i++){
this.applyTransform(children[i], transformString);
}
}
else if(transform && !transform.isIdentity()){
const id = element.getAttribute('id')
const className = element.getAttribute('class')

switch(element.tagName){
case 'ellipse':
// the goal is to remove the transform property, but an ellipse without a transform will have no rotation
Expand Down Expand Up @@ -263,14 +266,17 @@
var seglist = element.pathSegList;
var prevx = 0;
var prevy = 0;


let transformedPath = '';

for(var i=0; i<seglist.numberOfItems; i++){
var s = seglist.getItem(i);
var command = s.pathSegTypeAsLetter;


if(command == 'H'){
seglist.replaceItem(element.createSVGPathSegLinetoAbs(s.x,prevy),i);
s = seglist.getItem(i);
s = seglist.getItem(i);
}
else if(command == 'V'){
seglist.replaceItem(element.createSVGPathSegLinetoAbs(prevx,s.y),i);
Expand All @@ -282,27 +288,74 @@
seglist.replaceItem(element.createSVGPathSegArcAbs(s.x,s.y,s.r1*scale,s.r2*scale,s.angle+rotate,s.largeArcFlag,s.sweepFlag),i);
s = seglist.getItem(i);
}

const transPoints = {};

if('x' in s && 'y' in s){
var transformed = transform.calc(s.x, s.y);
prevx = s.x;
prevy = s.y;

s.x = transformed[0];
s.y = transformed[1];
transPoints.x = transformed[0];
transPoints.y = transformed[1];
}
if('x1' in s && 'y1' in s){
var transformed = transform.calc(s.x1, s.y1);
s.x1 = transformed[0];
s.y1 = transformed[1];
transPoints.x1 = transformed[0];
transPoints.y1 = transformed[1];
}
if('x2' in s && 'y2' in s){
var transformed = transform.calc(s.x2, s.y2);
s.x2 = transformed[0];
s.y2 = transformed[1];
transPoints.x2 = transformed[0];
transPoints.y2 = transformed[1];
}

let commandStringTransformed = ``;

//MLHVCSQTA
//H and V are transformed to "L" commands above so we don't need to handle them. All lowercase (relative) are already handled too (converted to absolute)
switch(command) {
case 'M':
commandStringTransformed += `${command} ${transPoints.x} ${transPoints.y}`;
break;
case 'L':
commandStringTransformed += `${command} ${transPoints.x} ${transPoints.y}`;
break;
case 'C':
commandStringTransformed += `${command} ${transPoints.x1} ${transPoints.y1} ${transPoints.x2} ${transPoints.y2} ${transPoints.x} ${transPoints.y}`;
break;
case 'S':
commandStringTransformed += `${command} ${transPoints.x2} ${transPoints.y2} ${transPoints.x} ${transPoints.y}`;
break;
case 'Q':
commandStringTransformed += `${command} ${transPoints.x1} ${transPoints.y1} ${transPoints.x} ${transPoints.y}`;
break;
case 'T':
commandStringTransformed += `${command} ${transPoints.x} ${transPoints.y}`;
break;
case 'A':
const largeArcFlag = s.largeArcFlag ? 1 : 0;
const sweepFlag = s.sweepFlag ? 1 : 0;
commandStringTransformed += `${command} ${s.r1} ${s.r2} ${s.angle} ${largeArcFlag} ${sweepFlag} ${transPoints.x} ${transPoints.y}`
break;
case 'H':
commandStringTransformed += `L ${transPoints.x} ${transPoints.y}`
break;
case 'V':
commandStringTransformed += `L ${transPoints.x} ${transPoints.y}`
break;
case 'Z':
case 'z':
commandStringTransformed += command;
break;
default:
console.log('FOUND COMMAND NOT HANDLED BY COMMAND STRING BUILDER', command);
break;
}

transformedPath += commandStringTransformed;
}


element.setAttribute('d', transformedPath);
element.removeAttribute('transform');
break;
case 'circle':
Expand All @@ -313,11 +366,17 @@
// skew not supported
element.setAttribute('r', element.getAttribute('r')*scale);
break;

case 'line':
const transformedStartPt = transform.calc(element.getAttribute('x1'), element.getAttribute('y1'));
const transformedEndPt = transform.calc(element.getAttribute('x2'), element.getAttribute('y2'));
element.setAttribute('x1', transformedStartPt[0].toString());
element.setAttribute('y1', transformedStartPt[1].toString());
element.setAttribute('x2', transformedEndPt[0].toString());
element.setAttribute('y2', transformedEndPt[1].toString());
break;
case 'rect':
// similar to the ellipse, we'll replace rect with polygon
var polygon = this.svg.createElementNS(element.namespaceURI, 'polygon');


var p1 = this.svgRoot.createSVGPoint();
var p2 = this.svgRoot.createSVGPoint();
Expand Down Expand Up @@ -348,19 +407,26 @@

element.parentElement.replaceChild(polygon, element);
element = polygon;

case 'polygon':
case 'polyline':
let transformedPoly = ''
for(var i=0; i<element.points.length; i++){
var point = element.points[i];
var transformed = transform.calc(point.x, point.y);
point.x = transformed[0];
point.y = transformed[1];
const pointPairString = `${transformed[0]},${transformed[1]} `;
transformedPoly += pointPairString;
}

element.setAttribute('points', transformedPoly);
element.removeAttribute('transform');
break;
}
if(id) {
element.setAttribute('id', id);
}
if(className){
element.setAttribute('class', className);
}
}
}

Expand Down

0 comments on commit 55ff0d5

Please sign in to comment.