[ 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