From 14bdacc2dfb94e4f0cd92e2fc468645659db9e6a Mon Sep 17 00:00:00 2001 From: Kendell R Date: Sun, 12 Nov 2023 05:52:01 -0800 Subject: [PATCH] feat(convertPathData): replace with z and remove z when optimal (#1822) --- docs/03-plugins/convert-path-data.mdx | 4 +++ plugins/convertPathData.js | 42 +++++++++++++++++++++++++-- plugins/plugins-types.ts | 3 +- test/coa/testSvg/test.1.svg | 2 +- test/coa/testSvg/test.svg | 2 +- test/plugins/convertPathData.12.svg | 8 ++--- test/plugins/convertPathData.13.svg | 2 +- test/plugins/convertPathData.14.svg | 6 ++-- test/plugins/convertPathData.18.svg | 2 +- test/plugins/convertPathData.19.svg | 2 +- test/plugins/convertPathData.20.svg | 2 +- test/plugins/convertPathData.27.svg | 13 +++++++++ test/svgo/plugins-order.svg | 2 +- 13 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 test/plugins/convertPathData.27.svg 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 @@ @@@ - - + + - - + + diff --git a/test/plugins/convertPathData.13.svg b/test/plugins/convertPathData.13.svg index d8c63aa2..84778eeb 100644 --- a/test/plugins/convertPathData.13.svg +++ b/test/plugins/convertPathData.13.svg @@ -5,5 +5,5 @@ @@@ - + diff --git a/test/plugins/convertPathData.14.svg b/test/plugins/convertPathData.14.svg index 69d59dc6..c2b72a9c 100644 --- a/test/plugins/convertPathData.14.svg +++ b/test/plugins/convertPathData.14.svg @@ -15,11 +15,11 @@ - + - + - + diff --git a/test/plugins/convertPathData.18.svg b/test/plugins/convertPathData.18.svg index e4af82d9..0cb5de76 100644 --- a/test/plugins/convertPathData.18.svg +++ b/test/plugins/convertPathData.18.svg @@ -5,5 +5,5 @@ @@@ - + diff --git a/test/plugins/convertPathData.19.svg b/test/plugins/convertPathData.19.svg index c57c8e28..03dfe0b6 100644 --- a/test/plugins/convertPathData.19.svg +++ b/test/plugins/convertPathData.19.svg @@ -5,5 +5,5 @@ @@@ - + diff --git a/test/plugins/convertPathData.20.svg b/test/plugins/convertPathData.20.svg index 2e00cbb4..3e880e63 100644 --- a/test/plugins/convertPathData.20.svg +++ b/test/plugins/convertPathData.20.svg @@ -5,5 +5,5 @@ @@@ - + diff --git a/test/plugins/convertPathData.27.svg b/test/plugins/convertPathData.27.svg new file mode 100644 index 00000000..ed257a42 --- /dev/null +++ b/test/plugins/convertPathData.27.svg @@ -0,0 +1,13 @@ + + + + + + +@@@ + + + + + + \ No newline at end of file diff --git a/test/svgo/plugins-order.svg b/test/svgo/plugins-order.svg index aecd05c0..038d3272 100644 --- a/test/svgo/plugins-order.svg +++ b/test/svgo/plugins-order.svg @@ -8,4 +8,4 @@ @@@ - +