Skip to content

Commit ce258ef

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. This works for my project, but might not work for yours.
1 parent 728c713 commit ce258ef

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)