takkaO/OpenFontRender

Font height is not computed correctly -- too short

MikeyMoMo opened this issue · 2 comments

I found that the font height was being calculated to a lower value than it should so I created a test string with the largest caps and two descenders and, still, it is coming up with too low of a number.

The font is being loaded with

  if (ofr.loadFont(BritanicBoldTTF, sizeof(BritanicBoldTTF))) {
    Serial.println("Render loadFont error for BritanicBoldTTF.");
    while (1);
  }
  ofr.setFontSize(50);

The display is being updated with

ofr.setCursor(dispWidth / 2, yPos); ofr.cprintf(myBuildString.c_str());

I calculate the size with one of these two (both return too small of a size)

myBuildString = "ABCabcjyWM";  // Include caps and descenders to get the full height
ofrFontHeight = ofr.getTextHeight(myBuildString.c_str());

char sizingChars[] = "ABCabcjyWM";
ofrFontHeight = ofr.getTextHeight(sizingChars);

They both return 42. Adding 8 makes it match the setFontSize parm. Maybe I should just use this. I don't know if it tracks to the larger sizes I am using... But the rest are numbers so it does not matter for now.

Then I do a fill rect with this

tft.fillRect(0, yPos, dispWidth, ofrFontHeight, SkyBlue); // dispWidth is the full 480 pixels.

Either way, it does not blank out the descenders with the initially returned value since it is calculating the height to a lower number than it should. It makes WiFi look like WjFi because there was a descender right under where the first i is in the previous status message.

When I added 5 to the height, it almost cleaned it all with fillRect. When I added 8, it was totally clean. I am using it on numbers, too, but, since there are no descenders, it does not show the problem.

Thanks for the library. I am enjoying using it. Sorry to report so much but there are some minor problems around the edges. It's expected with a new contribution.

Mike

Hi @MikeyMoMo
Sorry for the delay in replying 🙇‍♂️

I will try to verify this issue in my environment, so please tell me the smallest code and font you are using that can reproduce the problem.

Thank you.

I have changed my mind about what the problem is, now. It seems like the real problem is that there is blank pixels above the top of the font. I had noticed this before but, with the attached program, you can see that I am drawing a rectangle around the text using the returns from text width and height calls and it is low in the box with blank space at the top and the descenders are hanging out of the box. So, maybe the real problem is that blank space at the top. It gets larger as the font size gets larger and more of the letters hang outside the box, even those without descenders.

I hope this helps you decide where the problem is.

Thanks for the library. I am making extensive use of it in a large project I have. But I did have to adjust the cursor setting to account for the blank lines at the top of the characters. I will be very glad to fix my program when you find out what is happening here. If I can be of any more help, please let me know.

The following is a modification of one of your examples. To make it complete, you will need to add in the NotoSans.h tab, of course. I draw the rectangle before the text to see the left/right alignment and it is to-the-pixel perfect. The top of the A and y just take out one pixel of the red box. So it seems to boil down to the blank area above the top of the rendered font. It does happen on the other fonts I am using in my project, not just NotoSans. I am using BritanicBold from sizes 32 to 80 and an LCD font at size 190.

Thanks,
Mike

/*
  Font generation:

  This sketch uses a ttf (TrueType Font) file that has been converted to a binary
  format in a byte array. See NotoSans_Bold.h tab of this sketch.

    https://en.wikipedia.org/wiki/TrueType

  The font used in this sketch is free to use and from Google:

    https://fonts.google.com/

  TTF font files can be VERY large, fortunately there are python and online tools that can
  be used to convert a ttf font to a new ttf font file with a subset of the characters
  (called "font subsetting") as needed by the sketch. For example maybe only 0-9 and : are
  needed to display the time. It’s important to check the licence a particular font before
  use and subsetting. Here is a simple subsetting online tool:

    https://products.aspose.app/font/generator/ttf-to-ttf

  To use this tool:
   1. Drag and drop file
   2. Copy and paste the "Font symbols" you want, here are ASCII characters 20 (space) and 33-126:
       !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
   3. Make sure you include the "space" character if you are going to use it (renders as a box otherwise)
   4. Click "Generate"
   5. Save the file and convert to an array

  There is a more sophisticated font subsetting tool here:

    https://www.fontsquirrel.com/tools/webfont-generator

  The font must be in ttf format, it can then be converted to a binary array using the tool here:

    https://notisrac.github.io/FileToCArray/

  To use this tool:
   1. Drag and drop the ttf file on "Browse..." button
   2. Untick box "static"
   3. Click "Convert"
   4. Click "Save as file" and move the header file to sketch folder
   5. Open the sketch in IDE
   6. Include the header file containing the array (NotoSans_Bold.h in this example)

  ttf font files can be very large, there are python and online tools that can
  be used to convert a ttf font to a new font file with a subset of the characters
  that will be used by the sketch. For example maybe only 0-9 and : are needed to
  display the time.

  Note that translating a ttf font character to a glyph that can be rendered on
  screen is a processor intensive operation. This means the rendering speed will
  be much lower than bitmap encoded fonts which just need to be copied to the
  display.

  TrueType font characters are scalable, so different font files are not needed to
  render different size characters on the TFT screen. This is very convenient, just
  use the setFontSize(font_size) function (see below).
*/

#include "TFT_eSPI.h"
#include "NotoSans_Bold.h"
#include "OpenFontRender.h"
#include "TickingTimeBombTTF.h"

//#define TTF_FONT NotoSans_Bold

// This sprite will require ~18 kbytes of RAM
#define WIDTH  200
#define HEIGHT 300

TFT_eSPI tft;

OpenFontRender ofr;

char str_buf[] = "Aa3jxy";

void setup() {
  Serial.begin(115200); delay(1000);

  tft.begin();
  tft.setRotation(1);

  tft.setSwapBytes(true);
  tft.invertDisplay(false);
  tft.fillScreen(TFT_BLACK);
  ofr.setDrawer(tft); // Link renderer to sprite (font will be rendered in sprite spr)
}

void loop() {

  ofr.setFontColor(TFT_WHITE, TFT_BLACK);
  if (ofr.loadFont(NotoSans_Bold, sizeof(NotoSans_Bold))) {
    Serial.println("Render initialize error");
    return;
  }

  // Print in font sizes 20 to 200 (tiny sizes may be unreadable!)
  for (uint16_t font_size = 20; font_size <= 200; font_size += 5) {
    tft.fillScreen(TFT_BLACK);
    ofr.setCursor(20, 20);
    ofr.setFontSize(font_size);
    int myTH = ofr.getTextHeight(str_buf);
    int myTW = ofr.getTextWidth(str_buf);
    tft.drawRect(20, 20, myTW, myTH+20, TFT_RED);
    ofr.printf(str_buf); // Centre on cursor position
    delay(2500);
  }
  ofr.unloadFont();
}