-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Please add the option to flatten transforms #624
Comments
What does it do? If you mean applying transforms to paths then SVGO already does that when it possible. |
It flattens the transforms of all elements (including elliptical arcs, gradients, text and tspan) concatenating the cumulative transforms of all parents since the root node and applying the resulting matrix to the leaf elements (rects are converted to polygons to allow that). When not possible to completely eliminate the transform (for edge cases like some filters on groups), the children element is left with a transform matrix that includes all transformations of the parents, so that the matrix multiplications required to get the the element local coordinates are minimized. |
Well, SVGO does something of that. |
It does a very little of this, actually. Transforms in the output file are still too many compared to Affinity Designer exported svg file. |
It'd be more helpful if you provide such examples. Also, there was a bunch of bugs with incorrectly moved transforms, so one need to be careful with them. |
Here's some examples of what I think @Emasoft is talking about: Input: <svg>
<g transform="translate(2.240000, 10.453333)">
<rect fill="#000" x="210.56" y="94.359" width="22.811" height="33.2" rx="3.733"/>
<rect fill="#FFF" x="210.56" y="94.359" width="22.811" height="33.2" rx="3.733"/>
</g>
</svg> Output: <svg>
<g transform="translate(2.24 10.453)">
<rect x="210.56" y="94.359" width="22.811" height="33.2" rx="3.733"/>
<rect fill="#FFF" x="210.56" y="94.359" width="22.811" height="33.2" rx="3.733"/>
</g>
</svg> Should be: <svg>
<rect x="212.80" y="104.812" width="22.811" height="33.2" rx="3.733"/>
<rect fill="#FFF" x="212.80" y="104.812" width="22.811" height="33.2" rx="3.733"/>
</svg> Input: <svg>
<g transform="translate(2.240000, 10.453333)">
<rect fill="#000" x="210.56" y="94.359" width="22.811" height="33.2" rx="3.733"/>
</g>
</svg> Output: <svg>
<rect x="210.56" y="94.359" width="22.811" height="33.2" rx="3.733" transform="translate(2.24 10.453)"/>
</svg> Should be: <svg>
<rect x="212.80" y="104.812" width="22.811" height="33.2" rx="3.733"/>
</svg> |
Any update on this? |
I'm planning such an operation, but no updates yet. |
This comment was marked as spam.
This comment was marked as spam.
How long before this is implemented? This is a much needed feature. Too much time wasted in doing this manually every day. |
I'm afraid not so soon. It's trivial enough, but I have no time for this. |
@GreLI I hope you get time to get this important feature implemented very sooooooooooooon. |
would also be useful to convert svg graphics to simple paths, which can easily be used in react-native-svg |
seems theres an existing package https://github.com/stadline/svg-flatten to do it, maybe someone could transform this into an svgo plugin? btw the package does not seem to handle filters, maybe we should add a clause in the recursion to flatten the transform onto the filter as well. besides filters, are there any other cases where we might need to recurse transform flattening beyond a nodes children? masks maybe? |
I wrote code to flatten transforms for my own project, in case anyone wants to port it over. It doesn't quite support all possible transforms on all shapes yet, but it's a start. |
Another day, another starting point, my fork https://github.com/lemnis/svgo has support of removing |
This comment was marked as spam.
This comment was marked as spam.
@nashwaan Be grateful that other people are willing to use their precious time to create and extend code that everybody can use. Otherwise, you have 2 options, pay lots of money more for all software you use or create the code yourself. Or maybe the last solution is the best for you, keep waiting patiently. 😉 |
@lemnis Thank you for educating me and telling me I should be grateful to the open source community. |
I've had the same issue, it would be really nice to be able to flatten everything. |
+1 this would be awesome. |
I have this issue too, and I'd swear it used to work better. But currently SVGO doesn't "flatten" or precalculate even very simple transforms. I have examples where there's a One very common source of unnecessary transformations stems from the behavior of design software for working with SVGs. They tend to be very literal. If you click the "path" tool, you get a As the OP mentions, this is just noise--there's no reason whatsoever to preserve a transformation of that kind and so files that preserve this are missing an obvious if relatively minor optimization. But in my situation this leads to real issues. I have a use case where we need to convert our icons to Android's Vector Drawable format. We work with SVG as the "origin" format and convert to Vector Drawable after optimizing with SVGO. Vector Drawables are a little touchy, it seems (we had issues if we optimized away leading And, it turns out, these preserved translations also cause issues. They're unsupported or just ignored in the conversion process and result in the icon being mis-aligned and cut off relative to the Example SVGs, before and afterBefore <?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 51 (57462) - http://www.bohemiancoding.com/sketch -->
<title>icon/UI 24px/Vision Small (eye)</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="icon/UI-24px/Vision-Small-(eye)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linejoin="round">
<g id="Export" transform="translate(0.750000, 6.000000)" stroke="#000000" stroke-width="1.5">
<circle id="Oval" cx="11.1290323" cy="5.93548387" r="5.86870968"></circle>
<path d="M11.25,2.69999981 C13.0693329,2.69999981 14.5441935,4.1748605 14.5441935,5.99419336" id="Shape"></path>
<path d="M22.25,6 C19.7671057,2.32106779 15.5674223,0.0515191927 11.1290323,0.0515191927 C6.69064219,0.0515191927 2.54224915,2.25655166 0.0593548387,5.93548387 C2.34284932,9.31404472 6.0448217,11.4641714 10.110942,11.7735029 C14.1770623,12.0828345 18.0141602,10.4943237 20.93,7.52322581 C21.3881491,7.05639433 21.8281491,6.5486524 22.25,6 Z" id="Shape"></path>
</g>
</g>
</svg> After
Before <?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 51 (57462) - http://www.bohemiancoding.com/sketch -->
<title>icon/Display 32px/Vision (eye)</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="icon/Display-32px/Vision-(eye)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linejoin="round">
<g id="Export" transform="translate(1.000000, 8.000000)" stroke="#000000" stroke-width="1.5">
<circle id="Oval" cx="15" cy="8" r="7.91"></circle>
<path d="M15,3.56 C17.4521443,3.56 19.44,5.54785571 19.44,8" id="Shape"></path>
<path d="M30.3862847,8.58371311 C30.3073441,8.50705295 30.2439761,8.42555519 30.1961806,8.33921984 C30.1483851,8.25288448 30.0563249,8.1398112 29.92,8 C26.5734903,3.04143919 20.9821779,0.0694389119 15,0.0694389119 C9.01782208,0.0694389119 3.42650973,3.04143919 0.08,8 C3.15775344,12.5537124 8.14736837,15.4517092 13.6277914,15.8686344 C19.1082144,16.2855596 24.4788024,14.175719 28.21,10.14 C28.2774066,10.0677786 28.3279616,10.0136126 28.3616649,9.97750186 C28.3901838,9.94694589 28.4422526,9.89716988 28.5178711,9.82817383" id="Shape"></path>
</g>
</g>
</svg> After <?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" display="block" pointer-events="none" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linejoin="round" stroke-width="1.5" transform="translate(1 8)">
<circle cx="15" cy="8" r="7.91"/>
<path d="M15 3.56A4.44 4.44 0 0 1 19.44 8m10.946.584a1.052 1.052 0 0 1-.19-.245c-.048-.086-.14-.2-.276-.339A18 18 0 0 0 .08 8a18 18 0 0 0 28.13 2.14l.152-.162c.028-.031.08-.08.156-.15"/>
</g>
</svg> Before <?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 51 (57462) - http://www.bohemiancoding.com/sketch -->
<title>icon/UI 24px/Pin (map marker, location)</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="icon/UI-24px/Pin-(map-marker,-location)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Export" transform="translate(4.125000, 1.750000)" stroke="#000000" stroke-width="1.5">
<path d="M15.75,7.875 C15.75,14.625 7.875,20.25 7.875,20.25 C7.875,20.25 0,14.625 0,7.875 C0,3.4875 3.4875,0 7.875,0 C12.2625,0 15.75,3.4875 15.75,7.875 Z" id="Shape"></path>
<circle id="Oval" cx="7.875" cy="7.75" r="3.25"></circle>
</g>
</g>
</svg> After <?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" display="block" pointer-events="none" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-width="1.5" transform="translate(4.125 1.75)">
<path d="M15.75 7.875c0 6.75-7.875 12.375-7.875 12.375S0 14.625 0 7.875A7.827 7.827 0 0 1 7.875 0a7.827 7.827 0 0 1 7.875 7.875z"/>
<circle cx="7.875" cy="7.75" r="3.25"/>
</g>
</svg> Before <?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 51 (57462) - http://www.bohemiancoding.com/sketch -->
<title>icon/Display 32px/Prescription Generic (pharmacy, medicine, bottle, Rx)</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="icon/Display-32px/Prescription-Generic-(pharmacy,-medicine,-bottle,-Rx)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Export" transform="translate(6.000000, 1.750000)" stroke="#000000" stroke-width="1.5">
<path d="M2.06,0.08 L17.94,0.08 C18.4253462,0.08 18.8908138,0.272803018 19.2340054,0.61599459 C19.577197,0.959186163 19.77,1.42465382 19.77,1.91 L19.77,5 L0.229972639,5 L0.23,1.92 C0.22733834,1.43292351 0.4189657,0.964882708 0.762446184,0.619525281 C1.10592667,0.274167854 1.57291623,0.0799927277 2.06,0.08 Z" id="Shape" stroke-linejoin="round"></path>
<path d="M0.23,13 L19.77,13" id="Shape" stroke-linejoin="round"></path>
<path d="M19.77,24.5 L0.23,24.5" id="Shape" stroke-linejoin="round"></path>
<path d="M2.75,7.24 L2.75,7.95 C2.75114043,8.94951168 2.22325652,9.89667602 1.45,10.53 C0.679451144,11.1611045 0.231855477,12.1039905 0.23,13.1 L0.23,28.5 L19.7700022,28.5 L19.77,13.1 C19.7711404,12.1004883 19.3232565,11.153324 18.55,10.52 C17.7767435,9.88667602 17.2488596,8.93951168 17.25,7.94 L17.25,7.24" id="Shape" stroke-linejoin="round"></path>
<circle id="Oval-3" cx="10" cy="18.75" r="2.75"></circle>
</g>
</g>
</svg> After <?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" display="block" pointer-events="none" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-width="1.5" transform="translate(6 1.75)">
<path stroke-linejoin="round" d="M2.06.08h15.88a1.83 1.83 0 0 1 1.83 1.83V5H.23V1.92A1.83 1.83 0 0 1 2.06.08zM.23 13h19.54m0 11.5H.23M2.75 7.24v.71c.001 1-.527 1.947-1.3 2.58A3.33 3.33 0 0 0 .23 13.1v15.4h19.54V13.1a3.33 3.33 0 0 0-1.22-2.58c-.773-.633-1.301-1.58-1.3-2.58v-.7"/>
<circle cx="10" cy="18.75" r="2.75"/>
</g>
</svg> Before <?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 51 (57462) - http://www.bohemiancoding.com/sketch -->
<title>icon/UI 24px/Search (magnifying glass, find, discover)</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="icon/UI-24px/Search-(magnifying-glass,-find,-discover)" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Export" transform="translate(1.000000, 1.000000)" stroke="#000000" stroke-width="1.5">
<circle id="Oval" cx="8.5" cy="8.5" r="7.75"></circle>
<path d="M21,21 L13.9821656,13.9821656" id="Shape"></path>
</g>
</g>
</svg> After <?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" display="block" pointer-events="none" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-width="1.5" transform="translate(1 1)">
<circle cx="8.5" cy="8.5" r="7.75"/>
<path d="M21 21l-7.018-7.018"/>
</g>
</svg> SVGO configconst hash = require('string-hash');
module.exports = {
full: true,
multipass: true,
precision: 3,
// order of plugins is important to correct functionality
plugins: [
{ removeDoctype: true },
{ removeXMLProcInst: true },
{ removeComments: true },
{ removeMetadata: true },
{ removeXMLNS: false },
{ removeEditorsNSData: true },
{ cleanupAttrs: true },
{ inlineStyles: true },
{ minifyStyles: true },
{ convertStyleToAttrs: true },
{ cleanupIDs: true },
{
prefixIds: {
prefix: function(element, filePath) {
const fileNameId = hash(
filePath
.split('/')
.slice(-2)
.join('/')
);
return `i${fileNameId}`;
}
}
},
{ removeRasterImages: true },
{ removeUselessDefs: true },
{ cleanupNumericValues: true },
{ cleanupListOfValues: true },
{ convertColors: { currentColor: true } },
{ removeUnknownsAndDefaults: true },
{ removeNonInheritableGroupAttrs: true },
{ removeUselessStrokeAndFill: true },
{ removeViewBox: false },
{ cleanupEnableBackground: true },
{ removeHiddenElems: true },
{ removeEmptyText: true },
{ convertShapeToPath: true },
{ moveElemsAttrsToGroup: true },
{ moveGroupAttrsToElems: true },
{ collapseGroups: true },
{ convertPathData: true },
{ convertTransform: true },
{ removeEmptyAttrs: true },
{ removeEmptyContainers: true },
{ mergePaths: true },
{ removeUnusedNS: true },
{ sortAttrs: true },
{ removeTitle: true },
{ removeDesc: true },
{ removeDimensions: false },
{ removeAttrs: false },
{ removeElementsByAttr: false },
{ addClassesToSVGElement: false },
{ removeStyleElement: true },
{ removeScriptElement: true },
{
addAttributesToSVGElement: {
attributes: [{ display: 'block' }, { 'pointer-events': 'none' }]
}
}
],
js2svg: {
pretty: false,
indent: ''
}
}; Could be this works better with some configurations than others, but I'm sure we're all aware that SVGO can be a bit challenging to configure, so if anyone has some tips on that, please share, it could help in some cases. (One configuration possibility that I tried was not doubling up on |
@Emasoft, recently I made this repo, just like SVGOMG but with Vue. I added flatten svg option, whose code was provided by Timo in his gist (with some modifications). It is still in experimental stage. It worked on the transforms of most of the paths (except some text and clipaths). So can anyone check that option and give your opinions. Thank you all. |
@upendra-web Superb work with lean-SVG ! |
Thank you very much @andrewrcollins |
Lean-SVG still doesn't work with rect and circle. |
If you are using Inkscape:
In all cases I have tried, this has removed any transform attributes, then I can run through SVGO without having to worry about transforms. Not sure if it works for all SVG, but certainly the ones I have tried so far. |
@maxwell8888 this works well for some elements, but not all unfortunately. Some very tricky ones are:
I realise that the status of this is not likely to change, but I would love to know if it does (or if anyone finds a suitable workaround). |
I've been able to apply transforms to path elements manually using stadline/svg-flatten provided that the transform attribute was associated with the path element itself (and not a group) by running in the nodejs interpreter (inside the project directory): var flatten = require('./')
var svg = fs.readFileSync('./logo.svg')
flatten(svg).transform().value().toString() However, this doesn't work as far as preserving the gradients if they have been defined with |
I really hope this eventually comes in SVGO, I'm relying on Lean-SVG for my day-to-day work right now, but I'd love SVGO to handle everything. I think most of the debate earlier was about the reliability of the transforms, but as long as it's clear that it's an opt-in one, I can't see why not. |
Could we perhaps use Inkscape CLI to ungroup elements with a transform attribute on the group, then regroup them without the transform? That should help remove the transforms. |
I had an auto-generated svg with a I was able to accomplish this manually by using either of these tools: If you don't need an automated solution, these both offer a quick way to apply computations to path data. |
BTW, it's good starter task, if someone would like to help. |
Most of the raw SVG files have translations applied to groups of paths. During the build process, only paths are extracted. As a result, many icons are misaligned. Unfortunately, svgo [doesn't support flattening transforms](svg/svgo#624) yet, so I’ve done this manually.
Most of the raw SVG files have translations applied to groups of paths. During the build process, only paths are extracted. As a result, many icons are misaligned. Unfortunately, svgo [doesn't support flattening transforms](svg/svgo#624) yet, so I’ve done this manually.
Most of the raw SVG files have translations applied to groups of paths. During the build process, only paths are extracted. As a result, many icons are misaligned. Unfortunately, svgo [doesn't support flattening transforms](svg/svgo#624) yet, so I’ve done this manually.
I just thought about solving this here: I am not saying that this solves this problem for svgo. |
The solution above only works with translate transforms... A better solution, also for matrix transforms: Inkscape: Solution: GPT-4 told me this: THEN: then just search with empty input, it'll select all |
Has this been solved now? |
@mattsputnikdigital PRs are welcome |
#1854 may handle this? |
There are multiple implementations out there, but still, it would be really nice to have in svgo as well. |
"Flatten Transforms" option is available in SVG editors like Affinity Designer (~$40 / Mac) and it is very useful. It also improve performances of SVG rendering because coordinate transforms are precalculated.
Please add this option to SVGO.
The text was updated successfully, but these errors were encountered: