Hello there,

I’m currently trying to fix Blender’s text vertical alignment feature. I originally implemented this feature in Blender for the e-interiores project. What I had was perfect for my needs at the time. However let’s look at one of the trickiest ones: centered alignment.

The expected result would be to have the text center to be in-between the lowercase texts give it or take, yet it’s way off. What happens is that the code is taking into account the line size, and making sure the text base-line is located mid-way into the line height. As an example, let’s see how Google Slides handles vertical alignment:

I changed the text background color, so it is more obvious what is going on. Basically we have a “box” where the text fits in, and then this box is vertically centralized. That rises the question, how to determine this box dimensions? As it turn out these are called ascent and descent.

Ascent: The recommended distance from the baseline to the line above.
Descent: The recommended distance from the baseline to the line below.

In this example, using the Indie Flower font we have descent of about 20%, and ascent the 80% left. According to FontForge this is indeed correct – there I get 819 and 205 as their values:

However, and now it is the caveat, with freetype2 I’m getting 994 and (-)500 (67% and 33%). And why does it matter? Well, Blender uses freetype2 for its internal font management, so it matters big time.

I even scrapped a simple freetype2 example to test it isolated from Blender, and the result is the same.

/**
 * Example of FreeType2 library usage.
 *
 * How to build:
 * gcc example.c -o example -I/usr/include/freetype2 -lfreetype example.c
 */


#include <ft2build.h>
#include FT_FREETYPE_H

int main(int argc, char** argv) {
  FT_Library library;
  FT_Face face;

  /* Error handling is non-existent in this example. */
  FT_Error error;

  char* filename;

  if (argc != 2) {
    fprintf(stderr, "usage: %s font\n", argv[0]);
    exit(1);
  }

  /* First argument. */
  filename = argv[1];

  /* Initialize library. */
  error = FT_Init_FreeType(&library);

  /* Create face object. */
  error = FT_New_Face(library, filename, 0, &face);

  const short em_size = face->ascender - face->descender;
  const float descender = -(float)face->descender / em_size;
  const float ascender = 1.0f - descender;

  printf("Descender: %d (%4.2f)\n"
         "Ascender: %d (%4.2f)\n",
         face->descender,
         descender,
         face->ascender, ascender);

  FT_Done_Face(face);
  FT_Done_FreeType(library);

  return 0;
}

For the records, the final alignment change is something like:

  /* Offset from baseline. */
  float y_offset = descent - (em_size * 0.5f);

But I still need to figure out the correct way to get the ascent/descent from freetype2.
 
My best guess at the moment is that face->ascender is not the ascent but something else altogether.

Wish me luck!

Update : I decided to contact the freetype developers, I will post an update here once I hear back from them.

Update 2 : I got a reply by FontForge developer Khaled Hosny.

As it turned out, FontForge was using the hardcoded values (80%) when opening ttf files, and the proper Font ascent could be found in a different tab there (Font Info → OS/2 → Metrics) instead of (Font Info → General).

The baffling part of all this, is that Google Slides do seem to be using the same 80% value for their internal padding. I may as well hardcode it in Blender and move on. The results may be good enough (definitively better than what I get from using the real ascent/descent).

つづく