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

Point direction request #43

Closed
mooman219 opened this issue Oct 15, 2020 · 21 comments
Closed

Point direction request #43

mooman219 opened this issue Oct 15, 2020 · 21 comments

Comments

@mooman219
Copy link

mooman219 commented Oct 15, 2020

I ran into this font which defines and uses glyphs in the CFF and glyf tables: https://github.com/emilk/egui/blob/master/egui/fonts/Comfortaa-Regular.ttf

My rasterizer needs to know the point winding before it can run outline_glyph. For TrueType, a counter clockwise contour will always define the outside edge of a filled area, and a clockwise contour will always define the inside edge of a filled area. This is reversed for OpenType fonts.

Is there a way to know if for a given GlyphId, the glyph is from an OpenType table or TrueType table?

@RazrFalcon
Copy link
Owner

I can add a glyph_source method which will return the TableName or a specific type. We have 7 glyph sources right now.

@RazrFalcon
Copy link
Owner

By the way, the font that you have linked doesn't have a CFF table.

@mooman219
Copy link
Author

mooman219 commented Oct 15, 2020

Apologies, I assumed it had an OTF table like CFF in it because some of the points are defined in the order expected of the CFF/CFF2 tables.

Here's what I believe is happening: Looking at it closer, some glyphs in this font are defined as an affine transform, for example b is actually just a mirrored d. When this transform happens, ttf-parser is maintaining the the original point order. This is correct for most transforms, but incorrect if the glyph is mirrored over any axis. If mirrored over the x or y axis, OutlineBuilder should be called with the points reversed, if the glyph is mirrored over both axis no change is needed.

I would appreciate the glyph_source method for where the glyph's outline is sourced from. This would remove some ambiguity.

@mooman219
Copy link
Author

Some extra information on point ordering for TrueType fonts:

image

Contours that need to be filled black must have a clockwise direction. If we want to make a white area inside an existing contour we must make the direction of the new contour counter clockwise. Contour direction is determined by seeing in which direction the point index values increase or decrease. Contour direction is from the smaller point index to the larger. The general rule is that the contour direction should be such that "black is on the right". Using the glyph "O" as an example, the outer contour should travel clockwise, and the inner contour counter clockwise.

For CFF based contours the direction is exactly the opposite, so contours that need to be filled black must have a counter clockwise direction.

@RazrFalcon
Copy link
Owner

RazrFalcon commented Oct 15, 2020

  1. Can I reproduce this issue with Comfortaa-Regular.ttf?
  2. Can you post a link to the specification were this behavior is explained?
  3. Emitting points in reverse is highly problematic, since we do not allocate, therefore we have to store points on stack first, which is quite limiting.
  4. I will check what freetype does in this case.

@mooman219
Copy link
Author

mooman219 commented Oct 15, 2020

  1. Yes

  2. This font may just be out of spec and it should not define this transform. Apple notes the contour order restriction here. It appears stb_truetype will flip the points.
    image

  3. Really only the individual calls in OutlineBuilder need to be reversed. i.e. quad_to/curve_to/line_to

  4. Okay

Also feel free to message me on xi.zulipchat.com if you want.

@RazrFalcon
Copy link
Owner

RazrFalcon commented Oct 16, 2020

Very strange. FreeType produces the same outline, but the points order and values are very different. Looks like the problem not in mirroring, but in the second contour points order.

I will try to write a tool that will help me with debugging this issue tomorrow.

The most interesting part, is that if you export both outlines (ttf-parser one and ft2 one) to SVG, they will be filled equally, while having different points. Here: issue-43-test.zip
This is probably the reason why I'm missed this.

@RazrFalcon
Copy link
Owner

RazrFalcon commented Oct 16, 2020

So I've added points enumeration:

FreeType:

ft

ttf-parser:

ttfp

As you can see, the points order/direction is roughly the same. Looks like FreeType does some slight changes to the output (not rounding). I will have to run FreeType under debugger to figure it out.

PS: this is GID 232 without Y mirroring, i.e. as is.

@RazrFalcon
Copy link
Owner

So in the end, mirroring and direction are the same as in FreeType. Maybe the problem is somewhere else?

@mooman219
Copy link
Author

mooman219 commented Oct 16, 2020

The glyph in question should be 225 'b', which is a transform of 232.

Given this is a glyph from the glyf table, the outer outline should be clockwise for this character b. It may just be that FreeType is correcting it internally. I would be happy to do so in my raster, but I don't have enough information from ttf-parser to do that, since I would need to know the transform performed on the composite glyph on top of the table source of the glyph.

I don't think any solution here is elegant,

@RazrFalcon
Copy link
Owner

Here is 225 'b'. The directions is still correct.

FreeType:

ft

ttf-parser:

ttfp

I don't think any solution here is elegant,

First, we have to prove that there are an actual bug.

@hasenbanck
Copy link

Just as an information. I just downloaded the version from the google fonts directory and this version doesn't seem to have this problem:
https://fonts.google.com/specimen/Comfortaa

So it might very well be that the TTF file in the above repository is just plain broken.

@RazrFalcon
Copy link
Owner

@hasenbanck Yes, that font version doesn't mirror d for b. But it doesn't seem like the old font itself have a bug. ttf-parser, freetype and harfbuzz are processing it correctly. Something is missing between ttf-parser and fontdue.

@mooman219
Copy link
Author

mooman219 commented Oct 17, 2020

@RazrFalcon Your images are upside down, and also both of your comments for 'b' and 'd' have different windings which illustrates the problem. Your 'd' is counter clockwise and your 'b' is clockwise. There's nothing wrong with fontdue, you're giving me different windings for 'b' and 'd' and there's no way for the raster to know this without knowing information about the transform. Freetype likely holds onto this information given it handles both the parsing and rasterization itself.

I don't think any solution here is elegant

You'll need to expose both the glyph source and transforms applied for each contour to handle this correctly downstream, which is kind of a pain.

@RazrFalcon
Copy link
Owner

All I was able to find is FT_Outline_Get_Orientation, which indeed returns FT_ORIENTATION_POSTSCRIPT for d and FT_ORIENTATION_TRUETYPE for b. I will see how freetype implements it.

PS: And could you please tone down your demands. This is open source and not a commercial support line.

@mooman219
Copy link
Author

mooman219 commented Oct 18, 2020

PS: And could you please tone down your demands. This is open source and not a commercial support line.

Apologies that the last comment came on too strong. I appreciate your work on ttf-parser. I worded the comment to more directly specify the issue in response to "First, we have to prove that there are an actual bug", which felt like a dismissal that there was an issue in the first place.

@RazrFalcon
Copy link
Owner

Ok. My point was that there is no bug, if all other libraries are doing the same. Which is still true.
I consider this a feature, not a bug. Because the current code is correct. You just want information, which is unnecessary in many cases. Aka optional.

@RazrFalcon
Copy link
Owner

So FT_Outline_Get_Orientation is very simple. It tires to fill a path and accumulates the final area. That it. So I guess you can implement this on your side. Since in case of ttf-parser we would have to outline the glyph twice or track the area.

Once again, I don't see a bug here. It's a tricky feature with no clear solution. And as far a I can tell, transformations has nothing to with this. You can make such malformed font even without transformations.

Also, looks like it's not glyf specific. CFF also affected.

@mooman219
Copy link
Author

mooman219 commented Oct 29, 2020

Thank you for the extra information and context on FreeType's solution. I agree this is a malformed font and not a failure on ttf-parser's side. Given this, I agree that a feature request for exposing the transforms doesn't fix the problem that fonts can just be malformed in a different way to produce the same bad result. I'll take FreeType's approach to guess the orientation based on the sign of the accumulation, which although is undesirable to compute, seems like the only way of actually knowing.

Would the feature request for the glyph source still be reasonable to add in ttf-parser? Right now I'm guessing the source based on the tables present in the font.

@RazrFalcon
Copy link
Owner

Sure, I will add such method.

@RazrFalcon
Copy link
Owner

Right now I'm guessing the source based on the tables present in the font.

I think that this is the right way to do this. I've updated the docs. They mention parsing order now.

The problem with glyph_source method is that it's too vague. We have glyph outlining method and raster glyph extraction method. And outlining in case of glyf and variable fonts becomes even more confusing.

Basically, the current implementation is gvar+glyf -> glyf -> cff -> cff2. No fallback.

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

No branches or pull requests

3 participants