joshmarinacci / node-pureimage

Pure JS implementation of the HTML Canvas 2D drawing API
MIT License
797 stars 90 forks source link

fillText produces odd results with some fonts #144

Open SilentDepth opened 2 years ago

SilentDepth commented 2 years ago

Expected Behaviour

"A" rendered properly.

Actual Behaviour

The triangle region of "A" is filled.

Steps To Reproduce

  1. Repro repo: https://github.com/SilentDepth/repro-node-pureimage-filltext-issue
  2. npm install
  3. node index.js
  4. Check result.png

Any Relevant Code Snippets

No special codes needed. Just normal fillText().

Platform

OS: macOS 13.0 Node Version: 14.20.0 NPM Version: 6.14.17 PureImage Version: 0.3.14

Any Additional Info

The render results of other letters may have bigger problem. And Inter is not the only font which rendered improperly. For example: https://github.com/googlefonts/noto-cjk/tree/main/Serif/OTF/SimplifiedChinese

BTW these fonts work fine in opentype.js.org.

joshmarinacci commented 2 years ago

Most likely this is an issue with our font parser dependency. What version of opentype.js do you see works correctly?

joshmarinacci commented 2 years ago

I have an idea of the root cause (or at least one root cause). It's probably because the text processing code doesn't respect Cubics, only quadratic curves. TrueType only uses Quadratics but I believe newer font types (like open type) use Cubics. I'll add cubics to a branch and see if that fixes it.

joshmarinacci commented 2 years ago

This is what it looks like with the patch on the bug_144 branch. Much better, though now we can see that holes are filled

bug_144

SilentDepth commented 2 years ago

Most likely this is an issue with our font parser dependency. What version of opentype.js do you see works correctly?

Not sure about the exact version of opentype.js I tested with. Just opened https://opentype.js.org/, chose the font file, and checked the render result.

joshmarinacci commented 1 year ago

Can you see if the current release fixes your issues?

SilentDepth commented 1 year ago

Not fixed. The results seem the same. Font: Inter-Regular v3.019 PureImage Version: 0.3.15 Node Version: 18.12.1 ABC

joshmarinacci commented 1 year ago

I'm looking at this again. Even with C command support (Bézier curves) it still looks wrong. Looking at the path stream it seems that a TTF creates one long path where as the OTF creates three paths. I see three Z commands (end of path) in the OTF stream and just one in the TTF stream. This explains why the centers are filled. The TTF has a single path so the inside/outside polygon test works correctly. The OTF is essentially drawing three separate polygons: the outside shape of the B, then the first hole, then the second hole. Of course the code doesn't know that these second ones should be holes instead of additional polygons. I'll have to think about how to address this.