text(): the 2.4% problem

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

text(): the 2.4% problem

JordanBrown
[ As with most of the text() related threads, if you don't care about fine-grained behavior of text layout you can completely ignore this message. ]

There's a few references to the 2.4% problem scattered around, and since I think I've started to understand it pretty completely I thought I'd write it up.

See also text() metrics / glyph size problem #2436 which accurately points out the problem but doesn't go into detail on what it means.

Overview

The core of the problem is that OpenSCAD asks FreeType to generate glyphs with a nominal size of 100,000 units, and then when processing some of the metrics it scales those metrics down by a factor of 102,400.  Net, the metrics end up 2.4% smaller than the font is designed for.  This does *not* affect the sizes of the glyphs themselves, only how they are laid out.  (You might think it is that the glyphs are too large, but they have symmetric scaling factors; it is the metrics that have 100,000 on one side and 102,400 on the other side.)

To find the places affected, search the OpenSCAD sources for references to "unscale".  The key constants are at https://github.com/openscad/openscad/blob/7618d4d0f3d6cd1a43da7f3598885fc6e20849d2/src/FreetypeRenderer.cc#L49

Effects

There are approximately six things affected:


    • These values are used to calculate the width of the text for halign of horizontal text. The impact is that the width is slightly underestimated, by 2.4% of the width of the last glyph.  (The error in previous glyphs corresponds to them being packed together more tightly, and so although it is not as the font is designed it is consistent.)  The impact is that halign=left and halign=center yield text that is slightly right of where it should be.
      Ref https://github.com/openscad/openscad/blob/7618d4d0f3d6cd1a43da7f3598885fc6e20849d2/src/FreetypeRenderer.cc#L286
      This is the top right corner of a Liberation Sans W, right aligned:
    • These values are used to calculate the height of the text for valign of vertical text.  The result is that with valign=top and valign=center, the text is shifted slightly down from its design location.  As noted in other messages, valign=top yields unexpected results; it places the text above the origin.
      Ref https://github.com/openscad/openscad/blob/7618d4d0f3d6cd1a43da7f3598885fc6e20849d2/src/FreetypeRenderer.cc#L292
      Here is the bottom of a Liberation Sans open parenthesis:

      and here it is as designed:

History

The "unscale" constant was added ~9 months ago in response to #3262.  It perpetuated an existing scale mismatch, with commentary about being uncertain whether the mismatch was intentional but that it was being left for backwards compatibility.

The original 1000/1024 factor dates back to the introduction of the text() module in 2014.  In that version, the "scale" value was 1000 (vs 100,000 today) and the unscale value, expressed as numeric constants rather than a symbolic constant, was 64*16 = 1024.

The 1000 factor is intended, I believe, to yield glyphs at a conveniently large size so that their rendering resolution will not be an issue.  I suspect that it's a constant size so that the underlying Freetype will only need to render one copy of the font, rather than one for each size that the OpenSCAD user requests, with OpenSCAD scaling as required for the individual request.  (Note:  I believe that obsessive typographers would say that this is wrong, that fonts should not be simply scaled as they are enlarged but rather may need their dimensions (e.g. line widths) adjusted for the size.  But I'm not that obsessive.)

I suspect that the 1000/1024 mismatch derives from a misunderstanding about the values accepted by and returned by the FreeType and HarfBuzz interfaces.  The 64*16 factor aligns, at least somewhat, with the fact that the values are 26.6 bit fixed-point values - or, in other words, values in 1/64 of a point or maybe 1/64 of a pixel.  The mismatch is, I suspect, that the author didn't realize that the input size (the argument to FT_Set_Char_size) is *also* in 26.6 factional points and so the request for a size of 1000 was a request for a size of 15.625 points.

Related (or, really, unrelated)

The as-rendered size of the glyphs is unrelated to this issue.  For those cases, the value coming out of FreeType is de-scaled by "scale", 100,000, rather than by 102,400.  (It's further scaled up to the requested size in DrawingCallback.)
Ref https://github.com/openscad/openscad/blob/7618d4d0f3d6cd1a43da7f3598885fc6e20849d2/src/FreetypeRenderer.cc#L45
Ref https://github.com/openscad/openscad/blob/7618d4d0f3d6cd1a43da7f3598885fc6e20849d2/src/FreetypeRenderer.cc#L72
(et seq.)

One might think that the size parameter to the text() module would control the height of the glyphs rendered, in some obvious way.  (Maybe it's the height of a capital letter, or that plus descenders, or the interline spacing.)  Whether or not that's true depends on the font design.  When I print a 72-point Liberation Sans X from MS Word, you might think it'd be an inch tall... but it isn't; it's closer to 3/4".  Top of capital letters to bottom of descenders?  No, 7/8".  Maybe the interline spacing?  Nope, that's closer to 1 1/8".  Times New Roman is about the same.  Arial is a little larger; Courier New is quite a bit smaller.

I'm not sure that the scaling calculations are truly correct - as #2436 also says, it looks like there may be a 72/100 problem too - but any discrepancies between the specified size and the dimensions of the glyphs as rendered are definitely not tied to the 2.4% problem.

Why does Jordan care?

It's a small thing, but I'm trying to write a function that returns metric information about text and fonts, and I need to decide whether to return information that matches the glyphs that are rendered, or that matches what OpenSCAD will do to them, or perhaps both.  Also, the current behavior is, well, wrong and makes it hard(er) to follow what's going on in the implementation.

Future?

  • We could leave it alone.  My new metrics functions can either return the "as designed" values and the "as OpenSCAD calculates" values, so that  you could figure out whatever you need to know.
  • We could fix it, and eat the various compatibility breaks.  Quite possibly nobody would actually care.
  • We could fix it, optionally, by having either a configuration switch or a $ variable that sets whether to be in old incorrect mode or new correct mode.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: text(): the 2.4% problem

lar3ry
It was not until reading this that I found out that HarfBuzz is Persian.

When I typed حرف‌باز  into Google translate, it translated it as
"talkative". Hence my comment in another thread.



--
Sent from: http://forum.openscad.org/

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: text(): the 2.4% problem

adrianv
In reply to this post by JordanBrown
My vote is FIX IT.  I'd rather keep things clean and simple and not have a
compatibility mode personally, but I also don't have any big text-dependent
projects to worry about fixing.  





--
Sent from: http://forum.openscad.org/

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|

Re: text(): the 2.4% problem

Troberg
adrianv wrote
> My vote is FIX IT.  I'd rather keep things clean and simple and not have a
> compatibility mode personally, but I also don't have any big
> text-dependent
> projects to worry about fixing.  

I agree. Perpetuating bugs is perpetuating trouble.



--
Sent from: http://forum.openscad.org/

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org