fontforge / fontforge

Free (libre) font editor for Windows, Mac OS X and GNU+Linux
http://fontforge.github.io/
Other
6.53k stars 704 forks source link

Trouble importing Octicons v10 Heart Fill Icon #4378

Open FredHappyface opened 4 years ago

FredHappyface commented 4 years ago

Important

Mark with [x] to select. Leave as [ ] to unselect.

When reporting a bug/issue:

When you open an issue for a change/improvement/feature request:

Version

FontForge 20200314 as available https://ci.appveyor.com/project/fontforge/fontforge/build/artifacts Windows 10 1909

Description and screenshots

When importing the heart-fill.svg from Octicons v10, FontForge 20200314 crashes. Using https://ci.appveyor.com/project/fontforge/fontforge/build/artifacts prevents the crash but the icon looks rather unusual

image

Sample SVG

<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M7.655 14.916L8 14.25l.345.666a.752.752 0 01-.69 0zm0 0L8 14.25l.345.666.002-.001.006-.003.018-.01a7.643 7.643 0 00.31-.17 22.08 22.08 0 003.433-2.414C13.956 10.731 16 8.35 16 5.5 16 2.836 13.914 1 11.75 1 10.203 1 8.847 1.802 8 3.02 7.153 1.802 5.797 1 4.25 1 2.086 1 0 2.836 0 5.5c0 2.85 2.045 5.231 3.885 6.818a22.075 22.075 0 003.744 2.584l.018.01.006.003h.002z"/></svg>

FontForge render

image

SVG render

Using https://github.com/lishu/vscode-svg2 the svg file renders as such:

image

Thank you for your time

ctrlcctrlv commented 4 years ago

Hello!

There's certainly something mighty strange going on in this file, the path is unlikely to occur, I think. Perhaps they are doing it to save bytes? It's very hacky, it self-intersects.

In Inkscape, if you select the path and do «Path→Break Apart», you'll see how weird they drew this heart— it's essentially two shapes.

image

Of course, I'm not excusing the bug, please don't take it that way—just trying to provide context.

Even without simplification and direction correction, I get the same bizarre result:

image

So, the bug is confirmed. I'm going to ask @skef to comment, as he's the resident SVG expert.

ctrlcctrlv commented 4 years ago

By the way, in case this is somewhat "urgent" for you @FredHappyface, you can in Inkscape do «Path→Union», save SVG.

image

Please don't close the bug—workarounds do not close bugs on our tracker, only actual fixes.

FredHappyface commented 4 years ago

Hi,

Thanks for looking at this. I have no idea either. Seems completely absurd to say the least. They have done it with a good chunk of the fonts. Interesting how they've gone about it.

Thanks for the workaround too. The best one I had found was much more, shall we say, 'fiddly'. For reference, it is as follows:

  1. Open the svg in Inkscape
  2. Copy the svg to FontForge
  3. For 16x16 svgs, scale uniformly by 6250% and move by x=492, y=-492
  4. Metrics set width to 1000 and then metrics centre in width

Would you know how to apply this to the full icon set? (there are probably around 200 and I'll be up until lunch time tomorrow if I take the manual approach)

Thanks again 👍

ctrlcctrlv commented 4 years ago

I'd start here.

FredHappyface commented 4 years ago

Perfect thanks :)

skef commented 4 years ago

I view the SVG import feature as primarily a way of importing cubic or quadratic paths from other tools (and the export feature as the reverse). It's also provides a means of translating most SVG files into contours, which is handy. Expecting to be able to do that with any SVG input may not be realistic.

Anyway, with this one the primary issue turns out to be pretty simple. I took the file and enlarged it in Inkscape by 16 times, saved, and imported in FontForge:

svg

So ultimately the problem here is that the way that FontForge currently calculates (elliptical) arcs is sensitive to scale, and the scale of this shape is tiny. And although Fontforge normally scales the input based on font metrics it does that at the end. We could add an "upscale" option, or even an "upscale for conversion and then downscale back" option. I'm not sure it's worth the development time, though.

FredHappyface commented 4 years ago

One thing that would be really useful would be the ability to scale pasted glyphs

One moderately quick workaround I have automated is inkscape.com -g --batch-process [icon].svg --verb='EditSelectAll;SelectionUnion;FileSave'. The issue, however is that some icons change such as image.svg

<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M1.75 2.5a.25.25 0 00-.25.25v10.5c0 .138.112.25.25.25h.94a.76.76 0 01.03-.03l6.077-6.078a1.75 1.75 0 012.412-.06L14.5 10.31V2.75a.25.25 0 00-.25-.25H1.75zm12.5 11H4.81l5.048-5.047a.25.25 0 01.344-.009l4.298 3.889v.917a.25.25 0 01-.25.25zm1.75-.25V2.75A1.75 1.75 0 0014.25 1H1.75A1.75 1.75 0 000 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0016 13.25zM5.5 6a.5.5 0 11-1 0 .5.5 0 011 0zM7 6a2 2 0 11-4 0 2 2 0 014 0z"/></svg>

I'll probably look at batch scaling the glyphs and importing that way (I'll drop a follow up with the sample command for that in case someone finds it useful)

Thanks

skef commented 4 years ago

If you mean scale the glyph after pasting it you can just do a uniform scale transform. If you want every glyph to be scaled to a particular height and moved to a particular position (for example), you can write a little python snippet for the tools menu.

FredHappyface commented 4 years ago

I was thinking it may be better to scale first to correct the scale when importing. I've found that modifying the document properties does the trick. I've no idea how to automate that at this point but if I import a glyph then it auto scales anyway. Also importing without scaling and then manually scaling makes no difference. Some of the curves still go crazy

skef commented 4 years ago

Oh, yeah, FontForge can only scale interpreted contours, not (currently) the SVG input prior to interpretation.

I was going to suggest throwing a <g transform="scale(16)"> element around the content but that doesn't work either -- FontForge transforms interprets paths first and then scales them (which is fairly normal practice).

Sorry about this -- I realize that it sounds funny for a tool to be sensitive to scale when interpreting a format that isn't (or isn't supposed to be). It turns out to be hard enough just to make floating point calculations work at a target scale. Even if we fixed the arc calculations (which are causing your problem) the expand stroke code would still fail at these scales.

FredHappyface commented 4 years ago

That makes sense. Would you be able to recommend a tool to batch resize svgs? Ive tried scripting inkscape, rsvg-convert and neither of those work. The only solution I have found is https://www.iloveimg.com/resize-image/resize-svg but that limits you to about 16 and I would rather use something local if possible.

Yeah the transform thing was pretty much the first thing I tried. Turns out inkskape works but I can't seem to automate that.

Not at all. Does sound a bit odd I admit but I can imagine that it will be for a good reason. The fact that there seem to be so many ways to achieve the same thing in svg makes it one of those magical formats as far as I am concerned!

FredHappyface commented 4 years ago

Right found a rather convoluted but so far working solution

Note that the preferred solution is the new one but I've added the other solutions below

Preferred

  1. Install the following: inkscape (any), imagemagik (linux/wsl), potrace (linux/wsl)
  2. Run inkscape.com [icon].svg -w 160 -y 255 -o [icon].png for each svg
  3. Run convert [icon].png [icon].bmp
  4. Run potrace [icon].bmp -s -o [icon].svg
  5. Remove [icon].png and [icon].bmp

To do this all in one go, run batchProcessLinux.py in linux or wsl.

Alternative 1

  1. Run inkscape.com -g --batch-process [icon].svg --verb='EditSelectAll;SelectionUnion;FileSave' for each svg file
  2. Import into FontForge

Alternative 2

  1. Open the svg in Inkscape
  2. Copy the svg to FontForge
  3. For 16x16 svgs, scale uniformly by 6250% and move by x=492, y=-492
  4. Metrics set width to 1000 and then metrics centre in width

batchProcessLinux.py

""" script to batch process a directory of svgs """

from subprocess import Popen
from os.path import isfile, join
from os import listdir

from pathlib import Path
THISDIR = str(Path(__file__).resolve().parent)

files = [
file.replace(".svg", "") for file in listdir(THISDIR)
if isfile(join(THISDIR, file)) and file.endswith(".svg")]
for file in files:
    with Popen(f"inkscape.com {file}.svg -w 160 -y 255 -o {file}.png;convert {file}.png {file}.bmp;potrace {file}.bmp -s -o {file}.svg;rm {file}.png;rm {file}.bmp", shell=True) as process:
        exitCode = process.wait()
    if exitCode > 0:
        print(f"error for file: {file}")
    else:
        print(f"success for file: {file}")
ctrlcctrlv commented 4 years ago

Not sure why potrace would be preferred? Its output won't be that great most of the time...

skef commented 4 years ago

I'll think about how hard it would be to add a "temp scale" option to the SVG import code (at some point -- probably not soon) to close this issue. It might not be too hard to just multiply the values as they are read by a parameter and then scale down at the end.

ctrlcctrlv commented 4 years ago

Cool; for now I'd say that @FredHappyface's "alternative 1" is the best solution :-)

skef commented 4 years ago

Alternative 1 appears to be the best solution for the particular shape chosen to represent the issue. Alternative 2 is the best solution for the general scaling problem -- any shape with extremely tight (i.e. high curvature) elliptical arcs could wind up with problems like the illustration.

(I haven't checked but Inkscape's Union algorithm might be reducing arcs to quadratic or cubic splines; I'm not sure why else it would be fixing the problem. I verified that just increasing the size with Inkscape doesn't eliminate the arcs, which increases my confidence in the scale diagnosis.)

FredHappyface commented 4 years ago

I've found that potrace was perfect for the 250 icons at 160x160 and eliminated the issues that alternative 1 gave me. Alternative 2 worked but seemed more fiddly for my use case and the 'preferred' method could be automated with ease

ctrlcctrlv commented 4 years ago

OK. The reason I say it's not generally applicable is that most font designers expect their points to persist across imports. Potrace will do things like collapse corners into curves with very short Bézier handles, or collapse tight curves into points. It will usually destroy small details like inktraps, seeing them as not relevant to the shape. It will not put points in optimal places, nor at extrema. Etc. Etc.

This is because it works by rasterizing and retracing the SVG, which destroys the original data.

I am not really writing this for you, but rather for Googlers who may arrive here. :-)

In an icon font, I can see why these considerations do not matter, although due to my OCD, I would still preserve curves.

FredHappyface commented 4 years ago

Ah yeah I hadn't considered those points... Makes sense how that would be a horrible solution in those cases. I'm my case that's what I will be going with as I am not bothered about preserving any of those details

ctrlcctrlv commented 4 years ago

FYI. Just had this problem in my own project, so am here to offer a more complete solution.

I used two tools to fix it: svgcleaner and rsvg-convert.

My zoom icon was getting a very weird outline and the same kind of brokenness @FredHappyface observed.

Basically this was enough to solve the problem after svgcleaner took care of junk Inkscape left behind which was confusing rsvg-convert:

for f in *.svg; do rsvg-convert.exe --zoom=10.0 -f svg "$f" -o "${f%.svg}"2.svg && rm "$f"; done

rsvg-convert is used on Wikipedia. Works well now.

Before: image

After: image

And for posterity:

svgs.tar.gz

The "2" ones are the good ones.

FredHappyface commented 2 years ago

Hi it's been about 18 months or so since the last comment, so shall I close the issue or leave it open for the time being? :)

skef commented 2 years ago

There are many open FontForge issues that are older and sillier than this one, so you shouldn't feel any pressure to close it. Sometimes this kind of problem doesn't get fixed and sometimes a fix gets rolled into a collection of changes. There are no immediate plans to integrate a pre/post scaling option into the tool, however.

FredHappyface commented 2 years ago

Happy to do whatever is easiest for you, really. Don't see much point in keeping it open if the issue is dead but if you use gh issues as a sort of backlog then I could see the point in leaving it open :)

ctrlcctrlv commented 2 years ago

It's easiest for us for you to stop making comments like this across multiple issues please. It's bothering nobody. If someone pushes a fix that closes 5 or 10 issues, which has happened and could happen, then it'll be closed. Or someone could have your exact problem and more skills and so fix only this issue. Or it could never be fixed and will be here, open, in a decade. Either way we do not need reminders that it's still open.