Font textbox overflow: Different methods
authorDalai Felinto <dfelinto@gmail.com>
Wed, 12 Sep 2018 16:00:30 +0000 (13:00 -0300)
committerDalai Felinto <dfelinto@gmail.com>
Mon, 26 Nov 2018 23:47:33 +0000 (21:47 -0200)
Sometimes the text doesn't fit. What to do in this case?

* Overflow: The default behaviour still is to overflow the text.
* Truncated: If any text box is defined we can also not draw the text
  that goes outside the text boxes.
* Scale to Fit: For single-text box texts we can scale down the text until
  it fits.

To support textboxes we are bisecting the scale until we find a good
match. Right now the hardcoded iteration limit is 20, and the threshold 0.0001f.

An alternative in the future would be to tackle this by integrating existing
layout engines such as HarfBuzz.

Note: Scale to fit won't work for multiple text-boxes if any of them has
either width or height as zero.

Reviewers: campbellbarton
Differential Revision: https://developer.blender.org/D3874

Feature development sponsored by Viddyoze.

release/scripts/startup/bl_ui/properties_data_curve.py
source/blender/blenkernel/intern/font.c
source/blender/draw/modes/edit_text_mode.c
source/blender/makesdna/DNA_curve_types.h
source/blender/makesrna/intern/rna_curve.c

index 2625c23..e7f9de5 100644 (file)
@@ -441,6 +441,7 @@ class DATA_PT_text_boxes(CurveButtonsPanelText, Panel):
         text = context.curve
 
         layout.operator("font.textbox_add", icon='ADD')
+        layout.prop(text, "overflow", text="Overflow")
 
         for i, box in enumerate(text.text_boxes):
 
index f6e1fab..9c24cc4 100644 (file)
@@ -364,7 +364,8 @@ static VChar *find_vfont_char(VFontData *vfd, unsigned int character)
 }
 
 static void build_underline(Curve *cu, ListBase *nubase, const rctf *rect,
-                            float yofs, float rot, int charidx, short mat_nr)
+                            float yofs, float rot, int charidx, short mat_nr,
+                            const float font_size)
 {
        Nurb *nu2;
        BPoint *bp;
@@ -417,18 +418,19 @@ static void build_underline(Curve *cu, ListBase *nubase, const rctf *rect,
                bp = nu2->bp;
        }
 
-       mul_v2_fl(bp[0].vec, cu->fsize);
-       mul_v2_fl(bp[1].vec, cu->fsize);
-       mul_v2_fl(bp[2].vec, cu->fsize);
-       mul_v2_fl(bp[3].vec, cu->fsize);
+       mul_v2_fl(bp[0].vec, font_size);
+       mul_v2_fl(bp[1].vec, font_size);
+       mul_v2_fl(bp[2].vec, font_size);
+       mul_v2_fl(bp[3].vec, font_size);
 }
 
 static void buildchar(Curve *cu, ListBase *nubase, unsigned int character, CharInfo *info,
-                      float ofsx, float ofsy, float rot, int charidx)
+                      float ofsx, float ofsy, float rot, int charidx,
+                      const float fsize)
 {
        BezTriple *bezt1, *bezt2;
        Nurb *nu1 = NULL, *nu2 = NULL;
-       float *fp, fsize, shear, x, si, co;
+       float *fp, shear, x, si, co;
        VFontData *vfd = NULL;
        VChar *che = NULL;
        int i;
@@ -448,7 +450,6 @@ static void buildchar(Curve *cu, ListBase *nubase, unsigned int character, CharI
 #endif
 
        /* make a copy at distance ofsx, ofsy with shear */
-       fsize = cu->fsize;
        shear = cu->shear;
        si = sinf(rot);
        co = cosf(rot);
@@ -635,6 +636,27 @@ struct TempLineInfo {
        int   wspace_nr;  /* number of whitespaces of line */
 };
 
+typedef struct VFontToCurveIter {
+       int iteraction;
+       float scale_to_fit;
+       struct {
+               float min;
+               float max;
+       } bisect;
+       bool ok;
+       int status;
+} VFontToCurveIter;
+
+enum {
+       VFONT_TO_CURVE_INIT = 0,
+       VFONT_TO_CURVE_BISECT,
+       VFONT_TO_CURVE_SCALE_ONCE,
+       VFONT_TO_CURVE_DONE,
+};
+
+#define FONT_TO_CURVE_SCALE_ITERATIONS 20
+#define FONT_TO_CURVE_SCALE_THRESHOLD 0.0001f
+
 /**
  * Font metric values explained:
  *
@@ -651,7 +673,9 @@ struct TempLineInfo {
 #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,
+static bool vfont_to_curve(Object *ob, Curve *cu, int mode,
+                           VFontToCurveIter *iter_data,
+                           ListBase *r_nubase,
                            const wchar_t **r_text, int *r_text_len, bool *r_text_free,
                            struct CharTrans **r_chartransdata)
 {
@@ -671,11 +695,16 @@ bool BKE_vfont_to_curve_ex(Object *ob, Curve *cu, int mode, ListBase *r_nubase,
        int curbox;
        int selstart, selend;
        int cnr = 0, lnr = 0, wsnr = 0;
-       const wchar_t *mem;
+       const wchar_t *mem = NULL;
        wchar_t ascii;
        bool ok = false;
-       const float xof_scale = cu->xof / cu->fsize;
-       const float yof_scale = cu->yof / cu->fsize;
+       const float font_size = cu->fsize * iter_data->scale_to_fit;
+       const float xof_scale = cu->xof / font_size;
+       const float yof_scale = cu->yof / font_size;
+       int last_line = -1;
+       /* Length of the text disregarding \n breaks. */
+       float current_line_length = 0.0f;
+       float longest_line_length = 0.0f;
 
        /* Text at the beginning of the last used text-box (use for y-axis alignment).
         * We overallocate by one to simplify logic of getting last char. */
@@ -756,7 +785,7 @@ bool BKE_vfont_to_curve_ex(Object *ob, Curve *cu, int mode, ListBase *r_nubase,
        linedist = cu->linedist;
 
        curbox = 0;
-       textbox_scale(&tb_scale, &cu->tb[curbox], 1.0f / cu->fsize);
+       textbox_scale(&tb_scale, &cu->tb[curbox], 1.0f / font_size);
        use_textbox = (tb_scale.w != 0.0f);
 
 
@@ -768,7 +797,7 @@ bool BKE_vfont_to_curve_ex(Object *ob, Curve *cu, int mode, ListBase *r_nubase,
        oldvfont = NULL;
 
        for (i = 0; i < slen; i++) {
-               custrinfo[i].flag &= ~(CU_CHINFO_WRAP | CU_CHINFO_SMALLCAPS_CHECK);
+               custrinfo[i].flag &= ~(CU_CHINFO_WRAP | CU_CHINFO_SMALLCAPS_CHECK | CU_CHINFO_OVERFLOW);
        }
 
        for (i = 0; i <= slen; i++) {
@@ -837,6 +866,7 @@ makebreak:
                {
                        //              fprintf(stderr, "linewidth exceeded: %c%c%c...\n", mem[i], mem[i+1], mem[i+2]);
                        for (j = i; j && (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) {
+                               bool dobreak = false;
                                if (mem[j] == ' ' || mem[j] == '-') {
                                        ct -= (i - (j - 1));
                                        cnr -= (i - (j - 1));
@@ -846,9 +876,9 @@ makebreak:
                                        xof = ct->xof;
                                        ct[1].dobreak = 1;
                                        custrinfo[i + 1].flag |= CU_CHINFO_WRAP;
-                                       goto makebreak;
+                                       dobreak = true;
                                }
-                               if (chartransdata[j].dobreak) {
+                               else if (chartransdata[j].dobreak) {
                                        //                              fprintf(stderr, "word too long: %c%c%c...\n", mem[j], mem[j+1], mem[j+2]);
                                        ct->dobreak = 1;
                                        custrinfo[i + 1].flag |= CU_CHINFO_WRAP;
@@ -856,6 +886,13 @@ makebreak:
                                        cnr -= 1;
                                        i--;
                                        xof = ct->xof;
+                                       dobreak = true;
+                               }
+                               if (dobreak) {
+                                       if (tb_scale.h == 0.0f) {
+                                               /* Note: If underlined text is truncated away, the extra space is also truncated. */
+                                               custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW;
+                                       }
                                        goto makebreak;
                                }
                        }
@@ -877,16 +914,30 @@ makebreak:
                        CLAMP_MIN(maxlen, lineinfo[lnr].x_min);
 
                        if ((tb_scale.h != 0.0f) &&
-                           (cu->totbox > (curbox + 1)) &&
                            ((-(yof - tb_scale.y)) > (tb_scale.h - linedist) - yof_scale))
                        {
-                               maxlen = 0;
-                               curbox++;
-                               i_textbox_array[curbox] = i + 1;
+                               if (cu->totbox > (curbox + 1)) {
+                                       maxlen = 0;
+                                       curbox++;
+                                       i_textbox_array[curbox] = i + 1;
 
-                               textbox_scale(&tb_scale, &cu->tb[curbox], 1.0f / cu->fsize);
+                                       textbox_scale(&tb_scale, &cu->tb[curbox], 1.0f / font_size);
 
-                               yof = MARGIN_Y_MIN;
+                                       yof = MARGIN_Y_MIN;
+                               }
+                               else if (last_line == -1) {
+                                       last_line = lnr + 1;
+                                       info->flag |= CU_CHINFO_OVERFLOW;
+                               }
+                       }
+
+                       current_line_length += xof;
+                       if (ct->dobreak) {
+                               current_line_length += twidth;
+                       }
+                       else {
+                               longest_line_length = MAX2(current_line_length, longest_line_length);
+                               current_line_length = 0.0f;
                        }
 
                        /* XXX, has been unused for years, need to check if this is useful, r4613 r5282 - campbell */
@@ -925,9 +976,9 @@ makebreak:
 
                        if (selboxes && (i >= selstart) && (i <= selend)) {
                                sb = &selboxes[i - selstart];
-                               sb->y = yof * cu->fsize - linedist * cu->fsize * 0.1f;
-                               sb->h = linedist * cu->fsize;
-                               sb->w = xof * cu->fsize;
+                               sb->y = yof * font_size - linedist * font_size * 0.1f;
+                               sb->h = linedist * font_size;
+                               sb->w = xof * font_size;
                        }
 
                        if (ascii == 32) {
@@ -944,11 +995,13 @@ makebreak:
                        xof += (twidth * wsfac * (1.0f + (info->kern / 40.0f)) ) + xtrax;
 
                        if (sb) {
-                               sb->w = (xof * cu->fsize) - sb->w;
+                               sb->w = (xof * font_size) - sb->w;
                        }
                }
                ct++;
        }
+       current_line_length += xof + twidth;
+       longest_line_length = MAX2(current_line_length, longest_line_length);
 
        cu->lines = 1;
        for (i = 0; i <= slen; i++) {
@@ -1050,7 +1103,7 @@ makebreak:
                                ct_last = chartransdata + (is_last_filled_textbox ? slen: i_textbox_next - 1);
                                lines = ct_last->linenr - ct_first->linenr + 1;
 
-                               textbox_scale(&tb_scale, &cu->tb[tb_index], 1.0f / cu->fsize);
+                               textbox_scale(&tb_scale, &cu->tb[tb_index], 1.0f / font_size);
                                /* The initial Y origin of the textbox is hardcoded to 1.0f * text scale. */
                                const float textbox_y_origin = 1.0f;
                                float yoff = 0.0f;
@@ -1133,7 +1186,7 @@ makebreak:
 
                        copy_m3_m4(cmat, cu->textoncurve->obmat);
                        mul_m3_m3m3(cmat, cmat, imat3);
-                       sizefac = normalize_v3(cmat[0]) / cu->fsize;
+                       sizefac = normalize_v3(cmat[0]) / font_size;
 
                        minx = miny = 1.0e20f;
                        maxx = maxy = -1.0e20f;
@@ -1226,13 +1279,15 @@ makebreak:
                ct = chartransdata;
                for (i = 0; i <= selend; i++, ct++) {
                        if (i >= selstart) {
-                               selboxes[i - selstart].x = ct->xof * cu->fsize;
-                               selboxes[i - selstart].y = ct->yof * cu->fsize;
+                               selboxes[i - selstart].x = ct->xof * font_size;
+                               selboxes[i - selstart].y = ct->yof * font_size;
                        }
                }
        }
 
-       if (ELEM(mode, FO_CURSUP, FO_CURSDOWN, FO_PAGEUP, FO_PAGEDOWN)) {
+       if (ELEM(mode, FO_CURSUP, FO_CURSDOWN, FO_PAGEUP, FO_PAGEDOWN) && 
+           iter_data->status == VFONT_TO_CURVE_INIT)
+       {
                ct = &chartransdata[ef->pos];
 
                if (ELEM(mode, FO_CURSUP, FO_PAGEUP) && ct->linenr == 0) {
@@ -1277,27 +1332,25 @@ makebreak:
 
                f = ef->textcurs[0];
 
-               f[0] = cu->fsize * (-0.1f * co + ct->xof);
-               f[1] = cu->fsize * ( 0.1f * si + ct->yof);
+               f[0] = font_size * (-0.1f * co + ct->xof);
+               f[1] = font_size * ( 0.1f * si + ct->yof);
 
-               f[2] = cu->fsize * ( 0.1f * co + ct->xof);
-               f[3] = cu->fsize * (-0.1f * si + ct->yof);
+               f[2] = font_size * ( 0.1f * co + ct->xof);
+               f[3] = font_size * (-0.1f * si + ct->yof);
 
-               f[4] = cu->fsize * ( 0.1f * co + 0.8f * si + ct->xof);
-               f[5] = cu->fsize * (-0.1f * si + 0.8f * co + ct->yof);
+               f[4] = font_size * ( 0.1f * co + 0.8f * si + ct->xof);
+               f[5] = font_size * (-0.1f * si + 0.8f * co + ct->yof);
 
-               f[6] = cu->fsize * (-0.1f * co + 0.8f * si + ct->xof);
-               f[7] = cu->fsize * ( 0.1f * si + 0.8f * co + ct->yof);
+               f[6] = font_size * (-0.1f * co + 0.8f * si + ct->xof);
+               f[7] = font_size * ( 0.1f * si + 0.8f * co + ct->yof);
 
        }
 
        if (mode == FO_SELCHANGE) {
                MEM_freeN(chartransdata);
                chartransdata = NULL;
-               goto finally;
        }
-
-       if (mode == FO_EDIT) {
+       else if (mode == FO_EDIT) {
                /* make nurbdata */
                BKE_nurbList_free(r_nubase);
 
@@ -1306,6 +1359,13 @@ makebreak:
                        unsigned int cha = (unsigned int) mem[i];
                        info = &(custrinfo[i]);
 
+                       if ((cu->overflow == CU_OVERFLOW_TRUNCATE) &&
+                           (ob && ob->mode != OB_MODE_EDIT) &&
+                           (info->flag & CU_CHINFO_OVERFLOW))
+                       {
+                               break;
+                       }
+
                        if (info->flag & CU_CHINFO_SMALLCAPS_CHECK) {
                                cha = towupper(cha);
                        }
@@ -1316,7 +1376,7 @@ makebreak:
                        }
                        /* We do not want to see any character for \n or \r */
                        if (cha != '\n')
-                               buildchar(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i);
+                               buildchar(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size);
 
                        if ((info->flag & CU_CHINFO_UNDERLINE) && (cha != '\n')) {
                                float ulwidth, uloverlap = 0.0f;
@@ -1343,17 +1403,134 @@ makebreak:
 
                                build_underline(cu, r_nubase,
                                                &rect, cu->ulpos - 0.05f,
-                                               ct->rot, i, info->mat_nr);
+                                               ct->rot, i, info->mat_nr,
+                                               font_size);
                        }
                        ct++;
                }
        }
 
-       ok = true;
+       if (iter_data->status == VFONT_TO_CURVE_SCALE_ONCE) {
+               /* That means we were in a final run, just exit. */
+               BLI_assert(cu->overflow == CU_OVERFLOW_SCALE);
+               iter_data->status = VFONT_TO_CURVE_DONE;
+       }
+       else if (cu->overflow == CU_OVERFLOW_NONE) {
+               /* Do nothing. */
+       }
+       else if ((tb_scale.h == 0.0f) && (tb_scale.w == 0.0f)) {
+               /* Do nothing. */
+       }
+       else if (cu->overflow == CU_OVERFLOW_SCALE) {
+               if ((cu->totbox == 1) && ((tb_scale.w == 0.0f) || (tb_scale.h == 0.0f))) {
+                       /* These are special cases, simpler to deal with. */
+                       if (tb_scale.w == 0.0f) {
+                               /* This is a potential vertical overflow.
+                                * Since there is no width limit, all the new lines are from line breaks. */
+                               if ((last_line != -1) && (lnr > last_line)) {
+                                       const float total_text_height = lnr * linedist;
+                                       iter_data->scale_to_fit = tb_scale.h / total_text_height;
+                                       iter_data->status = VFONT_TO_CURVE_SCALE_ONCE;
+                               }
+                       }
+                       else if (tb_scale.h == 0.0f) {
+                               /* This is a horizontal overflow. */
+                               if (lnr > 1) {
+                                       /* We make sure longest line before it broke can fit here. */
+                                       float scale_to_fit = tb_scale.w / (longest_line_length);
+                                       scale_to_fit -= FLT_EPSILON;
+
+                                       iter_data->scale_to_fit = scale_to_fit;
+                                       iter_data->status = VFONT_TO_CURVE_SCALE_ONCE;
+                               }
+                       }
+               }
+               else {
+                       /* This is the really complicated case, the best we can do is to iterate over
+                        * this function a few times until we get an acceptable result.
+                        *
+                        * Keep in mind that there is no single number that will make all fit to the end.
+                        * In a way, our ultimate goal is to get the highest scale that still leads to the
+                        * number of extra lines to zero.
+                        */
+                       if (iter_data->status == VFONT_TO_CURVE_INIT) {
+                               bool valid = true;
 
-finally:
+                               for (int tb_index = 0; tb_index <= curbox; tb_index++) {
+                                       TextBox *tb = &cu->tb[tb_index];
+                                       if ((tb->w == 0.0f) || (tb->h == 0.0f)) {
+                                               valid = false;
+                                               break;
+                                       }
+                               }
+
+                               if (valid && (last_line != -1) && (lnr > last_line)) {
+                                       const float total_text_height = lnr * linedist;
+                                       float scale_to_fit = tb_scale.h / total_text_height;
 
+                                       iter_data->bisect.max = 1.0f;
+                                       iter_data->bisect.min = scale_to_fit;
+
+                                       iter_data->status = VFONT_TO_CURVE_BISECT;
+                               }
+                       }
+                       else {
+                               BLI_assert(iter_data->status == VFONT_TO_CURVE_BISECT);
+                               /* Try to get the highest scale that gives us the exactly
+                                * number of lines we need. */
+                               bool valid = false;
+
+                               if ((last_line != -1) && (lnr > last_line)) {
+                                       /* It is overflowing, scale it down. */
+                                       iter_data->bisect.max = iter_data->scale_to_fit;
+                               }
+                               else {
+                                       /* It fits inside the textbox, scale it up. */
+                                       iter_data->bisect.min = iter_data->scale_to_fit;
+                                       valid = true;
+                               }
+
+                               /* Bisecting to try to find the best fit. */
+                               iter_data->scale_to_fit = (iter_data->bisect.max + iter_data->bisect.min) * 0.5f;
+
+                               /* We iterated enough or got a good enough result. */
+                               if ((!iter_data->iteraction--) ||
+                                   ((iter_data->bisect.max - iter_data->bisect.min) < (cu->fsize * FONT_TO_CURVE_SCALE_THRESHOLD)))
+                               {
+                                       if (valid) {
+                                               iter_data->status = VFONT_TO_CURVE_DONE;
+                                       }
+                                       else {
+                                               iter_data->scale_to_fit = iter_data->bisect.min;
+                                               iter_data->status = VFONT_TO_CURVE_SCALE_ONCE;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /* Scale to fit only works for single text box layouts. */
+       if (ELEM(iter_data->status,
+                VFONT_TO_CURVE_SCALE_ONCE,
+                VFONT_TO_CURVE_BISECT))
        {
+               /* Always cleanup before going to the scale-to-fit repetition. */
+               if (r_nubase != NULL) {
+                       BKE_nurbList_free(r_nubase);
+               }
+
+               if (chartransdata != NULL) {
+                       MEM_freeN(chartransdata);
+               }
+
+               if (ef == NULL) {
+                       MEM_freeN((void *)mem);
+               }
+               return true;
+       }
+       else {
+               ok = true;
+finally:
                if (r_text) {
                        *r_text = mem;
                        *r_text_len = slen;
@@ -1364,17 +1541,19 @@ finally:
                                MEM_freeN((void *)mem);
                        }
                }
-       }
 
-       if (chartransdata) {
-               if (ok && r_chartransdata) {
-                       *r_chartransdata = chartransdata;
-               }
-               else {
-                       MEM_freeN(chartransdata);
+               if (chartransdata) {
+                       if (ok && r_chartransdata) {
+                               *r_chartransdata = chartransdata;
+                       }
+                       else {
+                               MEM_freeN(chartransdata);
+                       }
                }
-       }
 
+               /* Store the effective scale, to use for the textbox lines. */
+               cu->fsize_realtime = font_size;
+       }
        return ok;
 
 #undef MARGIN_X_MIN
@@ -1384,6 +1563,37 @@ finally:
 #undef DESCENT
 #undef ASCENT
 
+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)
+{
+       VFontToCurveIter data = {
+               .iteraction = cu->totbox * FONT_TO_CURVE_SCALE_ITERATIONS,
+               .scale_to_fit = 1.0f,
+               .ok = true,
+               .status = VFONT_TO_CURVE_INIT,
+       };
+
+       do {
+               data.ok &= vfont_to_curve(ob,
+                                         cu,
+                                         mode,
+                                         &data,
+                                         r_nubase,
+                                         r_text,
+                                         r_text_len,
+                                         r_text_free,
+                                         r_chartransdata);
+       } while (data.ok && ELEM(data.status,
+                                VFONT_TO_CURVE_SCALE_ONCE,
+                                VFONT_TO_CURVE_BISECT));
+
+       return data.ok;
+}
+
+#undef FONT_TO_CURVE_SCALE_ITERATIONS
+#undef FONT_TO_CURVE_SCALE_THRESHOLD
+
 bool BKE_vfont_to_curve_nubase(Object *ob, int mode, ListBase *r_nubase)
 {
        BLI_assert(ob->type == OB_FONT);
index d186ee9..7d6aa5b 100644 (file)
@@ -215,7 +215,7 @@ static void edit_text_cache_populate_boxes(void *vedata, Object *ob)
                DRWShadingGroup *shading_group = shading_groups[is_active ? 0 : 1];
 
                vec[0] = cu->xof + tb->x;
-               vec[1] = cu->yof + tb->y + cu->fsize;
+               vec[1] = cu->yof + tb->y + cu->fsize_realtime;
                vec[2] = 0.001;
 
                mul_v3_m4v3(vec1, ob->obmat, vec);
index b8b1066..0cf60a9 100644 (file)
@@ -225,11 +225,12 @@ typedef struct Curve {
        /* edit, index in active nurb (BPoint or BezTriple) */
        int actvert;
 
-       char pad[4];
+       char overflow;
+       char spacemode, align_y;
+       char pad[3];
 
        /* font part */
        short lines;
-       char spacemode, align_y;
        float spacing, linedist, shear, fsize, wordspace, ulpos, ulheight;
        float xof, yof;
        float linewidth;
@@ -264,6 +265,8 @@ typedef struct Curve {
        char bevfac1_mapping, bevfac2_mapping;
 
        char pad2[2];
+       float fsize_realtime;
+       float pad3;
 
        void *batch_cache;
 } Curve;
@@ -340,6 +343,13 @@ enum {
        CU_ALIGN_Y_BOTTOM             = 4,
 };
 
+/* Curve.overflow. */
+enum {
+       CU_OVERFLOW_NONE              = 0,
+       CU_OVERFLOW_SCALE             = 1,
+       CU_OVERFLOW_TRUNCATE          = 2,
+};
+
 /* Nurb.flag */
 enum {
        CU_SMOOTH             = 1 << 0,
@@ -457,13 +467,14 @@ typedef enum eBezTriple_KeyframeType {
 
 /* CharInfo.flag */
 enum {
-       /* note: CU_CHINFO_WRAP and CU_CHINFO_SMALLCAPS_TEST are set dynamically */
+       /* note: CU_CHINFO_WRAP, CU_CHINFO_SMALLCAPS_TEST and CU_CHINFO_TRUNCATE are set dynamically */
        CU_CHINFO_BOLD            = 1 << 0,
        CU_CHINFO_ITALIC          = 1 << 1,
        CU_CHINFO_UNDERLINE       = 1 << 2,
        CU_CHINFO_WRAP            = 1 << 3,  /* wordwrap occurred here */
        CU_CHINFO_SMALLCAPS       = 1 << 4,
        CU_CHINFO_SMALLCAPS_CHECK = 1 << 5,  /* set at runtime, checks if case switching is needed */
+       CU_CHINFO_OVERFLOW        = 1 << 6,  /* Set at runtime, indicates char that doesn't fit in text boxes. */
 };
 
 /* mixed with KEY_LINEAR but define here since only curve supports */
index e967175..41849f3 100644 (file)
@@ -974,6 +974,13 @@ static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna)
                {0, NULL, 0, NULL, NULL}
        };
 
+       static const EnumPropertyItem prop_overflow_items[] = {
+               {CU_OVERFLOW_NONE, "NONE", 0, "Overflow", "Let the text overflow outside the text boxes"},
+               {CU_OVERFLOW_SCALE, "SCALE", 0, "Scale to Fit", "Scale down the text to fit inside the text boxes"},
+               {CU_OVERFLOW_TRUNCATE, "TRUNCATE", 0, "Truncate", "Truncate the text that would go outside the text boxes"},
+               {0, NULL, 0, NULL, NULL}
+       };
+
        /* Enums */
        prop = RNA_def_property(srna, "align_x", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_sdna(prop, NULL, "spacemode");
@@ -987,6 +994,14 @@ static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna)
        RNA_def_property_ui_text(prop, "Text Vertical Align", "Text vertical align from the object center");
        RNA_def_property_update(prop, 0, "rna_Curve_update_data");
 
+       prop = RNA_def_property(srna, "overflow", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "overflow");
+       RNA_def_property_enum_items(prop, prop_overflow_items);
+       RNA_def_property_enum_default(prop, CU_OVERFLOW_NONE);
+       RNA_def_property_ui_text(prop, "Textbox Overflow",
+                                "Handle the text behaviour when it doesn't fit in the text boxes");
+       RNA_def_property_update(prop, 0, "rna_Curve_update_data");
+
        /* number values */
        prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_NONE);
        RNA_def_property_float_sdna(prop, NULL, "fsize");