Skip to content

Commit c78a508

Browse files
committed
Create <def> for Paths with multiple fills or strokes
This patch creates smaller exported svgs by creating <def> entries for paths references more than one. This kicks into gear when a fill or stroke is used multiple times on a single path.
1 parent c8d7985 commit c78a508

File tree

1 file changed

+62
-34
lines changed

1 file changed

+62
-34
lines changed

canvas2svg.js

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -774,66 +774,94 @@
774774
* Sets fill properties on the current element
775775
*/
776776
ctx.prototype.fill = function () {
777-
var element = this.__currentElement;
778-
var matrixString = this.__currentMatrix.toString();
777+
var element = getOrCreateElementToApplyStyleTo.call(this, "fill", "stroke");
779778

780-
var group = this.__closestGroupOrSvg();
781-
if (group.__hasFill || group.__hasStroke && group.__matrixString !== matrixString) {
782-
element = this.__currentElement = this.__createElement("path", {}, true);
783-
group.appendChild(element);
779+
/** `fillRule` could be first or second argument: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fill **/
780+
if (arguments[0] === "evenodd" || arguments[1] === "evenodd") {
781+
element.setAttribute("fill-rule", "evenodd");
784782
}
785783

786-
if (element.nodeName === "path") {
787-
element.setAttribute("paint-order", "stroke fill markers");
788-
789-
/** `fillRule` could be first or second argument: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fill **/
790-
if (arguments[0] === "evenodd" || arguments[1] === "evenodd") {
791-
element.setAttribute("fill-rule", "evenodd");
792-
}
793-
}
794-
795-
this.__applyCurrentDefaultPath();
796784
this.__applyStyleToCurrentElement("fill");
797785
this.__applyTransform(element, "fill");
798-
799-
group.__hasFill = true;
800-
group.__matrixString = matrixString;
801786
};
802787

803788
/**
804789
* Sets the stroke property on the current element
805790
*/
806791
ctx.prototype.stroke = function () {
792+
var element = getOrCreateElementToApplyStyleTo.call(this, "stroke", "fill");
793+
794+
this.__applyStyleToCurrentElement("stroke");
795+
this.__applyTransform(element, "stroke");
796+
};
797+
798+
function getOrCreateElementToApplyStyleTo(paint1, paint2) {
807799
var element = this.__currentElement;
808800
var matrixString = this.__currentMatrix.toString();
809801

810802
var group = this.__closestGroupOrSvg();
811-
if (group.__hasStroke || group.__hasFill && group.__matrixString !== matrixString) {
812-
element = this.__currentElement = this.__createElement("path", {}, true);
813-
group.appendChild(element);
803+
var extras = group.__extras || (group.__extras = {})
804+
var currentPath = this.__currentDefaultPath;
805+
806+
if (extras[paint1] || extras[paint2] && extras.matrixString !== matrixString) {
807+
var pathHasNotChanged = currentPath === extras.currentPath;
808+
if (pathHasNotChanged) {
809+
if (element.nodeName === "path") {
810+
convertPathToDef.call(this, group);
811+
}
812+
element = appendUseElement.call(this, group, extras.id);
813+
} else {
814+
element = this.__currentElement = this.__createElement("path", {}, true);
815+
group.appendChild(element);
816+
this.__applyCurrentDefaultPath();
817+
}
818+
} else {
819+
this.__applyCurrentDefaultPath();
814820
}
815821

822+
element.setAttribute("paint-order", `${paint2} ${paint1} markers`);
823+
824+
extras[paint1] = true;
825+
extras.currentPath = currentPath;
826+
extras.matrixString = matrixString;
827+
828+
return element;
829+
};
830+
831+
function convertPathToDef(group, id) {
832+
var element = this.__currentElement;
833+
834+
/** Create <path> <def> **/
835+
var id = group.__extras.id = randomString(this.__ids);
836+
var link = this.__createElement("path");
837+
link.setAttribute("id", id);
838+
link.setAttribute("d", element.getAttribute("d"));
839+
this.__defs.appendChild(link);
840+
841+
/** Convert previous <path> to <use> **/
816842
if (element.nodeName === "path") {
817-
element.setAttribute("paint-order", "fill stroke markers");
843+
element.remove();
844+
var attributes = element.attributes;
845+
element = appendUseElement.call(this, group, id);
846+
for (var i = 0; i < attributes.length; i ++) {
847+
var attribute = attributes[i];
848+
if (attribute.name === "d") continue;
849+
element.setAttribute(attribute.name, attribute.value);
850+
}
818851
}
852+
};
819853

820-
this.__applyCurrentDefaultPath();
821-
this.__applyStyleToCurrentElement("stroke");
822-
this.__applyTransform(element, "stroke");
823-
824-
group.__hasStroke = true;
825-
group.__matrixString = matrixString;
854+
function appendUseElement(group, id) {
855+
var element = this.__currentElement = this.__createElement("use", {}, true);
856+
element.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", `#${id}`);
857+
group.appendChild(element);
858+
return element;
826859
};
827860

828861
/**
829862
* Adds a rectangle to the path.
830863
*/
831864
ctx.prototype.rect = function (x, y, width, height) {
832-
// var group = this.__closestGroupOrSvg();
833-
// if (group.__hasFill || group.__hasStroke) {
834-
// var element = this.__currentElement = this.__createElement("path", {}, true);
835-
// group.appendChild(element);
836-
// }
837865
this.moveTo(x, y);
838866
this.lineTo(x+width, y);
839867
this.lineTo(x+width, y+height);

0 commit comments

Comments
 (0)