Skip to content

Commit 85cd5a1

Browse files
committed
Apply transforms directly to paths/gradients
This allows the intention behind commit c228ba4 to work once again. Note, this commit contains MPL code, so if this pull request is accepted an MPL license would need to be added to the repository.
1 parent 728c713 commit 85cd5a1

File tree

1 file changed

+82
-35
lines changed

1 file changed

+82
-35
lines changed

canvas2svg.js

+82-35
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,6 @@
373373
for (i = 0; i < keys.length; i++) {
374374
var key = keys[i]
375375
style = STYLES[key];
376-
377376
var attributeKey = style.apply
378377
if (!attributeKey || !attributeKey.includes(paintMethod)) {
379378
continue;
@@ -492,9 +491,7 @@
492491
var state = this.__getStyleState();
493492
this.__groupStack.push(parent);
494493
parent.appendChild(group);
495-
this.__applyTransform(group);
496494
this.__currentElement = group;
497-
this.__currentMatrix = new DOMMatrix();
498495
this.__stack.push(state);
499496
};
500497

@@ -506,9 +503,9 @@
506503
var state = this.__stack.pop();
507504

508505
/** Prune empty group created when running save/restore without any content **/
509-
var node = this.__currentElement
506+
var node = this.__currentElement;
510507
if (node.nodeName === 'g' && !node.childNodes.length) {
511-
node.remove()
508+
node.remove();
512509
}
513510

514511
this.__currentElementsToStyle = null;
@@ -533,9 +530,9 @@
533530
}
534531

535532
var styleId = style.__root.getAttribute("id");
536-
var linkedReferences = ++style.__linkedReferences
537-
var id = `${styleId}-${linkedReferences}`
538-
element.setAttribute(paintMethod, `url(#${id})`)
533+
var linkedReferences = ++style.__linkedReferences;
534+
var id = `${styleId}-${linkedReferences}`;
535+
element.setAttribute(paintMethod, `url(#${id})`);
539536

540537
var link = this.__createElement(style.__root.nodeName);
541538
link.setAttribute("id", id)
@@ -547,10 +544,11 @@
547544
link.setAttribute("patternTransform", matrix.toString());
548545
}
549546

550-
this.__defs.appendChild(link)
551-
} else {
552-
element.setAttribute("transform", matrix.toString());
547+
this.__defs.appendChild(link);
548+
return;
553549
}
550+
551+
element.setAttribute("transform", matrix.toString());
554552
};
555553

556554
/**
@@ -602,15 +600,6 @@
602600
this.__currentPosition = {};
603601
};
604602

605-
/**
606-
* Adds the move command to the current path element,
607-
* if the currentPathElement is not empty create a new path element
608-
*/
609-
ctx.prototype.moveTo = function (x, y) {
610-
// creates a new subpath with the given point
611-
setCurrentpath.call(this, x, y, `M ${x} ${y}`);
612-
};
613-
614603
/**
615604
* Closes the current path
616605
*/
@@ -620,28 +609,44 @@
620609
}
621610
};
622611

612+
/**
613+
* Adds the move command to the current path element,
614+
* if the currentPathElement is not empty create a new path element
615+
*/
616+
ctx.prototype.moveTo = function (x, y) {
617+
var point = point2d(this.__currentMatrix, x, y);
618+
// creates a new subpath with the given point
619+
setCurrentPath.call(this, point.x, point.y, `M ${point.x} ${point.y}`);
620+
};
621+
623622
/**
624623
* Adds a line to command
625624
*/
626625
ctx.prototype.lineTo = function (x, y) {
627-
setCurrentpath.call(this, x, y, `L ${x} ${y}`);
626+
var point = point2d(this.__currentMatrix, x, y);
627+
setCurrentPath.call(this, point.x, point.y, `L ${point.x} ${point.y}`);
628628
};
629629

630630
/**
631631
* Add a bezier command
632632
*/
633633
ctx.prototype.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {
634-
setCurrentpath.call(this, x, y, `C ${cp1x} ${cp1y} ${cp2x} ${cp2y} ${x} ${y}`);
634+
var point = point2d(this.__currentMatrix, x, y);
635+
var cp1 = point2d(this.__currentMatrix, cp1x, cp1y);
636+
var cp2 = point2d(this.__currentMatrix, cp2x, cp2y);
637+
setCurrentPath.call(this, point.x, point.y, `C ${cp1.x} ${cp1.y} ${cp2.x} ${cp2.y} ${point.x} ${point.y}`);
635638
};
636639

637640
/**
638641
* Adds a quadratic curve to command
639642
*/
640643
ctx.prototype.quadraticCurveTo = function (cpx, cpy, x, y) {
641-
setCurrentpath.call(this, x, y, `Q ${cpx} ${cpy} ${x} ${y}`);
644+
var point = point2d(this.__currentMatrix, x, y);
645+
var cp = point2d(this.__currentMatrix, cpx, cpy);
646+
setCurrentPath.call(this, point.x, point.y, `Q ${cp.x} ${cp.y} ${point.x} ${point.y}`);
642647
};
643648

644-
function setCurrentpath(x, y, value) {
649+
function setCurrentPath(x, y, value) {
645650
this.__currentPosition = {x, y};
646651
this.__currentPath || (this.__currentPath = "");
647652
this.__currentPath += value;
@@ -770,13 +775,13 @@
770775
function getOrCreateElementToApplyStyleTo(paintMethod1, paintMethod2) {
771776
var currentPath = this.__currentPath;
772777
if (!currentPath) {
773-
return
778+
return;
774779
}
775780

776781
var element = this.__currentElement;
777782
var group = this.__closestGroupOrSvg();
778783
var matrixString = this.__currentMatrix.toString();
779-
var state = group.__state || (group.__state = {})
784+
var state = group.__state || (group.__state = {});
780785
if (state[paintMethod1] || state[paintMethod2] && state.matrixString !== matrixString) {
781786
var pathHasNoChange = currentPath === state.currentPath;
782787
if (pathHasNoChange) {
@@ -979,12 +984,14 @@
979984
* Returns a canvas gradient object that has a reference to it's parent def
980985
*/
981986
ctx.prototype.createLinearGradient = function (x1, y1, x2, y2) {
987+
var point1 = point2d(this.__currentMatrix, x1, y1);
988+
var point2 = point2d(this.__currentMatrix, x2, y2);
982989
var grad = this.__createElement("linearGradient", {
983990
id : randomString(this.__ids),
984-
x1 : x1+"px",
985-
x2 : x2+"px",
986-
y1 : y1+"px",
987-
y2 : y2+"px",
991+
x1 : point1.x+"px",
992+
x2 : point2.x+"px",
993+
y1 : point1.y+"px",
994+
y2 : point2.y+"px",
988995
gradientUnits : "userSpaceOnUse"
989996
});
990997
this.__defs.appendChild(grad);
@@ -996,19 +1003,59 @@
9961003
* Returns a canvas gradient object that has a reference to it's parent def
9971004
*/
9981005
ctx.prototype.createRadialGradient = function (x0, y0, r0, x1, y1, r1) {
1006+
var scale = getCurrentScale.call(this)
1007+
var point0 = point2d(this.__currentMatrix, x0, y0);
1008+
var point1 = point2d(this.__currentMatrix, x1, y1);
9991009
var grad = this.__createElement("radialGradient", {
10001010
id : randomString(this.__ids),
1001-
cx : x1+"px",
1002-
cy : y1+"px",
1003-
r : r1+"px",
1004-
fx : x0+"px",
1005-
fy : y0+"px",
1011+
cx : point1.x+"px",
1012+
cy : point1.y+"px",
1013+
r : r1*scale+"px",
1014+
fx : point0.x+"px",
1015+
fy : point0.y+"px",
10061016
gradientUnits : "userSpaceOnUse"
10071017
});
10081018
this.__defs.appendChild(grad);
10091019
return new CanvasGradient(grad, this);
10101020
};
10111021

1022+
function getCurrentScale() {
1023+
// Extracted from: http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp
1024+
// MPL 1.1 license
1025+
1026+
const matrix = this.__currentMatrix
1027+
let scaleX = 0
1028+
let scaleY = 0
1029+
let skewX = 0
1030+
let a = matrix.a
1031+
let b = matrix.b
1032+
let c = matrix.c
1033+
let d = matrix.d
1034+
1035+
const determinant = a * d - b * c
1036+
if (determinant) {
1037+
/** Compute X scale factor and normalize first row **/
1038+
scaleX = Math.sqrt(a * a + b * b)
1039+
a /= scaleX
1040+
b /= scaleX
1041+
1042+
/** Compute shear factor and make 2nd row orthogonal to 1st **/
1043+
skewX = a * c + b * d
1044+
c -= a * skewX
1045+
d -= b * skewX
1046+
1047+
/** Compute Y scale **/
1048+
scaleY = Math.sqrt(c * c + d * d)
1049+
1050+
/** Negate **/
1051+
if (determinant < 0) {
1052+
scaleX = -scaleX
1053+
}
1054+
}
1055+
1056+
return Math.max(scaleX, scaleY)
1057+
};
1058+
10121059
/**
10131060
* Parses the font string and returns svg mapping
10141061
* @private

0 commit comments

Comments
 (0)