Adafruit GFX font size points to pixels for Epapers

Hi all!

I'm looking at the wonderful GxEPD2 library, which uses Adafruit GFX library for the fonts.
I look at the fonts and I read for example:

FreeMonoBold9pt7b

From Adafruit GFX using fonts page I read that 7b stands for 7 bytes and 9pt is the size of the text in points.

Now I would like to know if 9 pt is the height or the width of the character and where I can find the conversion from points to pixels (maybe this depends from the density of the single screen, so may it vary screen by screen?)

This information is useful for undestanding the real space occupied by the font character on the screen.

Thank you

Hi Bjack795,

You nearly got it. I am not a fonts expert, but I think 9pt roughly stands for font height, which is constant, 7b stands for 7 bit encoding, meaning character values from 0 to 127.
The width is character dependent, except for Mono-spaced fonts.

Points and pixels are the same for textsize 1.

font                 xAdvance  yAdvance

FreeMono9pt7b        11        18
FreeMonoBold9pt7b    11        18
FreeMono12pt7b       14        24
FreeMonoBold12pt7b   14        24
FreeMono18pt7b       21        35
FreeMonoBold18pt7b   21        35
FreeMono24pt7b       28        47
FreeMonoBold24pt7b   28        47

FreeSans9pt7b        5..18     22
FreeSansBold9pt7b    5..18     22
FreeSans12pt7b       6..24     29
FreeSans18pt7b       9..36     42
FreeSans24pt7b       12..48    56

FreeSerif9pt7b       5..17     22

(code tag used to render table in monospace)

Use the following Adafruit_GFX methods to retrieve the space needed for single character, whole word, or whole line.

   getTextBounds(const char *string, int16_t x, int16_t y,
      int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h),
    getTextBounds(const __FlashStringHelper *s, int16_t x, int16_t y,
      int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h),
    getTextBounds(const String &str, int16_t x, int16_t y,
      int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);

A point is a fixed unit of measurement. I think it is 1/100 of an inch. Google will tell you.
I think point-size it is the height from base line to the top of a Capital letter on the printed page.

A 0.96 inch 128x64 OLED has pixels that are 0.0075 inch.
A 2.7 inch 128x64 GLCD has pixels that are 0.021 inch

A 3.2 inch diagonal 480x320 TFT has even smaller pixels than the OLED.

Capitals like ABCD are 10 pixels high in FreeMono9pt7b. Braces are 13 pixels high.
You can see the individual dimensions by examining Glyph tables e.g.

const GFXglyph FreeMono9pt7bGlyphs[] PROGMEM = {

David.

Thank you to both.
So if I want to know everytime how to print a character after the previous one I have to use the "mono" fonts and measure on the specific screen the width they use in pixels?
Instead the height should be constant for all the characters.

No, you can always read the current Cursor postion with the appropriate getCursorX() etc.
Or read the geography with getTextBounds().

I am typing on a Tablet. You will need to check spelling etc from Adafruit_GFX.h

Of course individual letters will differ with different Fonts.

It is not difficult. You write screen layout once. Only update certain fields whose dimensions you know at compile time.

Write a helper function once. Use it often e.g. to update right aligned numeric fields.

David.

david_prentice:
No, you can always read the current Cursor postion with the appropriate getCursorX() etc.
Or read the geography with getTextBounds().

I am typing on a Tablet. You will need to check spelling etc from Adafruit_GFX.h

Of course individual letters will differ with different Fonts.

It is not difficult. You write screen layout once. Only update certain fields whose dimensions you know at compile time.

Write a helper function once. Use it often e.g. to update right aligned numeric fields.

David.

The project is a typing project I've written for an alphanumeric screen, so I have to think about a function checking if the "not already displayed" character fits the line or should be sent to the next line.
So I have to know before how much space a character will occupy.
Maybe I will use as threshold the max width within all the possible characters.

Most apps are fixed background with update-able numeric (or text) fields of a fixed size.
My reply applies to these.

If you have an unknown input to an unknown size field, life becomes difficult.
You will not be able to display "antidisestablishmentarianism" on most OLEDs or TFTs in any Font.

You have to choose a suitable "get-out" mechanism. e.g. scroll left-wise in a fixed window as you type
Think about your PC and entering a long URL.

It all gets a bit fiddly. Invest in a pencil and a good supply of cigarette packets.

David.

david_prentice:
Most apps are fixed background with update-able numeric (or text) fields of a fixed size.
My reply applies to these.

If you have an unknown input to an unknown size field, life becomes difficult.
You will not be able to display "antidisestablishmentarianism" on most OLEDs or TFTs in any Font.

You have to choose a suitable "get-out" mechanism. e.g. scroll left-wise in a fixed window as you type
Think about your PC and entering a long URL.

It all gets a bit fiddly. Invest in a pencil and a good supply of cigarette packets.

David.

I spent last two months on make "antidisestablishmentarianism" easily printable on a 1602 lcd, I will pass over this challenge too. :slight_smile:
I print character by character but I will find a solution to manage the space before sending it to the screen.

Please note that I have fixed my first answer and added some numbers.

ZinggJM:
Please note that I have fixed my first answer and added some numbers.

Amazing! Thank you very much.
I will look in details the usage of those methods in the library to understand the input and the outputs of the functions.

Ps.
I didn't notice that you have edited the message so pointing it out was a good choice, thanks!

Hi Bjack795,

I had just fixed my previous answer and added the post so it might be noticed.

Here I have example functions for use of the Adafruit_GFX methods getTextBounds.
Untested, I will attach the complete example later.

uint16_t charWidth(const char ch)
{
  char onecharstring[2] = {ch, 0};
  int16_t x1, y1;
  uint16_t w, h;
  display.getTextBounds(onecharstring, 0, 0, &x1, &y1, &w, &h);
  return w;
}

uint16_t textWidth(const char* text)
{
  int16_t x1, y1;
  uint16_t w, h;
  display.getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
  return w;
}

Oops, doesn't work as expected, value gets limited to display width. Needs more investigation.

getTextBounds (nearly) does what its name implies: it returns the box bounds the text would occupy on the display. With textwrap true the h returned will be for 2 lines if the text does not fit on one line.
Note that w returned may be less than width when wrapped to next line!

For y set 0 getTextBounds returns a negative y1 value, the invisible part above the 0,0 cursor!

The above functions still can be used for the intended purpose, you just need to be aware of this limitation.

GxEPD2_TextWidthExample.ino (4.13 KB)

ZinggJM:
getTextBounds (nearly) does what its name implies: it returns the box bounds the text would occupy on the display. With textwrap true the h returned will be for 2 lines if the text does not fit on one line.

This is ok, I will always use the function with one character but otherwise the h could be a good indication to be aware if a string can fit one line or not.

ZinggJM:
Note that w returned may be less than width when wrapped to next line!

Because it is returning the "width" of the second line?

ZinggJM:
For y set 0 getTextBounds returns a negative y1 value, the invisible part above the 0,0 cursor!

This may be because the text is printed from the left-bottom point?

ZinggJM:
The above functions still can be used for the intended purpose, you just need to be aware of this limitation.

This is really very useful, and the limitations in my opinion can be exploited as further information, for example if I insert a certain y and I obtain a negative y1 maybe it is because the text wil be cut in the top part.

Or if I get an unusual h it's because the string is too long for the line.

So thank you for this.
When I'll get the screen I will do some experiments on this.

Because it is returning the "width" of the second line?

No, but because the last character of the line wrapped may not reach the end of the screen.

This may be because the text is printed from the left-bottom point?

No, but because the cursor must be set for the baseline of the text. So for 9pt you would set the cursor to 0, 10. In my examples I just use a newline with an empty println(), which wastes some few pixel lines.

You can play with the example even without display. Maybe tie BUSY to inactive to avoid long timeout.

ZinggJM:
No, but because the last character of the line wrapped may not reach the end of the screen.

No, but because the cursor must be set for the baseline of the text. So for 9pt you would set the cursor to 0, 10. In my examples I just use a newline with an empty println(), which wastes some few pixel lines.

You can play with the example even without display. Maybe tie BUSY to inactive to avoid long timeout.

Ok then I will try out these days :smiley:

EDIT:
I'm cutting out the minimum part of code from your example in a new file to test these commands.
"GxEPD2_boards_added.h" is necessary only in the case of one of the board listed in the header right?

ZinggJM:
Maybe tie BUSY to inactive to avoid long timeout.

  1. BUSY pin can be any or must be one specific pin on the board? (this is a question I wanted to make you from long time but I had forgotten it ;D )

  2. How I set it to inactive?

  3. For testing the cursor I simply added "display.setCursor(0, 0);" to the setup in which I recall the function
    "charWidth('c')"
    (in this case I use the character 'c' for testing)

  4. x1 and y1 stand for?

using FreeMonoBold9pt7b as font:
EDIT:
The width is always 6
EDIT_2:
The height is always 8 (returning h instead of w)
EDIT_3:
Keeping also the showFont function in the Setup(), the width goes to 9 for 'c', the height is still 8.
EDIT_4:
The width for the numbers is 7 instead of 9 for the 'c'
EDIT_5:
The width for the 't' is 8 instead of the 9 for the 'c'

5)Ok now I'm getting confused, shouldn't "mono" give the same result for the width of every character?

I've added to the previous post some EDITS as a log of the experiments to register all the results, I don't know if it could be useful in the discussion.
Maybe next time I will do an overall resume after all the tests.

    getTextBounds(const char *string, int16_t x, int16_t y,
      int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h),

In my opinion Adafruit did a good job in naming this method "getTextBounds", and with the parameter names chosen. You do not need much experience to guess that for a given point x,y it returns the rectangle parameters the text will fit in, will be used on the screen, x1,y1 the upper left corner, w,h the dimensions of that rectangle.

This should be clear for a whole text, as well as for a single character. For a single character this is also the rectangle the bitmap of the character will occupy.

In post #10 I have attached a test example for you, complete with constructor with parameters for your LOLIN D32 PRO. This is also a suggestion for pins use for this board. You are free to select different pins, except for SCK and MOSI.

I decided to take the effort to answer once more to help you. But I do not like "discussions", as you should already know.

ZinggJM:

    getTextBounds(const char *string, int16_t x, int16_t y,

int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h),




In my opinion Adafruit did a good job in naming this method "getTextBounds", and with the parameter names chosen. You do not need much experience to guess that for a given point x,y it returns the rectangle parameters the text will fit in, will be used on the screen, x1,y1 the upper left corner, w,h the dimensions of that rectangle.

This should be clear for a whole text, as well as for a single character. For a single character this is also the rectangle the bitmap of the character will occupy.

In post #10 I have attached a test example for you, complete with constructor with parameters for your LOLIN D32 PRO. This is also a suggestion for pins use for this board. You are free to select different pins, except for SCK and MOSI.

I decided to take the effort to answer once more to help you. But I do not like "discussions", as you should already know.

I hadn't seen the attached sketch, so thank you for pointing it out. I was using quite the same sketch I did by myself from your examples in the library.
It was pretty the same code except from the display pins setup.

I used the word "discussion" because I had thought you were interested in developing this aspect (you seeemed very interested, my fault) but I misunderstood your intentions, so as always no problem.
As always I do some questions (this time centred on a practical issue with the library and not on theoretical aspects) and if you want you can answer, if not maybe someone else will do it.

Thank you for your help till this point so, and excuse me.