-
Notifications
You must be signed in to change notification settings - Fork 64
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
Parse and validate metadata for stretchable icons from SVGs #68
Conversation
Missing:
|
|
lib/extract-svg-metadata.js
Outdated
const applyPlugins = require('svgo/lib/svgo/plugins'); | ||
const computePathBounds = require('svg-boundings').path; | ||
|
||
const extractMapboxIconMetadata = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should consider add more test fixtures to identify the limit of which kinds of SVGs this code will handle, and consider throwing errors in cases where we get SVGs that we can't handle well (nested transforms, groups, different element types). I can forsee difficult to debug cases cropping up in the future if we don't get on top of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks pretty good but I think we should add more thorough test coverage and afterwards, see if might make sense to do some stricter validation of elements with mapbox-icon
.
I will post some illustrator svgs for you that match the affinity svgs!
@kkaefer here's a fixture I just made in illustrator. I think I did it right: <?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<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"
width="18px" height="18px" viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
<rect x="3" y="3" style="fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;" width="12" height="12"/>
<rect x="4" y="4" style="fill:#00FFEE;" width="10" height="4"/>
<rect id="mapbox-icon-content" x="4" y="8" style="fill:none;" width="10" height="6"/>
<rect id="mapbox-icon-stretch-y" x="13" y="8" style="fill:none;" width="1" height="6"/>
<rect id="mapbox-icon-stretch-x" x="4" y="13" style="fill:none;" width="10" height="1"/>
</svg>
|
What kind of test coverage are you referring to? There are already a lot of tests for validation; do you think we should add more SVGs? In that case, I think we'd need a larger variety of icons designed for being stretched that we can test this with. Where do you propose we'd get these from? Questions from mapbox/mapbox-gl-js#8917:
The code resolves transforms prior to computing the bounding boxes. I tested
The code converts all shapes to paths, and computes the bounding box of the path. We could change it to be restricted to just |
Turns out svgo skips transform folding if elements have an |
673253d
to
01b55f3
Compare
Yep that’s what I meant - in my experience SVGO doesn’t always work exactly the way I expect it to. I can make a few more fixtures for you! |
Here are some weird SVGs, food for thought. A shield where each element is individually rotated. Results in a shield that is rotated 45 degrees. <?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<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"
width="18px" height="18px" viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
<g>
<rect x="4.5" y="4.5" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -3.7279 9)" style="fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;" width="8.9" height="8.9"/>
<rect x="9.1" y="3.7" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -2.1473 9.6547)" style="fill:#00FFEE;" width="3" height="7.5"/>
<rect id="mapbox-icon-content" x="5.7" y="6.3" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -4.7817 8.5635)" style="fill:none;" width="4.5" height="7.5"/>
<rect id="mapbox-icon-stretch-y" x="8.1" y="12.1" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -5.7637 10.9344)" style="fill:none;" width="4.5" height="0.7"/>
<rect id="mapbox-icon-stretch-x" x="6.3" y="7.6" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -6.0988 8.0179)" style="fill:none;" width="0.7" height="7.5"/>
</g>
</svg> A shield inside two groups, one with a rotation, one with a translate. Results in a shield that is rotated 45 degrees. <?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<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"
width="18px" height="18px" viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
<g transform="rotate(45)">
<g transform="translate(4 -8)">
<rect x="5" y="5" style="fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;" width="8" height="8"/>
<rect x="6" y="6" style="fill:#00FFEE;" width="6" height="2"/>
<rect id="mapbox-icon-content" x="6" y="8" style="fill:none;" width="6" height="4"/>
<rect id="mapbox-icon-stretch-y" x="11" y="8" style="fill:none;" width="1" height="4"/>
<rect id="mapbox-icon-stretch-x" x="6" y="11" style="fill:none;" width="6" height="1"/>
</g>
</g>
</svg> A shield inside two groups, one with a rotation, one with the reverse rotation. Results in a normal-looking shield: <?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<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"
width="18px" height="18px" viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
<g transform="rotate(45)">
<g transform="rotate(-45)">
<rect x="5" y="5" style="fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;" width="8" height="8"/>
<rect x="6" y="6" style="fill:#00FFEE;" width="6" height="2"/>
<rect id="mapbox-icon-content" x="6" y="8" style="fill:none;" width="6" height="4"/>
<rect id="mapbox-icon-stretch-y" x="11" y="8" style="fill:none;" width="1" height="4"/>
<rect id="mapbox-icon-stretch-x" x="6" y="11" style="fill:none;" width="6" height="1"/>
</g>
</g>
</svg> |
@samanpwbb is that the verbatim output from Illustrator or did you modify them after the export? |
The last two are modified, first is directly from illustrator. Do you think these are edge cases that we shouldn’t worry about? |
Ok, I was wondering whether Illustrator will actually put transforms into exported SVGs, but it looks like it does in some circumstances. Affinity seems to resolve them prior to exporting. |
01b55f3
to
c848928
Compare
@samanpwbb Thanks for the SVGs. I incorportated them into the test suite and changed our parsing so that transforms, including multiple nested transforms on groups, are resolved correctly. The SVGs' stretch zones don't really make a lot of sense given that the entire icon is rotated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great.
One thing: This new extractMetadata
function is doing a lot of work: traversing SVGs multiple times, converting all shapes to path, then simplifying all paths for every SVG.
Is there a chance we could see a performance hit when we add this to the API? There are optimizations we could make, for example, by adding a property to certain icons to denote whether they should be searched for metadata or skipped. Studio could surface a checkbox that enables/disables extraction. Probably best to get this merged, and circle back around to performance optimizations if they're needed.
Yeah, it can take a few millisecond to process an icon. If we're looking for optimizations, #7 has probably a higher impact. In any case, sprite generation should be an infrequent operation. |
c848928
to
af06f40
Compare
This is ready to be merged pending the discussion in mapbox/mapbox-gl-js#8917 (comment) |
Implements metadata parsing from SVGs for stretchable icons.