I have a SVG that contains a single <path>
element that draws a certain shape. The coordinates of this path are contained within the path's 'd'
attribute's value. I need this shape flipped horizontally. When I try to accomplish this in Adobe Illustrator, using Reflect tool for example, I get double the size of data in the 'd'
attribute value and therefore double the size of SVG file and that is just too painful to do. I could use transform
and scale
functions to flip the shape without changing the coordinates in 'd'
but then I would increase the rendering time and CPU usage since I added extra work for browser or whichever software renders the SVG.
The logical thing to do is just change the coordinates themselves within the 'd'
to their 'opposites' to achieve the flipping of the shape.
I could write a script that does this but alas I do not know the format of how these coordinates are stored and what they actually represent. There are both letters and numbers used.
So, my question is, how would one change the coordinates of a <path>
element's 'd'
in order to achieve a horizontal flip of the entire shape?
Here is my example SVG for illustration:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px">
<path id="example" class="st0" d="M492 534h-96q-37 0 -53.5 -12.5t-30.5 -50.5q-20 -54 -44 -165q-17 -79 -17 -105q0 -49 38 -58q17 -3 51 -3h67q41 0 56 7.5t23 32.5h27l-24 -106q-10 -42 -27 -58q-18 -15 -50 -19t-139 -4q-89 0 -128 5.5t-63 21.5q-54 35 -54 122q0 53 25 177q31 146 62 218t76 101 t124 29h258l-18 -80q-7 -34 -19 -43.5t-44 -9.5z"/>
</svg>
Here you have an example of how the path can be flipped. I do it in two different functions because moveElm()
could be useful in other situations. So, first flipping the path over the y-axes and then moving the path back over the y-axes.
For both functions the mathematics is not that hard, it is basically just a matter of understanding what the different commands in the d attribute does. The implementation is not complete, but it works for the example given in the question.
The question did not say anything about what programming language you would like to use. The only language specific I used in the example is getBBox(). In Python you can find something similar in the package svgpathtools where you can find the method path.bbox()
(see, this answer: https://stackoverflow.com/a/76076555/322084). Finding the position and size of the path is necessary for moving (and scaling) the path.
let path = document.getElementById('example');
let path_bbox = path.getBBox();
let x = Math.round(path_bbox.width + 2 * path_bbox.x);
flipElm(path);
moveElm(path, x, 0);
function moveElm(path_elm, x, y){
let d = path_elm.getAttribute('d');
let regexp = /([a-zA-Z])([\s\d\-\.]*)/g;
let new_d = [...d.matchAll(regexp)].map(command => {
let arr = command[2].trim().split(/\s/).map(val => parseFloat(val));
let return_arr = arr;
switch(command[1]){
case 'M':
case 'L':
case 'H':
case 'V':
case 'Q':
case 'T':
return_arr = [arr[0] + x, arr[1] + y];
break;
case 'z':
case 'Z':
return_arr = [];
break;
}
return `${command[1]}${return_arr.join(' ')}`;
}).join(' ');
path_elm.setAttribute('d', new_d);
}
function flipElm(path_elm) {
let d = path_elm.getAttribute('d');
let regexp = /([a-zA-Z])([\s\d\-\.]*)/g;
let new_d = [...d.matchAll(regexp)].map(command => {
let arr = command[2].trim().split(/\s/).map(val => parseFloat(val));
let return_arr = [];
switch (command[1]) {
case 'A':
case 'a':
return_arr = arr.map((num, i) => "not implemented");
break;
case 'z':
case 'Z':
return_arr = [];
break;
default:
return_arr = arr.map((num, i) => (i % 2) ? num : num * -1);
break;
}
return `${command[1]}${return_arr.join(' ')}`;
}).join(' ');
path_elm.setAttribute('d', new_d);
}
svg {
border: solid thin black;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -10 600 680" height="200">
<path class="st0" d="M492 534h-96q-37 0 -53.5 -12.5t-30.5 -50.5q-20 -54 -44 -165q-17 -79 -17 -105q0 -49 38 -58q17 -3 51 -3h67q41 0 56 7.5t23 32.5h27l-24 -106q-10 -42 -27 -58q-18 -15 -50 -19t-139 -4q-89 0 -128 5.5t-63 21.5q-54 35 -54 122q0 53 25 177q31 146 62 218t76 101 t124 29h258l-18 -80q-7 -34 -19 -43.5t-44 -9.5z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -10 600 680" height="200">
<path id="example" class="st0" d="M492 534h-96q-37 0 -53.5 -12.5t-30.5 -50.5q-20 -54 -44 -165q-17 -79 -17 -105q0 -49 38 -58q17 -3 51 -3h67q41 0 56 7.5t23 32.5h27l-24 -106q-10 -42 -27 -58q-18 -15 -50 -19t-139 -4q-89 0 -128 5.5t-63 21.5q-54 35 -54 122q0 53 25 177q31 146 62 218t76 101 t124 29h258l-18 -80q-7 -34 -19 -43.5t-44 -9.5z"/>
</svg>