discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

textmetrics() and fontmetrics() functions now in the development snapshot

JB
Jordan Brown
Thu, Aug 19, 2021 5:07 PM

I'm excited to announce that the text metric functions that I've been
working on are finally in the development snapshot.  (OK, I'm easily
excited.)

What they do:  You give textmetrics() parameters equivalent to the
parameters to text() - a string, size, font, et cetera - and the
textmetrics() function tells you various things about how that string
would be rendered.  It tells you the bounding box of the actual object
created, how far above and below the baseline the text extends, where
the next string would be placed, et cetera.  The related fontmetrics()
function gives you some font-global measurements and, perhaps most
usefully, tells you the name of the actual font selected.

What they don't do:  They don't give you the list of points that make up
the text.  (Maybe in a future project, but maybe that would be better
done in a generic way that works for any geometric object.)

Also interesting is that this project introduces a new data type - for
the moment called an "object", but I don't know that that's really been
settled on, given that that name is also used for geometric objects.  An
object is, like an object in JavaScript or a dictionary in Python, a
collection of names and values.  Objects are  important to this project
because they allow the function to return several values in a structured
and self-documenting way.  You can retrieve values out of an object
using either "object.name" or "object[string]", you can get all of the
keys for the object, you can echo an object, you can convert an object
into a string, and you can test for whether a particular data item is an
object using is_object().  You cannot yet create your own objects.

These new features are still experimental.  To use them, you have to
turn them on at Edit / Preferences / Features.

Comments solicited.

Here's some draft documentation.


|textmetrics()| returns information about the size and positioning that
would be applied to text if laid out as specified by its arguments.

The arguments to |textmetrics()| are exactly the same as the arguments
to |text()|.

The return value from |textmetrics()| is an object with these properties:

  • position: an [x,y] pair giving the lower left corner of the smallest
    box that would completely surround the text.
  • size: an [x,y] pair giving the size of that box.
  • ascent: the vertical distance (normally positive) from the baseline
    of the text to the highest point in the text.
  • descent: the vertical distance (normally zero or negative) from the
    baseline of the text to the lowest point in the text.
  • offset: an [x,y] pair giving the distance from the origin to the
    starting point of the baseline of the text. This value is normally
    [0,0] but can be non-zero when using non-default alignments.
  • advance: an [x,y] pair giving the distance from the starting point
    for this text to the starting point for a subsequent piece of text.
    Informally, it says how far the pen should be moved before starting
    the next piece of text. This value is directly helpful only with
    default alignments - horizontal text aligned left/baseline and
    vertical text aligned center/top.

Note:  as with any array, you can use an object-like ".x" or ".y" to
refer to the two entries in [x,y] pairs.  Thus you can refer to either
obj.size[0] or obj.size.x to get the X dimension of the text.

Here's a sample output (reformatted for readability):

ECHO: {
    position = [2.2784, -5.7728];
    size = [87.0362, 24.8832];
    ascent = 19.1104;
    descent = -5.7728;
    offset = [0, 0];
    advance = [89.5186, 0];
}

and here's an illustration showing some of the values:


|fontmetrics()| returns information about the specified font and size.

The arguments to |fontmetrics()| are the size and font name.

The return value from |fontmetrics()| is an object with these properties:

  • nominal: an object with |ascent| and |descent| properties describing
    the "usual" ascent and descent of the font. For most fonts, this
    will be the ascent of upper-case letters and the descent of
    lower-case letters with descenders.
  • max: an object with |ascent| and |descent| properties describing the
    maximum ascent and descent of the font.
  • interline: gives, as a positive number, the designed inter-line
    spacing for the font.
  • font: an object with members |family| and |style| that gives the
    name and style of the selected font. These may not be the same as
    the name supplied, if the specified font is not available, or if a
    generic name like "sans" or "serif" is specified, or if only a style
    is specified.
I'm excited to announce that the text metric functions that I've been working on are finally in the development snapshot.  (OK, I'm easily excited.) What they do:  You give textmetrics() parameters equivalent to the parameters to text() - a string, size, font, et cetera - and the textmetrics() function tells you various things about how that string would be rendered.  It tells you the bounding box of the actual object created, how far above and below the baseline the text extends, where the next string would be placed, et cetera.  The related fontmetrics() function gives you some font-global measurements and, perhaps most usefully, tells you the name of the actual font selected. What they don't do:  They don't give you the list of points that make up the text.  (Maybe in a future project, but maybe that would be better done in a generic way that works for any geometric object.) Also interesting is that this project introduces a new data type - for the moment called an "object", but I don't know that that's really been settled on, given that that name is also used for geometric objects.  An object is, like an object in JavaScript or a dictionary in Python, a collection of names and values.  Objects are  important to this project because they allow the function to return several values in a structured and self-documenting way.  You can retrieve values out of an object using either "object.name" or "object[string]", you can get all of the keys for the object, you can echo an object, you can convert an object into a string, and you can test for whether a particular data item is an object using is_object().  You cannot yet create your own objects. These new features are still experimental.  To use them, you have to turn them on at Edit / Preferences / Features. Comments solicited. Here's some draft documentation. --- |textmetrics()| returns information about the size and positioning that would be applied to text if laid out as specified by its arguments. The arguments to |textmetrics()| are exactly the same as the arguments to |text()|. The return value from |textmetrics()| is an object with these properties: * position: an [x,y] pair giving the lower left corner of the smallest box that would completely surround the text. * size: an [x,y] pair giving the size of that box. * ascent: the vertical distance (normally positive) from the baseline of the text to the highest point in the text. * descent: the vertical distance (normally zero or negative) from the baseline of the text to the lowest point in the text. * offset: an [x,y] pair giving the distance from the origin to the starting point of the baseline of the text. This value is normally [0,0] but can be non-zero when using non-default alignments. * advance: an [x,y] pair giving the distance from the starting point for this text to the starting point for a subsequent piece of text. Informally, it says how far the pen should be moved before starting the next piece of text. This value is directly helpful only with default alignments - horizontal text aligned left/baseline and vertical text aligned center/top. Note:  as with any array, you can use an object-like ".x" or ".y" to refer to the two entries in [x,y] pairs.  Thus you can refer to either obj.size[0] or obj.size.x to get the X dimension of the text. Here's a sample output (reformatted for readability): ECHO: {     position = [2.2784, -5.7728];     size = [87.0362, 24.8832];     ascent = 19.1104;     descent = -5.7728;     offset = [0, 0];     advance = [89.5186, 0]; } and here's an illustration showing some of the values: --- |fontmetrics()| returns information about the specified font and size. The arguments to |fontmetrics()| are the size and font name. The return value from |fontmetrics()| is an object with these properties: * nominal: an object with |ascent| and |descent| properties describing the "usual" ascent and descent of the font. For most fonts, this will be the ascent of upper-case letters and the descent of lower-case letters with descenders. * max: an object with |ascent| and |descent| properties describing the maximum ascent and descent of the font. * interline: gives, as a positive number, the designed inter-line spacing for the font. * font: an object with members |family| and |style| that gives the name and style of the selected font. These may not be the same as the name supplied, if the specified font is not available, or if a generic name like "sans" or "serif" is specified, or if only a style is specified.
TP
Torsten Paul
Fri, Aug 20, 2021 12:09 AM

I think that is genuinely exciting stuff :-).

Thanks for the documentation part, I've added that to the WIP page
of the manual.

https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/WIP

ciao,
Torsten.

I think that is genuinely exciting stuff :-). Thanks for the documentation part, I've added that to the WIP page of the manual. https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/WIP ciao, Torsten.
LM
Leonard Martin Struttmann
Fri, Aug 20, 2021 1:46 AM

At first glance, this looks awesome!  I'm not at a point that I can install
a dev build, but I look forward to trying this out in the future.

Some more examples of how these are used would be nice.

I am especially excited about your new "objects" and being able to access a
value by "obj.size.x" syntax.  Being able to define our own "objects" would
certainly raise OpenSCAD to a higher plane of existence!  ;-)

On Thu, Aug 19, 2021 at 1:07 PM Jordan Brown openscad@jordan.maileater.net
wrote:

I'm excited to announce that the text metric functions that I've been
working on are finally in the development snapshot.  (OK, I'm easily
excited.)

What they do:  You give textmetrics() parameters equivalent to the
parameters to text() - a string, size, font, et cetera - and the
textmetrics() function tells you various things about how that string would
be rendered.  It tells you the bounding box of the actual object created,
how far above and below the baseline the text extends, where the next
string would be placed, et cetera.  The related fontmetrics() function
gives you some font-global measurements and, perhaps most usefully, tells
you the name of the actual font selected.

What they don't do:  They don't give you the list of points that make up
the text.  (Maybe in a future project, but maybe that would be better done
in a generic way that works for any geometric object.)

Also interesting is that this project introduces a new data type - for the
moment called an "object", but I don't know that that's really been settled
on, given that that name is also used for geometric objects.  An object is,
like an object in JavaScript or a dictionary in Python, a collection of
names and values.  Objects are  important to this project because they
allow the function to return several values in a structured and
self-documenting way.  You can retrieve values out of an object using
either "object.name" or "object[string]", you can get all of the keys for
the object, you can echo an object, you can convert an object into a
string, and you can test for whether a particular data item is an object
using is_object().  You cannot yet create your own objects.

These new features are still experimental.  To use them, you have to turn
them on at Edit / Preferences / Features.

Comments solicited.

Here's some draft documentation.


textmetrics() returns information about the size and positioning that
would be applied to text if laid out as specified by its arguments.

The arguments to textmetrics() are exactly the same as the arguments to
text().

The return value from textmetrics() is an object with these properties:

- position: an [x,y] pair giving the lower left corner of the smallest
box that would completely surround the text.
- size: an [x,y] pair giving the size of that box.
- ascent: the vertical distance (normally positive) from the baseline
of the text to the highest point in the text.
- descent: the vertical distance (normally zero or negative) from the
baseline of the text to the lowest point in the text.
- offset: an [x,y] pair giving the distance from the origin to the
starting point of the baseline of the text. This value is normally [0,0]
but can be non-zero when using non-default alignments.
- advance: an [x,y] pair giving the distance from the starting point
for this text to the starting point for a subsequent piece of text.
Informally, it says how far the pen should be moved before starting the
next piece of text. This value is directly helpful only with default
alignments - horizontal text aligned left/baseline and vertical text
aligned center/top.

Note:  as with any array, you can use an object-like ".x" or ".y" to refer
to the two entries in [x,y] pairs.  Thus you can refer to either
obj.size[0] or obj.size.x to get the X dimension of the text.

Here's a sample output (reformatted for readability):

ECHO: {
position = [2.2784, -5.7728];
size = [87.0362, 24.8832];
ascent = 19.1104;
descent = -5.7728;
offset = [0, 0];
advance = [89.5186, 0];
}

and here's an illustration showing some of the values:


fontmetrics() returns information about the specified font and size.

The arguments to fontmetrics() are the size and font name.

The return value from fontmetrics() is an object with these properties:

- nominal: an object with ascent and descent properties describing the
"usual" ascent and descent of the font. For most fonts, this will be the
ascent of upper-case letters and the descent of lower-case letters with
descenders.
- max: an object with ascent and descent properties describing the
maximum ascent and descent of the font.
- interline: gives, as a positive number, the designed inter-line
spacing for the font.
- font: an object with members family and style that gives the name
and style of the selected font. These may not be the same as the name
supplied, if the specified font is not available, or if a generic name like
"sans" or "serif" is specified, or if only a style is specified.

OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

At first glance, this looks awesome! I'm not at a point that I can install a dev build, but I look forward to trying this out in the future. Some more examples of how these are used would be nice. I am especially excited about your new "objects" and being able to access a value by "obj.size.x" syntax. Being able to define our own "objects" would certainly raise OpenSCAD to a higher plane of existence! ;-) On Thu, Aug 19, 2021 at 1:07 PM Jordan Brown <openscad@jordan.maileater.net> wrote: > I'm excited to announce that the text metric functions that I've been > working on are finally in the development snapshot. (OK, I'm easily > excited.) > > What they do: You give textmetrics() parameters equivalent to the > parameters to text() - a string, size, font, et cetera - and the > textmetrics() function tells you various things about how that string would > be rendered. It tells you the bounding box of the actual object created, > how far above and below the baseline the text extends, where the next > string would be placed, et cetera. The related fontmetrics() function > gives you some font-global measurements and, perhaps most usefully, tells > you the name of the actual font selected. > > What they don't do: They don't give you the list of points that make up > the text. (Maybe in a future project, but maybe that would be better done > in a generic way that works for any geometric object.) > > Also interesting is that this project introduces a new data type - for the > moment called an "object", but I don't know that that's really been settled > on, given that that name is also used for geometric objects. An object is, > like an object in JavaScript or a dictionary in Python, a collection of > names and values. Objects are important to this project because they > allow the function to return several values in a structured and > self-documenting way. You can retrieve values out of an object using > either "object.name" or "object[string]", you can get all of the keys for > the object, you can echo an object, you can convert an object into a > string, and you can test for whether a particular data item is an object > using is_object(). You cannot yet create your own objects. > > These new features are still experimental. To use them, you have to turn > them on at Edit / Preferences / Features. > > Comments solicited. > > Here's some draft documentation. > > --- > > textmetrics() returns information about the size and positioning that > would be applied to text if laid out as specified by its arguments. > > The arguments to textmetrics() are exactly the same as the arguments to > text(). > > The return value from textmetrics() is an object with these properties: > > - position: an [x,y] pair giving the lower left corner of the smallest > box that would completely surround the text. > - size: an [x,y] pair giving the size of that box. > - ascent: the vertical distance (normally positive) from the baseline > of the text to the highest point in the text. > - descent: the vertical distance (normally zero or negative) from the > baseline of the text to the lowest point in the text. > - offset: an [x,y] pair giving the distance from the origin to the > starting point of the baseline of the text. This value is normally [0,0] > but can be non-zero when using non-default alignments. > - advance: an [x,y] pair giving the distance from the starting point > for this text to the starting point for a subsequent piece of text. > Informally, it says how far the pen should be moved before starting the > next piece of text. This value is directly helpful only with default > alignments - horizontal text aligned left/baseline and vertical text > aligned center/top. > > Note: as with any array, you can use an object-like ".x" or ".y" to refer > to the two entries in [x,y] pairs. Thus you can refer to either > obj.size[0] or obj.size.x to get the X dimension of the text. > > Here's a sample output (reformatted for readability): > > ECHO: { > position = [2.2784, -5.7728]; > size = [87.0362, 24.8832]; > ascent = 19.1104; > descent = -5.7728; > offset = [0, 0]; > advance = [89.5186, 0]; > } > > and here's an illustration showing some of the values: > > --- > > fontmetrics() returns information about the specified font and size. > > The arguments to fontmetrics() are the size and font name. > > The return value from fontmetrics() is an object with these properties: > > - nominal: an object with ascent and descent properties describing the > "usual" ascent and descent of the font. For most fonts, this will be the > ascent of upper-case letters and the descent of lower-case letters with > descenders. > - max: an object with ascent and descent properties describing the > maximum ascent and descent of the font. > - interline: gives, as a positive number, the designed inter-line > spacing for the font. > - font: an object with members family and style that gives the name > and style of the selected font. These may not be the same as the name > supplied, if the specified font is not available, or if a generic name like > "sans" or "serif" is specified, or if only a style is specified. > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
JB
Jordan Brown
Fri, Aug 20, 2021 4:44 AM

On 8/19/2021 6:46 PM, Leonard Martin Struttmann wrote:

At first glance, this looks awesome!  I'm not at a point that I can
install a dev build, but I look forward to trying this out in the
future.  

Some more examples of how these are used would be nice.

My guess is that 90%+ would be using the position and size values to
allow you to wrap other elements of the model around the text.  Back
when I started on it, what prompted me to do it was making from-to tags
for Christmas presents.  I wanted the tags to grow or shrink to match
the text.  The same concept might apply to name tags, data/name plates
on models, et cetera.

Beyond that... mostly, I grabbed every metric I could find and reported
it, in case somebody finds it useful.

I am especially excited about your new "objects" and being able to
access a value by "obj.size.x" syntax.  Being able to define our own
"objects" would certainly raise OpenSCAD to a higher plane of
existence!  ;-)

Although I think I did most or all of the implementation, I took much of
the design from a much more elaborate plan discussed at
https://github.com/openscad/openscad/issues/3088 and
https://github.com/doug-moen/openscad2/blob/master/rfc/Objects.md .  You
might look there for some hints as to what might lie in the future.

(I don't remember whether I looked at the existing work at
https://github.com/openscad/openscad/pull/3087 .  I don't think that I
did, but I don't remember why not.)

The first thing that's missing is the ability to create objects in
OpenSCAD programs.  I'm not saying that's hard... I just didn't want to
take it on, since the "object" capability was not what I was primarily
trying to achieve.

On 8/19/2021 6:46 PM, Leonard Martin Struttmann wrote: > At first glance, this looks awesome!  I'm not at a point that I can > install a dev build, but I look forward to trying this out in the > future.   > > Some more examples of how these are used would be nice. My guess is that 90%+ would be using the position and size values to allow you to wrap other elements of the model around the text.  Back when I started on it, what prompted me to do it was making from-to tags for Christmas presents.  I wanted the tags to grow or shrink to match the text.  The same concept might apply to name tags, data/name plates on models, et cetera. Beyond that... mostly, I grabbed every metric I could find and reported it, in case somebody finds it useful. > I am especially excited about your new "objects" and being able to > access a value by "obj.size.x" syntax.  Being able to define our own > "objects" would certainly raise OpenSCAD to a higher plane of > existence!  ;-) Although I think I did most or all of the implementation, I took much of the design from a much more elaborate plan discussed at https://github.com/openscad/openscad/issues/3088 and https://github.com/doug-moen/openscad2/blob/master/rfc/Objects.md .  You might look there for some hints as to what might lie in the future. (I don't remember whether I looked at the existing work at https://github.com/openscad/openscad/pull/3087 .  I don't think that I did, but I don't remember why not.) The first thing that's missing is the ability to create objects in OpenSCAD programs.  I'm not saying that's hard... I just didn't want to take it on, since the "object" capability was not what I was primarily trying to achieve.
JB
Jordan Brown
Fri, Aug 20, 2021 4:51 AM

I should mention that there are a couple of subtle incompatible
changes.  These behaviors are not controlled by the experimental flag.

All inter-glyph spacing was previously shrunk by 2.4%.  Nobody remembers
why, and it sure seemed like it was a mistake.  I got rid of that, so
the text is at its designed spacing.  If you need to, I think you can
compensate by setting spacing to 1/1.024.

Non-default alignment on vertical text was just wrong.  As I recall, the
default stayed pretty much the same, but I fixed the other variations.

Note:  I think that alignment behavior on right-to-left and
bottom-to-top text is still wrong; for instance, I think that the
default for RTL text should be halign=right.  But that's not something I
want to fix today.

I should mention that there are a couple of subtle incompatible changes.  These behaviors are *not* controlled by the experimental flag. All inter-glyph spacing was previously shrunk by 2.4%.  Nobody remembers why, and it sure seemed like it was a mistake.  I got rid of that, so the text is at its designed spacing.  If you need to, I think you can compensate by setting spacing to 1/1.024. Non-default alignment on vertical text was just wrong.  As I recall, the default stayed pretty much the same, but I fixed the other variations. Note:  I think that alignment behavior on right-to-left and bottom-to-top text is still wrong; for instance, I think that the default for RTL text should be halign=right.  But that's not something I want to fix today.
LM
Leonard Martin Struttmann
Fri, Aug 20, 2021 12:50 PM

"My guess is that 90%+ would be using the position and size values to
allow you to wrap other elements of the model around the text.  Back when I
started on it, what prompted me to do it was making from-to tags for
Christmas presents.  I wanted the tags to grow or shrink to match the
text.  The same concept might apply to name tags, data/name plates on
models, et cetera."

I already use https://www.thingiverse.com/thing:3004457 by Alexander Pruss
to make our garden plant signs.  Looking forward to seeing what yours does.

On Fri, Aug 20, 2021 at 12:51 AM Jordan Brown openscad@jordan.maileater.net
wrote:

I should mention that there are a couple of subtle incompatible changes.
These behaviors are not controlled by the experimental flag.

All inter-glyph spacing was previously shrunk by 2.4%.  Nobody remembers
why, and it sure seemed like it was a mistake.  I got rid of that, so the
text is at its designed spacing.  If you need to, I think you can
compensate by setting spacing to 1/1.024.

Non-default alignment on vertical text was just wrong.  As I recall, the
default stayed pretty much the same, but I fixed the other variations.

Note:  I think that alignment behavior on right-to-left and bottom-to-top
text is still wrong; for instance, I think that the default for RTL text
should be halign=right.  But that's not something I want to fix today.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

*"My guess is that 90%+ would be using the position and size values to allow you to wrap other elements of the model around the text. Back when I started on it, what prompted me to do it was making from-to tags for Christmas presents. I wanted the tags to grow or shrink to match the text. The same concept might apply to name tags, data/name plates on models, et cetera."* I already use https://www.thingiverse.com/thing:3004457 by Alexander Pruss to make our garden plant signs. Looking forward to seeing what yours does. On Fri, Aug 20, 2021 at 12:51 AM Jordan Brown <openscad@jordan.maileater.net> wrote: > I should mention that there are a couple of subtle incompatible changes. > These behaviors are *not* controlled by the experimental flag. > > All inter-glyph spacing was previously shrunk by 2.4%. Nobody remembers > why, and it sure seemed like it was a mistake. I got rid of that, so the > text is at its designed spacing. If you need to, I think you can > compensate by setting spacing to 1/1.024. > > Non-default alignment on vertical text was just wrong. As I recall, the > default stayed pretty much the same, but I fixed the other variations. > > Note: I think that alignment behavior on right-to-left and bottom-to-top > text is still wrong; for instance, I think that the default for RTL text > should be halign=right. But that's not something I want to fix today. > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
JB
Jordan Brown
Fri, Aug 20, 2021 5:04 PM

On 8/20/2021 5:50 AM, Leonard Martin Struttmann wrote:

I already use https://www.thingiverse.com/thing:3004457
https://www.thingiverse.com/thing:3004457 by Alexander Pruss to make
our garden plant signs.  Looking forward to seeing what yours does.

He does a lot more, in that he uses the metrics data to do things like
wrap text.  I only provide the metrics data.

The big difference is in where the metrics data comes from.  He has a
1.3m megabyte file, 27,000 lines, that provides metrics data for 46
specific fonts.  I get the metrics data from the font, so
automatically have data for any font that you have installed.

The functions that I've provided should let him drop the 1.3MB file and
make a few of the functions be trivial.  The text-wrapping functions
could be rebuilt on top of my functions.  That conversion would be an
interesting exercise.

On 8/20/2021 5:50 AM, Leonard Martin Struttmann wrote: > I already use https://www.thingiverse.com/thing:3004457 > <https://www.thingiverse.com/thing:3004457> by Alexander Pruss to make > our garden plant signs.  Looking forward to seeing what yours does. He does a lot more, in that he uses the metrics data to do things like wrap text.  I only provide the metrics data. The big difference is in where the metrics data comes from.  He has a 1.3m megabyte file, 27,000 lines, that provides metrics data for 46 specific fonts.  I get the metrics data *from* the font, so automatically have data for any font that you have installed. The functions that I've provided should let him drop the 1.3MB file and make a few of the functions be trivial.  The text-wrapping functions could be rebuilt on top of my functions.  That conversion would be an interesting exercise.