3D Text: improvements to vertical alignment
authorDalai Felinto <dfelinto@gmail.com>
Wed, 5 Sep 2018 01:21:12 +0000 (11:21 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Wed, 5 Sep 2018 01:33:14 +0000 (11:33 +1000)
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.

release/scripts/startup/bl_ui/properties_data_curve.py
source/blender/blenkernel/intern/font.c
source/blender/blenlib/BLI_vfontdata.h
source/blender/blenlib/intern/freetypefont.c
source/blender/makesdna/DNA_curve_types.h
source/blender/makesrna/intern/rna_curve.c

index 671cff4ebb4d8da66ec37cfb8143d37893876ecc..9ba6bc70892e9ffbf5192802f54024c01f012f7c 100644 (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):
index b5fba6d30e80e365343f506cc145fc521f563128..31de8d8d51e00d00afa4c68b45895f7392243cc0 100644 (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)
 {
index 1cc1ef17486b6d62f90e5f9ee6d9d543dcfc1426..892d084f5ee26a62c22cff15c5b52be565386581 100644 (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 {
index c7604b3cd6d60f976fe48746d67b9d14c7d4441b..3f3868bea455070bcd89863be9a687e7639b622a 100644 (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;
index 6e3573b9f8024357f9d142eae5bdab066418c4e1..7c7eaae49496577f9435f2d2428764db84e7092c 100644 (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 */
index 6b294b9b3cd06a2237ba4eb54a349b4972b9abfb..a90a5cdff903cc918273a6595bb87be18c6331a1 100644 (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}
        };