Skip to content
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

It seems that fill-rule="evenodd" does not works correctly #62

Open
megahertz opened this issue Dec 22, 2016 · 21 comments · May be fixed by #139
Open

It seems that fill-rule="evenodd" does not works correctly #62

megahertz opened this issue Dec 22, 2016 · 21 comments · May be fixed by #139

Comments

@megahertz
Copy link

megahertz commented Dec 22, 2016

I have the following SVG:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg 
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  width="16"
  height="16"
>
  <symbol id="icon-search-16" viewBox="0 0 16 16" preserveAspectRatio="xMidYMin slice" width="100%">
      <path class="path1" fill="currentColor" fill-rule="evenodd" d="M6.50015 0c3.5902,0 6.50015,2.90995 6.50015,6.50015 0,1.36286 -0.418069,2.62651 -1.13493,3.6705l3.72837 3.79923c0.434603,0.485385 0.602303,1.15264 0.0791261,1.67582 -0.524358,0.525539 -1.1928,0.494833 -1.71715,-0.0295247l-3.75317 -3.77325c-1.05108,0.729849 -2.32654,1.15737 -3.70239,1.15737 -3.5902,0 -6.50015,-2.90995 -6.50015,-6.50015 0,-3.5902 2.90995,-6.50015 6.50015,-6.50015zm0 1.99941c2.48479,0 4.49956,2.01476 4.49956,4.49956 0,2.48479 -2.01476,4.49956 -4.49956,4.49956 -2.48479,0 -4.49956,-2.01476 -4.49956,-4.49956 0,-2.48479 2.01476,-4.49956 4.49956,-4.49956z"></path>
  </symbol>
  <use xlink:href="#icon-search-16" />
</svg> 

The result is:

<?xml version="1.0" standalone="no"?> 
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
  <font id="iconfont" horiz-adv-x="16">
    <font-face font-family="iconfont"
      units-per-em="16" ascent="16"
      descent="0" />
    <missing-glyph horiz-adv-x="0" />
    <glyph glyph-name="1"
      unicode="&#xEA01;"
      horiz-adv-x="16" d=" M6.50015 16C10.09035 16 13.0003 13.09005 13.0003 9.49985C13.0003 8.13699 12.582231 6.87334 11.86537 5.82935L15.59374 2.03012C16.028343 1.544735 16.196043 0.87748 15.6728661 0.3543C15.1485081 -0.171239 14.4800661 -0.140533 13.9557161 0.3838247L10.2025461 4.1570747C9.1514661 3.4272257 7.8760061 2.9997047 6.5001561 2.9997047C2.9099561 2.9997047 0.0000061 5.9096547 0.0000061 9.4998547C0.0000061 13.0900547 2.9099561 16.0000047 6.5001561 16.0000047zM6.50015 14.00059C8.98494 14.00059 10.99971 11.98583 10.99971 9.50103C10.99971 7.01624 8.98495 5.00147 6.50015 5.00147C4.01536 5.00147 2.00059 7.01623 2.00059 9.50103C2.00059 11.98582 4.01535 14.00059 6.50015 14.00059z" />
  </font>
</defs>
</svg>

It seems that a property fill-rule="evenodd" does not used in converted font. Is it possible to convert it correctly, or it requires a lot of work?

PS: Thank you for the awesome library.

@nfroidure
Copy link
Owner

No idea to be honnest. I guess i'll need to dive into SVG specs to get the shape of the amount of work required. It could bz fasr if it only requires path transformations but the name of the peoperty is more about path merge/combination which is new to me. There maybe are some libs to do that already.

But you have to know i am not so active on those projects since i do not use icon fonts anymore so this may never happen. That said i'm open to add new collaborators that may want to help maintaining this project.

@megahertz
Copy link
Author

Thank you Nicolas. I just wanted to know whether it's easy or not.

@nfroidure
Copy link
Owner

nfroidure commented Dec 23, 2016

No problem. I'll just leave this issue open for others being aware of the problem.

@nfroidure nfroidure reopened this Dec 23, 2016
@Giovancruz
Copy link

I think i'm facing simmilar issue. I'm using gulp-iconfont to render svgs as font. I have the following SVG:
image

<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="300" height="330" viewBox="0 0 300 330">
  <defs>
    <style> .cls-1 {fill-rule: evenodd; } </style>
  </defs>
  <path id="Composite_Path" data-name="Composite Path" fill-rule="evenodd" class="cls-1" d="M273.474,220.754a21.79,21.79,0,1,0-21.79,21.8A21.8,21.8,0,0,0,273.474,220.754Zm-246.948,0a21.79,21.79,0,1,1,21.79,21.8A21.8,21.8,0,0,1,26.526,220.754Zm60.148,22.029,0.092-36.042c0-4.425,3.9-8.016,8.7-8.016H204.534c4.8,0,8.7,3.591,8.7,8.016l0.093,36.042H86.674ZM37.309,10C23.361,10,12.083,20.468,12.083,33.4L12.012,161.421l26.316-9.432,0.106-31.883c0-24.527,12.677-38.341,40.117-38.341H221.447c27.441,0,40.118,13.813,40.118,38.341l0.106,31.883,26.317,9.432L287.917,33.4c0-12.927-11.278-23.4-25.226-23.4H37.309ZM215.78,111.222c7.641,0,13.923,5.447,14.546,12.373l-0.155,48.037H69.829L69.674,123.6c0.622-6.926,6.9-12.373,14.546-12.373H215.78ZM84.632,98.117c-14.73,0-26.824,10.5-28.017,23.86L56.49,165.024,12,183.459l0.055,87.258H27.849l-0.007,29.07C27.842,311.5,34.7,321,50.722,321c11.212,0,19.383-10.408,19.383-22.122l-0.03-29.069h159.85l-0.03,29.069c0,11.714,8.171,22.122,19.383,22.122,16.025,0,22.88-9.5,22.88-21.213l-0.007-29.07h15.794L288,183.459l-44.49-18.435-0.125-43.047c-1.193-13.356-13.286-23.86-28.017-23.86H84.632Z"/>
</svg>

In gulp i'm using normalize: true, fontHeight: 1001, and centerHorizontally: true options.

The convertion ignore the fill-rule, and the font generate with the following appearence:
image

I forgot something in SVG file?

@jwilson8767
Copy link

jwilson8767 commented Mar 27, 2019

@nfroidure It looks like this is an essential feature for compound paths. Here's a doc: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule

And here's an example svg I've been having trouble with:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="400px" height="400px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
    <rect id="socioeconomics" x="0.988" y="0" width="400" height="400" style="fill:none;"/>
    <path d="M385.603,188.852l-64.265,-55.425l0,-83.346c0,-3.961 -3.259,-7.22 -7.221,-7.22l-33.696,0c-3.961,0 -7.221,3.259 -7.221,7.22l0,43.692l-53.873,-44.323c-10.621,-8.741 -26.084,-8.741 -36.705,0l-166.294,139.42c-1.659,1.371 -2.62,3.414 -2.62,5.566c0,1.68 0.586,3.309 1.657,4.603l15.344,18.654c1.371,1.667 3.419,2.634 5.577,2.634c1.678,0 3.305,-0.585 4.599,-1.653l155.517,-130.557c2.677,-2.158 6.529,-2.158 9.206,0l155.505,130.539c1.294,1.071 2.923,1.658 4.603,1.658c2.152,0 4.195,-0.962 5.566,-2.621l15.344,-18.654c1.062,-1.291 1.642,-2.912 1.642,-4.584c0,-2.15 -0.959,-4.189 -2.665,-5.603Z" style="fill-rule:nonzero;"/>
    <path d="M195.677,112.748l-128.03,105.447l0,126.065c0,6.095 5.016,11.111 11.111,11.111l77.817,-0.201c6.074,-0.031 88.829,-0.014 88.829,-0.014l77.789,0.215c6.095,0 11.111,-5.016 11.111,-11.111l0,-126.141l-128.002,-105.371c-3.09,-2.49 -7.536,-2.49 -10.625,0Zm18.011,123.769l0,-57.15c0,-3.508 -2.841,-6.349 -6.35,-6.35l-12.7,0c-3.509,0.001 -6.35,2.842 -6.35,6.35l0,57.15l-57.15,0c-3.509,0 -6.35,2.842 -6.35,6.35l0,12.7c0,3.508 2.841,6.35 6.35,6.35l57.15,0l0,57.15c0,3.508 2.842,6.35 6.35,6.35l12.7,0c3.509,-0.001 6.35,-2.842 6.35,-6.35l0,-57.15l57.15,0c3.509,0 6.35,-2.842 6.35,-6.35l0,-12.7c0,-3.508 -2.841,-6.35 -6.35,-6.35l-57.15,0Z"/>
</svg>

I tried both Affinity Designer and Gravit for making this icon and exporting it and in both cases ran into issues. FontForge has a tool for "Correct Direction" which appears to convert "evenodd" paths to "nonzero" if that helps you figure out how to implement this.

edit: here is the code behind that "Correct Direction" tool: https://github.com/fontforge/fontforge/blob/ae2f40b2f213b2d645d5a9f1e7a55475cbfe84ea/fontforge/scripting.c#L5505 (line 5505)

@Coding-Kiwi
Copy link

facing the same issue, any updates?

@jwilson8767
Copy link

@Coding-Kiwi No, this still seems to be an issue. I have stopped using SVGO and instead opted for -- get this -- uploading svgs to the icomoon App and then re-downloading them. It does a fairly decent job of flattening the image and dealing with winding order issues. I don't think they've open sourced whatever they're using to clean svgs, but I wish they would.

@Coding-Kiwi
Copy link

@jwilson8767 guess who is currently reading through obfuscated icomoon app javascript lol

@Coding-Kiwi
Copy link

I uhm.. researched a bit, like totally not reverse engineering icomoon code because that would be against their terms.. and I found out that one solution would be:

  1. splitting up the path of an svg into subpaths
  2. drawing the path to two different canvas elements, one with evenodd one without
  3. if there are differences, reverse one subpath after another until there are no differences between the two canvases

I spent some time on this and for now I can say that the pure node implementation of canvg (the one backed by canvas / cairo) fails to correctly draw an evenodd svg.. just like this package here so basically we're back at the beginning

@jwilson8767
Copy link

Wow! That's actually a really crazy approach! I love it. I did some googling (mostly focused on github) to try to find literally anyone else that had implemented an evenodd "cleaner" or anything similar, but I actually...ended up getting linked back to my own issue over here: svg/svgo#1092

Kind of weird to realize how poor support for evenodd really is.

So the algorithm you mentioned really does depend on being able to render / rasterize svgs at some level. One interesting thing that I note is that this algorithm would not handle cases where the same path crosses back over itself creating a void (as is seen in the first example here). I wonder if doing a series of split operations on each path, we could end up at a point where all possible insides / outsides exist as separate paths -- but we would still need to figure out which ones were which.

So yeah, back where we started -- basically browsers support evenodd because they're processing it as they rasterize the image. Any other approach will require understanding how the paths affect one another better.

@Coding-Kiwi
Copy link

@jwilson8767 Judging from the code I have to say I'm impressed, icomoon is a great piece of software, sadly not very automatable in the free version (except things like headless browser solutions that break after each update of icomoon). Furthermore, I can confirm that even icomoon is unable to fix paths that cross back over themselves, I threw the evenodd star from the example you listed into it and it resulted in a filled out star.

In my personal opinion: most icons that you would convert to a font dont have these crossing paths so it could work most of the time..

If you have an icon with inner subpaths you can autmatically, or manually with tools like Affinity Designer, fix them by changing the fillrule and reversing subpaths until everything looks fine, but crossing paths have to be fixed by adding new subpaths which would require complex detection of path overlapping in the overall context of the svg or equally expensive manual work.

For anyone that is looking for a solution now:

  • fix the icons manually as described

non-selfcrossing paths only:

  • use icomoon and automate the unpacking of the zip
  • use icomoon basic or unlimited plan (non free) and automate the download and unpacking

If I find a solution that at least fixed the non-selfcrossing ones automatically, I'll post a link here.

@eli-crow
Copy link

You might also look into paper.js and their source code.

reorient() saved me when I was creating a Figma plugin that generates custom FontAwesome icon sets. FontAwesome requires path data to be written for nonzero winding in their js icon format. Importing paper.js was certainly not a lightweight solution, but a good enough hack for an export plugin.

http://paperjs.org/reference/compoundpath/#reorient
https://github.com/paperjs/paper.js/blob/18956805efc679923d1c5b65b3ae9bb130693bcd/src/path/PathItem.Boolean.js#L293-L381

@jwilson8767
Copy link

That's interesting, I'll have to check that out.

@Coding-Kiwi
Copy link

Coding-Kiwi commented Apr 22, 2020

I tried out to use paperjs' reorient method, sadly it did not work, I have to use my current method of reversing every single path one by one and checking if the produced image stays the same. If anyone is interested feel free to contact me.

@areve
Copy link

areve commented May 2, 2020

I had the same problem and can fix it in Inkscape. In Inkscape you can turn on show direction of paths.

The path of the hole on the left is in the opposite direction to the one on the right
image
which causes the icon to be rendered as follows
image

To fix this I just need to select the hole on the right and reverse the path direction. What would be nice is if we had a svgo plugin that could do this automatically.

@avet-m
Copy link

avet-m commented Feb 5, 2021

try to export for web via illustrator (AI), it works for me

@umanghome
Copy link

umanghome commented Mar 15, 2021

If you're using Figma, you can use the Fill Rule Editor plugin to reverse paths. Here's how to use it: https://youtu.be/j6dZw3K_E3M

@ivictbor ivictbor mentioned this issue Mar 26, 2021
1 task
@ivictbor
Copy link

If someone else needs clear details on how to fix it, this post explains it also https://hinty.io/brucehardywald/how-to-generate-a-webfont-automated-setup/

@Hobart2967
Copy link

If you're using Figma, you can use the Fill Rule Editor plugin to reverse paths. Here's how to use it: https://youtu.be/j6dZw3K_E3M

That's actually the solution for us!

@SyncroIT
Copy link

I used picosvg to circumvent this problem, just simplify the SVG by running picosvg on it, and then use the simplified version.

https://github.com/googlefonts/picosvg

picosvg your_broken.svg > healthy.svg

@whcompany123
Copy link

I used picosvg to circumvent this problem, just simplify the SVG by running picosvg on it, and then use the simplified version.

https://github.com/googlefonts/picosvg

picosvg your_broken.svg > healthy.svg

fwiw, picosvg uses skia

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet