Commits

Lenard Lindstrom committed a976caa

freetype: underline position is now adjustable, removing the need for underscore font.Font emulation style

Comments (0)

Files changed (8)

docs/reST/ref/freetype.rst

       calculations unless overriden specifically in the \`render()` or
       \`get_size()` calls, via the 'style' parameter.
 
-   .. attribute:: underscore
-
-      | :sl:`Gets or sets the face's underscore style`
-      | :sg:`underscore -> bool`
-
-      Gets or sets whether the text will be underscored. Unlike underline,
-      the underscore is 1 pixel thick and is positioned descender pixels
-      below the baseline. This is for compatibility with font.Font.
-
    .. attribute:: strong
 
       | :sl:`Gets or sets the face's strong style`
       dimensions are enlarged. A wide style of strength 1/12 is
       equivalent to the font.Font bold style. The default is 1/36.
 
+   .. attribute:: underline_adjustment
+
+      | :sl:`Gets or sets an adjustment factor for the underline position`
+      | :sg:`underline_adjustment -> float`
+
+      Gets or sets a factor which is multiplied with the face's underline
+      offset to adjust the underline position. Accepted values are between
+      -2.0 and 2.0 inclusive. A negative value can turn an underline into
+      a strikethrough or overline. A value of 1.0 restores the default
+      position.
+
    .. attribute:: fixed_width
 
       | :sl:`Gets whether the face is fixed-width`

docs/ref/freetype.html

 </div>
 <table border="1" class="toc docutils">
 <colgroup>
-<col width="41%" />
+<col width="39%" />
 <col width="1%" />
-<col width="59%" />
+<col width="60%" />
 </colgroup>
 <tbody valign="top">
 <tr><td><a class="toc reference external" href="freetype.html#pygame.freetype.Face.name">pygame.freetype.Face.name</a></td>
 <td>—</td>
 <td>Gets or sets the face&#8217;s underline style</td>
 </tr>
-<tr><td><a class="toc reference external" href="freetype.html#pygame.freetype.Face.underscore">pygame.freetype.Face.underscore</a></td>
-<td>—</td>
-<td>Gets or sets the face&#8217;s underscore style</td>
-</tr>
 <tr><td><a class="toc reference external" href="freetype.html#pygame.freetype.Face.strong">pygame.freetype.Face.strong</a></td>
 <td>—</td>
 <td>Gets or sets the face&#8217;s strong style</td>
 <td>—</td>
 <td>Gets or sets the strength of the strong or wide styles</td>
 </tr>
+<tr><td><a class="toc reference external" href="freetype.html#pygame.freetype.Face.underline_adjustment">pygame.freetype.Face.underline_adjustment</a></td>
+<td>—</td>
+<td>Gets or sets an adjustment factor for the underline position</td>
+</tr>
 <tr><td><a class="toc reference external" href="freetype.html#pygame.freetype.Face.fixed_width">pygame.freetype.Face.fixed_width</a></td>
 <td>—</td>
 <td>Gets whether the face is fixed-width</td>
 </dd></dl>
 
 <dl class="definition attribute">
-<dt class="title" id="pygame.freetype.Face.underscore">
-<tt class="descname">underscore</tt><a class="headerlink" href="#pygame.freetype.Face.underscore" title="Permalink to this definition">¶</a></dt>
-<dd><div class="line-block">
-<div class="line"><span class="summaryline">Gets or sets the face&#8217;s underscore style</span></div>
-<div class="line"><span class="signature">underscore -&gt; bool</span></div>
-</div>
-<p>Gets or sets whether the text will be underscored. Unlike underline,
-the underscore is 1 pixel thick and is positioned descender pixels
-below the baseline. This is for compatibility with font.Font.</p>
-</dd></dl>
-
-<dl class="definition attribute">
 <dt class="title" id="pygame.freetype.Face.strong">
 <tt class="descname">strong</tt><a class="headerlink" href="#pygame.freetype.Face.strong" title="Permalink to this definition">¶</a></dt>
 <dd><div class="line-block">
 </dd></dl>
 
 <dl class="definition attribute">
+<dt class="title" id="pygame.freetype.Face.underline_adjustment">
+<tt class="descname">underline_adjustment</tt><a class="headerlink" href="#pygame.freetype.Face.underline_adjustment" title="Permalink to this definition">¶</a></dt>
+<dd><div class="line-block">
+<div class="line"><span class="summaryline">Gets or sets an adjustment factor for the underline position</span></div>
+<div class="line"><span class="signature">underline_adjustment -&gt; float</span></div>
+</div>
+<p>Gets or sets a factor which is multiplied with the face&#8217;s underline
+offset to adjust the underline position. Accepted values are between
+-2.0 and 2.0 inclusive. A negative value can turn an underline into
+a strikethrough or overline. A value of 1.0 restores the default
+position.</p>
+</dd></dl>
+
+<dl class="definition attribute">
 <dt class="title" id="pygame.freetype.Face.fixed_width">
 <tt class="descname">fixed_width</tt><a class="headerlink" href="#pygame.freetype.Face.fixed_width" title="Permalink to this definition">¶</a></dt>
 <dd><div class="line-block">

examples/freetype_misc.py

     screen = pygame.display.set_mode((800, 600))
     screen.fill (colors["grey_light"])
 
+    face.underline_adjustment = 0.25
     face.render((screen, 32, 32), "Hello World", colors["red"], colors['grey_dark'],
             ptsize=64, style=freetype.STYLE_UNDERLINE|freetype.STYLE_OBLIQUE)
 

src/doc/freetype_doc.h

 
 #define DOC_FACEUNDERLINE "underline -> bool\nGets or sets the face's underline style"
 
-#define DOC_FACEUNDERSCORE "underscore -> bool\nGets or sets the face's underscore style"
-
 #define DOC_FACESTRONG "strong -> bool\nGets or sets the face's strong style"
 
 #define DOC_FACEOBLIQUE "oblique -> bool\nGets or sets the face's oblique style"
 
 #define DOC_FACESTRENGTH "strength -> float\nGets or sets the strength of the strong or wide styles"
 
+#define DOC_FACEUNDERLINEADJUSTMENT "underline_adjustment -> float\nGets or sets an adjustment factor for the underline position"
+
 #define DOC_FACEFIXEDWIDTH "fixed_width -> bool\nGets whether the face is fixed-width"
 
 #define DOC_FACEANTIALIASED "antialiased -> bool\nFace antialiasing mode"
  underline -> bool
 Gets or sets the face's underline style
 
-pygame.freetype.Face.underscore
- underscore -> bool
-Gets or sets the face's underscore style
-
 pygame.freetype.Face.strong
  strong -> bool
 Gets or sets the face's strong style
  strength -> float
 Gets or sets the strength of the strong or wide styles
 
+pygame.freetype.Face.underline_adjustment
+ underline_adjustment -> float
+Gets or sets an adjustment factor for the underline position
+
 pygame.freetype.Face.fixed_width
  fixed_width -> bool
 Gets whether the face is fixed-width
 static PyObject *_ftface_getfixedwidth(PyObject *, void *);
 static PyObject *_ftface_getstrength(PgFaceObject *, void *);
 static int _ftface_setstrength(PgFaceObject *, PyObject *, void *);
+static PyObject *_ftface_getunderlineadjustment(PgFaceObject *, void *);
+static int _ftface_setunderlineadjustment(PgFaceObject *, PyObject *, void *);
 
 static PyObject *_ftface_getvertical(PyObject *, void *);
 static int _ftface_setvertical(PyObject *, PyObject *, void *);
         (void *)FT_STYLE_UNDERLINE
     },
     {
-        "underscore",
-        (getter)_ftface_getstyle_flag,
-        (setter)_ftface_setstyle_flag,
-        DOC_FACEUNDERSCORE,
-        (void *)FT_STYLE_UNDERSCORE
-    },
-    {
         "wide",
         (getter)_ftface_getstyle_flag,
         (setter)_ftface_setstyle_flag,
         (setter)_ftface_setstrength,
         DOC_FACESTRENGTH,
         0
+    }, 
+    {
+        "underline_adjustment",
+        (getter)_ftface_getunderlineadjustment,
+        (setter)_ftface_setunderlineadjustment,
+        DOC_FACEUNDERLINEADJUSTMENT,
+        0
     },
     {
         "ucs4",
         obj->ptsize = -1;
         obj->style = FT_STYLE_NORMAL;
         obj->strength = PGFT_DBL_DEFAULT_STRENGTH;
+        obj->underline_adjustment = 1.0;
         obj->vertical = 0;
         obj->antialias = 1;
         obj->kerning = 0;
         obj->origin = 0;
 	obj->pad = 0;
         obj->do_transform = 0;
-        obj->transform.xx = 0x00010000;
-        obj->transform.xy = 0x00000000;
-        obj->transform.yx = 0x00000000;
-        obj->transform.yy = 0x00010000;
-   }
+        obj->transform.xx = FX16_ONE;
+        obj->transform.xy = 0;
+        obj->transform.yx = 0;
+        obj->transform.yy = FX16_ONE;
+    }
     return (PyObject *)obj;
 }
 
     return 0;
 }
 
+static PyObject *
+_ftface_getunderlineadjustment(PgFaceObject *self, void *closure)
+{
+    return PyFloat_FromDouble(self->underline_adjustment);
+}
+
+static int
+_ftface_setunderlineadjustment(PgFaceObject *self, PyObject *value,
+                               void *closure)
+{
+    PyObject *adjustmentobj = PyNumber_Float(value);
+    double adjustment;
+
+    if (!adjustmentobj) {
+        return -1;
+    }
+    adjustment = PyFloat_AS_DOUBLE(adjustmentobj);
+    Py_DECREF(adjustmentobj);
+    if (adjustment < -2.0 || adjustment > 2.0) {
+        char msg[100];
+
+        sprintf(msg,
+                "underline adjustment value %.4e is outside range [-2.0, 2.0]",
+                adjustment);
+        PyErr_SetString(PyExc_ValueError, msg);
+        return -1;
+    }
+    self->underline_adjustment = adjustment;
+    return 0;
+}
+
 
 /** ucs4 unicode text handling attribute */
 static PyObject *
     DEC_CONST(STYLE_STRONG);
     DEC_CONST(STYLE_OBLIQUE);
     DEC_CONST(STYLE_UNDERLINE);
-    DEC_CONST(STYLE_UNDERSCORE);
     DEC_CONST(STYLE_WIDE);
 
     DEC_CONST(BBOX_EXACT);
 #define FT_STYLE_STRONG     0x01
 #define FT_STYLE_OBLIQUE    0x02
 #define FT_STYLE_UNDERLINE  0x04
-#define FT_STYLE_UNDERSCORE 0x08
-#define FT_STYLE_WIDE       0x10
+#define FT_STYLE_WIDE       0x08
 #define FT_STYLE_DEFAULT    0xFF
 
 /* Bounding box modes */
     FT_Int16 ptsize;
     FT_Byte style;
     double strength;
+    double underline_adjustment;
     FT_Byte vertical;
     FT_Byte antialias;
     FT_Byte kerning;

src/freetype/ft_render.c

         FT_STYLE_STRONG |
         FT_STYLE_OBLIQUE |
         FT_STYLE_UNDERLINE |
-        FT_STYLE_UNDERSCORE |
         FT_STYLE_WIDE;
 
     return style > max_style;
                   "the underline style is unsupported for rotated text");
             return -1;
         }
-        if (mode->style & FT_STYLE_UNDERSCORE) {
-            PyErr_SetString(PyExc_ValueError,
-                  "the underscore style is unsupported for rotated text");
-            return -1;
-        }
         if (mode->render_flags & FT_RFLAG_PAD) {
             PyErr_SetString(PyExc_ValueError,
                   "padding is unsupported for rotated text");
                   "the underline style is unsupported for vertical text");
             return -1;
         }
-        if (mode->style & FT_STYLE_UNDERSCORE) {
-            PyErr_SetString(PyExc_ValueError,
-                  "the underscore style is unsupported for vertical text");
-            return -1;
-        }
     }
 
     return 0;
             text->width, FX6_TRUNC(FX6_CEIL(text->underline_size)),
             surface, fg_color);
     }
-
-    if (mode->style & FT_STYLE_UNDERSCORE) {
-        surface->fill(
-            FX6_TRUNC(FX6_CEIL(left - text->offset.x)),
-            FX6_TRUNC(FX6_CEIL(top - text->descender)),
-            text->width, 1, surface, fg_color);
-    }
 }

src/freetype/ft_text.c

                 min_y = -ascender;
             }
             if (max_y <= -descender) {
-                max_y = -descender + /* underscore allowance */ FX6_ONE;
+                max_y = -descender + /* baseline */ FX6_ONE;
             }
         }
     }
-    else if (mode->style & FT_STYLE_UNDERSCORE) {
-        if (-ftext->descender >= max_y) {
-            max_y = -ftext->descender + /* underscore allowance */ FX6_ONE;
-        }
-    }
 
     if (mode->style & FT_STYLE_UNDERLINE) {
         FT_Fixed scale = face->size->metrics.y_scale;
-        FT_Fixed underline_pos;
-        FT_Fixed underline_size;
+        FT_Fixed adjustment = DBL_TO_FX16(faceobj->underline_adjustment);
+        FT_Fixed pos;
+        FT_Fixed size;
+        FT_Fixed adjusted_pos;
         FT_Fixed max_y_underline;
 
-        underline_pos = -FT_MulFix(face->underline_position, scale) / 4; /*(1)*/
-        underline_size = FT_MulFix(face->underline_thickness, scale);
-        max_y_underline = underline_pos + underline_size / 2;
+        pos = -FT_MulFix(face->underline_position, scale);
+        adjusted_pos = FT_MulFix(pos, adjustment);
+        size = FT_MulFix(face->underline_thickness, scale);
+        max_y_underline = adjusted_pos + size / 2;
         if (max_y_underline > max_y) {
             max_y = max_y_underline;
         }
-        ftext->underline_pos = underline_pos;
-        ftext->underline_size = underline_size;
-
-        /*
-         * (1) HACK HACK HACK
-         *
-         * According to the FT documentation, 'underline_pos' is the offset
-         * to draw the underline in 26.6 FP, based on the text's baseline
-         * (negative values mean below the baseline).
-         *
-         * However, after scaling the underline position, the values for all
-         * fonts are WAY off (e.g. fonts with 32pt size get underline offsets
-         * of -14 pixels).
-         *
-         * Dividing the offset by 4, somehow, returns very sane results for
-         * all kind of fonts; the underline seems to fit perfectly between
-         * the baseline and bottom of the glyphs.
-         *
-         * We'll leave it like this until we can figure out what's wrong
-         * with it...
-         *
-         */
+        ftext->underline_pos = adjusted_pos;
+        ftext->underline_size = size;
     }
 
     text_width = FX6_CEIL(max_x) - FX6_FLOOR(min_x);
 
     /* load our sized face */
     face = _PGFT_GetFaceSized(ft, faceobj, mode->pt_size);
-
     if (!face) {
         return -1;
     }