discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Re: textmetrics size and text halign

SL
Steve Lelievre
Mon, Jul 14, 2025 8:18 PM

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

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 >
JB
Jordan Brown
Mon, Jul 14, 2025 11:34 PM

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"].

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"].
SL
Steve Lelievre
Tue, Jul 15, 2025 4:56 PM

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"].

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"]. >