diff --git a/docs/03-plugins/convert-path-data.mdx b/docs/03-plugins/convert-path-data.mdx
index e68e89ef..9aea92cf 100644
--- a/docs/03-plugins/convert-path-data.mdx
+++ b/docs/03-plugins/convert-path-data.mdx
@@ -18,6 +18,9 @@ svgo:
lineShorthands:
description: If to convert regular lines to an explicit horizontal or vertical line where possible.
default: true
+ convertToZ:
+ description: If to convert lines that go to the start to a z command.
+ default: true
curveSmoothShorthands:
description: If to convert curves to smooth curves where possible.
default: true
@@ -55,6 +58,7 @@ This plugin uses multiple techniques to either reduce the number of instructions
* Convert between relative or absolute coordinates, whichever is shortest.
* Convert between commands. For example, a bézier curve that behaves like a straight line might as well use a line instruction.
+* Remove redundant commands. For example, a command that moves to the current position can be removed.
* Trim redundant delimiters and leading zeros.
* Round numeric values using conventional rounding rules.
diff --git a/plugins/convertPathData.js b/plugins/convertPathData.js
index f9e07c42..6aab8347 100644
--- a/plugins/convertPathData.js
+++ b/plugins/convertPathData.js
@@ -46,6 +46,7 @@ let arcTolerance;
* },
* straightCurves: boolean,
* lineShorthands: boolean,
+ * convertToZ: boolean,
* curveSmoothShorthands: boolean,
* floatPrecision: number | false,
* transformPrecision: number,
@@ -95,6 +96,7 @@ exports.fn = (root, params) => {
},
straightCurves = true,
lineShorthands = true,
+ convertToZ = true,
curveSmoothShorthands = true,
floatPrecision = 3,
transformPrecision = 5,
@@ -116,6 +118,7 @@ exports.fn = (root, params) => {
makeArcs,
straightCurves,
lineShorthands,
+ convertToZ,
curveSmoothShorthands,
floatPrecision,
transformPrecision,
@@ -167,6 +170,12 @@ exports.fn = (root, params) => {
(computedStyle['stroke-linecap'].type === 'dynamic' ||
computedStyle['stroke-linecap'].value !== 'butt');
const maybeHasStrokeAndLinecap = maybeHasStroke && maybeHasLinecap;
+ const isSafeToUseZ = maybeHasStroke
+ ? computedStyle['stroke-linecap']?.type === 'static' &&
+ computedStyle['stroke-linecap'].value === 'round' &&
+ computedStyle['stroke-linejoin']?.type === 'static' &&
+ computedStyle['stroke-linejoin'].value === 'round'
+ : true;
var data = path2js(node);
@@ -175,6 +184,7 @@ exports.fn = (root, params) => {
convertToRelative(data);
data = filters(data, newParams, {
+ isSafeToUseZ,
maybeHasStrokeAndLinecap,
hasMarkerMid,
});
@@ -371,10 +381,14 @@ const convertToRelative = (pathData) => {
* @type {(
* path: PathDataItem[],
* params: InternalParams,
- * aux: { maybeHasStrokeAndLinecap: boolean, hasMarkerMid: boolean }
+ * aux: { isSafeToUseZ: boolean, maybeHasStrokeAndLinecap: boolean, hasMarkerMid: boolean }
* ) => PathDataItem[]}
*/
-function filters(path, params, { maybeHasStrokeAndLinecap, hasMarkerMid }) {
+function filters(
+ path,
+ params,
+ { isSafeToUseZ, maybeHasStrokeAndLinecap, hasMarkerMid }
+) {
var stringify = data2Path.bind(null, params),
relSubpoint = [0, 0],
pathBase = [0, 0],
@@ -664,6 +678,20 @@ function filters(path, params, { maybeHasStrokeAndLinecap, hasMarkerMid }) {
}
}
+ // convert going home to z
+ // m 0 0 h 5 v 5 l -5 -5 -> m 0 0 h 5 v 5 z
+ if (
+ params.convertToZ &&
+ (isSafeToUseZ || next?.command === 'Z' || next?.command === 'z') &&
+ (command === 'l' || command === 'h' || command === 'v')
+ ) {
+ // @ts-ignore
+ if (pathBase[0] === item.coords[0] && pathBase[1] === item.coords[1]) {
+ command = 'z';
+ data = [];
+ }
+ }
+
// collapse repeated commands
// h 20 h 30 -> h 50
if (
@@ -806,6 +834,16 @@ function filters(path, params, { maybeHasStrokeAndLinecap, hasMarkerMid }) {
if (prev.command === 'Z' || prev.command === 'z') return false;
prev = item;
}
+ if (
+ (command === 'Z' || command === 'z') &&
+ params.removeUseless &&
+ isSafeToUseZ &&
+ // @ts-ignore
+ item.base[0] === item.coords[0] &&
+ // @ts-ignore
+ item.base[1] === item.coords[1]
+ )
+ return false;
return true;
});
diff --git a/plugins/plugins-types.ts b/plugins/plugins-types.ts
index a3f8852f..f872a8d5 100644
--- a/plugins/plugins-types.ts
+++ b/plugins/plugins-types.ts
@@ -42,6 +42,7 @@ type DefaultPlugins = {
};
straightCurves?: boolean;
lineShorthands?: boolean;
+ convertToZ?: boolean;
curveSmoothShorthands?: boolean;
floatPrecision?: number | false;
transformPrecision?: number;
@@ -138,7 +139,7 @@ type DefaultPlugins = {
moveElemsAttrsToGroup: void;
moveGroupAttrsToElems: void;
removeComments: {
- preservePatterns: Array | false
+ preservePatterns: Array | false;
};
removeDesc: {
removeAny?: boolean;
diff --git a/test/coa/testSvg/test.1.svg b/test/coa/testSvg/test.1.svg
index 6e9bd764..bd829c7e 100644
--- a/test/coa/testSvg/test.1.svg
+++ b/test/coa/testSvg/test.1.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/test/coa/testSvg/test.svg b/test/coa/testSvg/test.svg
index 6e9bd764..bd829c7e 100644
--- a/test/coa/testSvg/test.svg
+++ b/test/coa/testSvg/test.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/test/plugins/convertPathData.12.svg b/test/plugins/convertPathData.12.svg
index fb497b2e..ae147195 100644
--- a/test/plugins/convertPathData.12.svg
+++ b/test/plugins/convertPathData.12.svg
@@ -18,12 +18,12 @@
@@@