Okay,
I partially answered my own question: the inked part of text doesn't
start at the origin as I had assumed (for an example, just use the
statement text("B"); and you'll see that the shape that is placed to the
left of the y axis rather than touching it.)
Why is that?
Anyway, it seems that to get 'tight alignment' I can use something along
the lines of
s="ABC";
align = object(left = 0, center = 0.5, right = 1);
// Ensure that the parameter lists in these next two statements are identical, and don't use halign parameter
t = textmetrics(s);
translate([-t.position.x - align["center"] * t.size.x, 0]) text(s);
Steve
On 2025-07-14 10:25 a.m., Steve Lelievre wrote:
Hi, everyone,
I don't know if this question is specific to OpenSCAD (Nightly
2025.07.11) or more about the way computer text layout works in
general (maybe kerning or one of the many other strange text layout
things I don't understand.)
If I translate the text "ABC" to the left by half the x size give by
textmetrics, its position is not quite the same as the I get by
centering it using halign. Why is there a difference?
And, if I change the text string to be a single character "A" the two
approaches exactly overlap, but using "B" they do not. Is this a
related behaviour or something else going on?
s = "ABC";
color("red") text(s, halign = "center");
displacement = textmetrics(s).size.x/-2;
color("lime") translate([displacement, 0, 0]) text(s);
Cheers,
Steve
On 7/14/2025 1:18 PM, Steve Lelievre via Discuss wrote:
I partially answered my own question: the inked part of text doesn't
start at the origin as I had assumed (for an example, just use the
statement text("B"); and you'll see that the shape that is placed to
the left of the y axis rather than touching it.)
Yes. (You mean to the right, not the left.)
Why is that?
It's all about how the letters fit together. A letter with a big
vertical side (B, N, E) will get more space on that side than a letter
with a point on the side (A, V, W). The idea is to make it so the
reader perceives the same amount of space between the letters, where
that perception is influenced by the shapes of the letters.
Quick demo:
s="A";
tm = textmetrics(s);
translate([0,0,0.5]) text(s);
color("white") translate(tm.position) square(tm.size);
translate([0,0,1]) color("black") square([tm.advance.x,0.2]);
Anyway, it seems that to get 'tight alignment' I can use something
along the lines of
s="ABC";
align = object(left = 0, center = 0.5, right = 1);
// Ensure that the parameter lists in these next two statements are identical, and don't use halign parameter
t = textmetrics(s);
translate([-t.position.x - align["center"] * t.size.x, 0]) text(s);
Yes, that is exactly one of the use cases for textmetrics() -
positioning based on the actual ink.
One minor point: you can say align.center instead of align["center"].
Jordan,
Thank you for the explanations.
Steve
On 2025-07-14 4:34 p.m., Jordan Brown wrote:
On 7/14/2025 1:18 PM, Steve Lelievre via Discuss wrote:
I partially answered my own question: the inked part of text doesn't
start at the origin as I had assumed (for an example, just use the
statement text("B"); and you'll see that the shape that is placed to
the left of the y axis rather than touching it.)
Yes. (You mean to the right, not the left.)
Why is that?
It's all about how the letters fit together. A letter with a big
vertical side (B, N, E) will get more space on that side than a letter
with a point on the side (A, V, W). The idea is to make it so the
reader perceives the same amount of space between the letters, where
that perception is influenced by the shapes of the letters.
Quick demo:
s="A";
tm = textmetrics(s);
translate([0,0,0.5]) text(s);
color("white") translate(tm.position) square(tm.size);
translate([0,0,1]) color("black") square([tm.advance.x,0.2]);
Anyway, it seems that to get 'tight alignment' I can use something
along the lines of
s="ABC";
align = object(left = 0, center = 0.5, right = 1);
// Ensure that the parameter lists in these next two statements are identical, and don't use halign parameter
t = textmetrics(s);
translate([-t.position.x - align["center"] * t.size.x, 0]) text(s);
Yes, that is exactly one of the use cases for textmetrics() -
positioning based on the actual ink.
One minor point: you can say align.center instead of align["center"].