mooman219 / fontdue

The fastest font renderer in the world, written in pure rust.
Apache License 2.0
1.44k stars 71 forks source link

Font missing dots above lowercase 'i' and 'j' #69

Closed nickmass closed 3 years ago

nickmass commented 3 years ago

When the following font is rasterized with fontdue it is missing the dots above the lowercase i and j.

https://nickmass.com/files/evesansneue-regular.otf

I modified the raster-print example code to render the same font with rusttype/freetype-rs for comparison:

image

Changing the rasterizing scale has no effect on the problem, and slightly interestingly the exclamation point renders with its dot just fine.

I checked the font against some previous versions of fontdue and noticed that prior to commit cb2e9b237 the problem is actually reversed and it only renders the dot while the stem of the characters is absent.

I added some debugging to the fontdue Geometry struct to see if the glyphs were being loaded correctly and as far as I could tell that was fine.

// fontdue loading the lowercase 'i'
move_to(x0: 75, y0: 540)
line_to(x0: 155, y0: 540)
line_to(x0: 155, y0: 0)
line_to(x0: 75, y0: 0)
close()
move_to(x0: 75, y0: 640)
line_to(x0: 155, y0: 640)
line_to(x0: 155, y0: 720)
line_to(x0: 75, y0: 720)
close()

// results in the following v_lines being created
v_line(start:(155, 540), end:(155, 0))
v_line(start:(75, 0), end:(75, 540))
v_line(start:(155, 640), end:(155, 720))
v_line(start:(75, 720), end:(75, 640))
mooman219 commented 3 years ago

tl;dr fontdue accepts out of spec fonts, but your font is more out of spec than the usual out of spec font.

Most font rasterizers, like Fontdue, raster a glyph in scanline order. It knows when it enters or leaves a contour by the direction of the line it encounters. Specifically, this is the winding order. TTF fonts use the glyf table while OTF fonts generally use the CFF/CFF2 tables. The glyf table specifies that contours are defined clockwise, while CFF/CFF2 is counter-clockwise.

TTF glypf table example: image

Fontdue accept malformed fonts to some degree. It will re-compute glyph metrics and winding order per glyph (that commit cb2e9b2), which fixes a number of issues with some moderately wrong fonts. If a font has a clockwise winding order for some glyphs, but a counter clockwise order for other, Fontdue will render them all correct.

The issue here is that this spec is not enforced, and fonts that don't follow the spec produce undefined behavior. Your font, https://nickmass.com/files/evesansneue-regular.otf, is out of spec. It's a malformed font. Your 'i' and 'j' use two different winding orders for each of its contours. I colored the subtractive and additive sections of the glyph in red and green.

image


I'm open to fixing this, I'd love to support more out of spec fonts, but this issue is definitely on the font.

nickmass commented 3 years ago

Thank you for the detailed response, I was able to open the font in an editor and quickly fix the directions of those contours and now fontdue renders everything perfectly.

mooman219 commented 3 years ago

No worries, I'll work on thinking of a way to handle this new edge case automatically so you won't need to hand fix the font in the future.

mooman219 commented 3 years ago

I fixed it, I'll push the change eventually

mooman219 commented 3 years ago

Fixed here: https://github.com/mooman219/fontdue/commit/6f0cea6233bf37fb05ca3ea0c57de65821068ef1#diff-7c2ff0572abc69580f0e70fc67a1cbe0d6d5e94bdb96a787ed350e108ef7f3b0