3D Text: improvements to vertical alignment

They way Blender handles vertical alignment is very buggy:

- Top-Base: It works perfectly.

- Bottom: It is actually bottom-baseline,
  and it fails when line size is != 1.0 when working with text boxes.

- Top: Poorly implemented, it should use font's ascent
  (recommended distance from baseline),
  so it has room for accents,
  but it's not one line distance far from the origin (as it is now).

- Center: Poorly implemented.
  This is tricky since there is no silver bullet.

To clear this situation I created a new option (Bottom-Baseline),
and addressed the issues above.
I'm getting the ascent and descent from freetype2,
and use this for padding above/below the text.
Also for vertically centering the text.
This commit is contained in:
Dalai Felinto 2018-09-05 11:21:12 +10:00 committed by Campbell Barton
parent 053669e4ae
commit 312af01fb4
6 changed files with 83 additions and 30 deletions

View File

@ -398,12 +398,13 @@ class DATA_PT_paragraph_alignment(CurveButtonsPanelText, Panel):
def draw(self, context):
layout = self.layout
layout.use_property_split = False
layout.use_property_split = True
text = context.curve
layout.row().prop(text, "align_x", expand=True)
layout.row().prop(text, "align_y", expand=True)
col = layout.column()
col.prop(text, "align_x", text="Horizontal")
col.prop(text, "align_y", text="Vertical")
class DATA_PT_paragraph_spacing(CurveButtonsPanelText, Panel):

View File

@ -635,6 +635,22 @@ struct TempLineInfo {
int wspace_nr; /* number of whitespaces of line */
};
/**
* Font metric values explained:
*
* Baseline: Line where the text "rests", used as the origin vertical position for the glyphs.
* Em height: Space most glyphs should fit within.
* Ascent: the recommended distance above the baseline to fit most characters.
* Descent: the recommended distance below the baseline to fit most characters.
*
* We obtain ascent and descent from the font itself (FT_Face->ascender / face->height).
* And in some cases it is even the same value as FT_Face->bbox.yMax/yMin (font top and bottom respectively).
*
* The em_height here is relative to FT_Face->bbox.
*/
#define ASCENT(vfd) ((vfd)->ascender * (vfd)->em_height)
#define DESCENT(vfd) ((vfd)->em_height - ASCENT(vfd))
bool BKE_vfont_to_curve_ex(Object *ob, Curve *cu, int mode, ListBase *r_nubase,
const wchar_t **r_text, int *r_text_len, bool *r_text_free,
struct CharTrans **r_chartransdata)
@ -1019,26 +1035,30 @@ makebreak:
/* top-baseline is default, in this case, do nothing */
if (cu->align_y != CU_ALIGN_Y_TOP_BASELINE) {
if (tb_scale.h != 0.0f) {
/* top and top-baseline are the same when text-boxes are used */
if (cu->align_y != CU_ALIGN_Y_TOP && i_textbox < slen) {
/* all previous textboxes are 'full', only align the last used text-box */
if (i_textbox < slen) {
/* All previous textboxes are 'full', only align the last used text-box. */
struct CharTrans *ct_textbox = chartransdata + i_textbox;
float yoff = 0.0f;
int lines;
struct CharTrans *ct_last, *ct_textbox;
ct_last = chartransdata + slen - 1;
ct_textbox = chartransdata + i_textbox;
/* The initial Y origin of the textbox is harcoded to 1.0f * text scale. */
const float textbox_y_origin = 1.0f;
lines = ct_last->linenr - ct_textbox->linenr + 1;
if (mem[slen - 1] == '\n') {
lines++;
}
if (cu->align_y == CU_ALIGN_Y_BOTTOM) {
yoff = (lines * linedist) - tb_scale.h;
}
else if (cu->align_y == CU_ALIGN_Y_CENTER) {
yoff = 0.5f * ((lines * linedist) - tb_scale.h);
switch (cu->align_y) {
case CU_ALIGN_Y_TOP_BASELINE:
break;
case CU_ALIGN_Y_TOP:
yoff = textbox_y_origin - ASCENT(vfd);
break;
case CU_ALIGN_Y_CENTER:
yoff = ((((vfd->em_height + (lnr - 1) * linedist) * 0.5f) - ASCENT(vfd)) -
(tb_scale.h * 0.5f) + textbox_y_origin);
break;
case CU_ALIGN_Y_BOTTOM_BASELINE:
yoff = textbox_y_origin + ((lnr - 1) * linedist) - tb_scale.h;
break;
case CU_ALIGN_Y_BOTTOM:
yoff = textbox_y_origin + ((lnr - 1) * linedist) - tb_scale.h + DESCENT(vfd);
break;
}
ct = ct_textbox;
@ -1049,18 +1069,25 @@ makebreak:
}
}
else {
/* non text-box case handled separately */
/* Non text-box case handled separately. */
ct = chartransdata;
float yoff = 0.0f;
if (cu->align_y == CU_ALIGN_Y_TOP) {
yoff = -linedist;
}
else if (cu->align_y == CU_ALIGN_Y_BOTTOM) {
yoff = (lnr - 1.0f) * linedist;
}
else if (cu->align_y == CU_ALIGN_Y_CENTER) {
yoff = (lnr - 2.0f) * linedist * 0.5f;
switch (cu->align_y) {
case CU_ALIGN_Y_TOP_BASELINE:
break;
case CU_ALIGN_Y_TOP:
yoff = -ASCENT(vfd);
break;
case CU_ALIGN_Y_CENTER:
yoff = ((vfd->em_height + (lnr - 1) * linedist) * 0.5f) - ASCENT(vfd);
break;
case CU_ALIGN_Y_BOTTOM_BASELINE:
yoff = (lnr - 1) * linedist;
break;
case CU_ALIGN_Y_BOTTOM:
yoff = (lnr - 1) * linedist + DESCENT(vfd);
break;
}
for (i = 0; i <= slen; i++) {
@ -1339,6 +1366,8 @@ finally:
#undef MARGIN_Y_MIN
}
#undef DESCENT
#undef ASCENT
bool BKE_vfont_to_curve_nubase(Object *ob, int mode, ListBase *r_nubase)
{

View File

@ -43,6 +43,9 @@ typedef struct VFontData {
struct GHash *characters;
char name[128];
float scale;
/* Calculated from the font. */
float em_height;
float ascender;
} VFontData;
typedef struct VChar {

View File

@ -359,10 +359,27 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf)
lcode = charcode = FT_Get_First_Char(face, &glyph_index);
}
/* Blender default BFont is not "complete". */
const bool complete_font = (face->ascender != 0) && (face->descender != 0) &&
(face->ascender != face->descender);
if (complete_font) {
/* We can get descender as well, but we simple store descender in relation to the ascender.
* Also note that descender is stored as a negative number. */
vfd->ascender = (float)face->ascender / (face->ascender - face->descender);
}
else {
vfd->ascender = 0.8f;
vfd->em_height = 1.0f;
}
/* Adjust font size */
if (face->bbox.yMax != face->bbox.yMin) {
vfd->scale = (float)(1.0 / (double)(face->bbox.yMax - face->bbox.yMin));
if (complete_font) {
vfd->em_height = (float)(face->ascender - face->descender) / (face->bbox.yMax - face->bbox.yMin);
}
}
else {
vfd->scale = 1.0f / 1000.0f;

View File

@ -334,7 +334,8 @@ enum {
CU_ALIGN_Y_TOP_BASELINE = 0,
CU_ALIGN_Y_TOP = 1,
CU_ALIGN_Y_CENTER = 2,
CU_ALIGN_Y_BOTTOM = 3,
CU_ALIGN_Y_BOTTOM_BASELINE = 3,
CU_ALIGN_Y_BOTTOM = 4,
};
/* Nurb.flag */

View File

@ -969,6 +969,8 @@ static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna)
{CU_ALIGN_Y_TOP, "TOP", 0, "Top", "Align text to the top"},
{CU_ALIGN_Y_CENTER, "CENTER", 0, "Center", "Align text to the middle"},
{CU_ALIGN_Y_BOTTOM, "BOTTOM", 0, "Bottom", "Align text to the bottom"},
{CU_ALIGN_Y_BOTTOM_BASELINE, "BOTTOM_BASELINE", 0, "Bottom Base-Line",
"Align text to the bottom but use the base-line of the text"},
{0, NULL, 0, NULL, NULL}
};