chiark / gitweb /
Slightly ugly hack to bias middle stems upwards
authorBen Harris <bjh21@bjh21.me.uk>
Wed, 20 Nov 2024 14:25:46 +0000 (14:25 +0000)
committerBen Harris <bjh21@bjh21.me.uk>
Wed, 20 Nov 2024 14:25:46 +0000 (14:25 +0000)
By default, FreeType 2.12.1 prefers to move the middle stems of
characters like 'B' and 'e' downwards, which is ugly.  By applying a
tiny tweak to the "hstem" commands for such stems we can persuade
FreeType to move them upwards instead.  This wasn't a problem when we
were using FontForge because FontForge didn't use proper stem hints on
the baseline.

bedstead.c

index b5d3b68bc5d9535d675386f35e400f99ffb1341b..193e02fafa1f6f8311ca7c0b7afc0365b5a19e32 100644 (file)
@@ -3863,6 +3863,47 @@ counters_done:
        printf(" -->");
 }
 
+/*
+ * The purpose of this function is to adjust a few hints to improve
+ * FreeType's rendering of certain characters at awkward sizes.  The
+ * letters 'B' and 'e' at 11 pixels per em under FreeType 2.12.1 are a
+ * good test case.
+ *
+ * It appears that when rendering these characters, FreeType fixes the
+ * top and bottom strokes to the top and bottom alignment zones.  If
+ * those are an even number of pixels apart, this means that the
+ * middle stroke falls precisely on a pixel boundary.  FreeType then
+ * has three choices: move the stem up to the pixel row above the
+ * centre, move it down to the row below, or anti-alias it across the
+ * two rows.  FreeType chooses to move the stem down, which makes the
+ * characters look top-heavy.  We'd much prefer FreeType to move the
+ * stem upwards.
+ *
+ * It turns out that this can be achieved just by making the middle
+ * stem hint slightly closer to the stem above that to the stem below.
+ * This just requires adjusting the width of the stem hint by one
+ * design unit.  As far as I can see, this is allowed by the Type 1
+ * font specification.  A stem hint must encompass the stem outline,
+ * but it's allowed to be larger than the stem.  And it practice it
+ * seems to work fine.
+ *
+ * To avoid surprising effects on other characters, this tweak is
+ * applied only to two rows and only if the character looks like it
+ * might benefit.  In particular, it's not applied to any stem hint
+ * that might be captured by an alignment zone.
+ */
+static bool
+middle_stem_tweak_wanted(enum hint_type hhints[YSIZE], int row)
+{
+       if (row == 4 &&
+           hhints[1] == hint_counter_stem &&
+           hhints[7] == hint_counter_stem) return true;
+       if (row == 5 &&
+           hhints[3] == hint_counter_stem &&
+           hhints[7] == hint_counter_stem) return true;
+       return false;
+}
+
 static void
 emit_hints(int vstems[XSIZE], int ledges[XSIZE], int redges[XSIZE],
           int hstems[YSIZE], int tedges[YSIZE], int bedges[YSIZE])
@@ -3877,7 +3918,12 @@ emit_hints(int vstems[XSIZE], int ledges[XSIZE], int redges[XSIZE],
        for (i = 0; i < YSIZE; i++) {
                switch (hhints[YSIZE - 1 - i]) {
                case hint_counter_stem:
-                       need_cntrmask = true; /* FALLTHROUGH */
+                       need_cntrmask = true;
+                       if (middle_stem_tweak_wanted(hhints, YSIZE - 1 - i)) {
+                               start = i * YPIX;
+                               size = YPIX + 1;
+                               break;
+                       } /* FALLTHROUGH */
                case hint_stem:
                        start = i * YPIX;
                        size = YPIX;