Commit 7e4b0fbfdd1e15780ff1899bb442f70ffc83791b

Werner Lemberg 2013-11-06T07:14:49

[truetype] Improve emulation of vertical metrics. This commit also improves the start values of vertical phantom points. Kudos to Greg Hitchcock for help. * src/truetype/ttgload.c (TT_Get_VMetrics): Add parameter to pass `yMax' value. Replace code with fixed Microsoft definition. (tt_get_metrics): Updated. (TT_LOADER_SET_PP): Add explanation how to initialize phantom points, taken from both the OpenType specification and private communication with Greg (which will eventually be added to the standard). Fix horizontal position of `pp3' and `pp4'. * src/truetype/ttgload.h: Updated. * src/truetype/ttdriver.c (tt_get_advances): Updated. * docs/CHANGES: Updated.

diff --git a/ChangeLog b/ChangeLog
index c44b3f9..115333d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2013-11-06  Werner Lemberg  <wl@gnu.org>
+
+	[truetype] Improve emulation of vertical metrics.
+
+	This commit also improves the start values of vertical phantom
+	points.  Kudos to Greg Hitchcock for help.
+
+	* src/truetype/ttgload.c (TT_Get_VMetrics): Add parameter to pass
+	`yMax' value.  Replace code with fixed Microsoft definition.
+	(tt_get_metrics): Updated.
+	(TT_LOADER_SET_PP): Add explanation how to initialize phantom
+	points, taken from both the OpenType specification and private
+	communication with Greg (which will eventually be added to the
+	standard).
+	Fix horizontal position of `pp3' and `pp4'.
+
+	* src/truetype/ttgload.h: Updated.
+
+	* src/truetype/ttdriver.c (tt_get_advances): Updated.
+
+	* docs/CHANGES: Updated.
+
 2013-11-05  Werner Lemberg  <wl@gnu.org>
 
 	* builds/windows/vc2010/freetype.vcxproj: s/v110/v100/.
diff --git a/docs/CHANGES b/docs/CHANGES
index ddd24e0..c843046 100644
--- a/docs/CHANGES
+++ b/docs/CHANGES
@@ -11,6 +11,10 @@ CHANGES BETWEEN 2.5 and 2.5.1
     - Many fields of the `PCLT' table in SFNT based fonts (if accessed
       with `FT_Get_Sfnt_Table') were computed incorrectly.
 
+    - In TrueType fonts,  hinting of composite glyphs  could sometimes
+      deliver  incorrect positions  of  components or  even  distorted
+      shapes.
+
 
   II. IMPORTANT CHANGES
 
@@ -76,6 +80,12 @@ CHANGES BETWEEN 2.5 and 2.5.1
       to set  selector bit 6  to get  results for selector  bits 7-10,
       which is wrong.
 
+    - Improved computation  of emulated vertical metrics  for TrueType
+      fonts.
+
+    - Fixed horizontal start-up position of vertical phantom points in
+      TrueType bytecode.
+
 
 ======================================================================
 
diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c
index fb25706..36d23a2 100644
--- a/src/truetype/ttdriver.c
+++ b/src/truetype/ttdriver.c
@@ -215,7 +215,8 @@
         FT_UShort  ah;
 
 
-        TT_Get_VMetrics( face, start + nn, &tsb, &ah );
+        /* since we don't need `tsb', we use zero for `yMax' parameter */
+        TT_Get_VMetrics( face, start + nn, 0, &tsb, &ah );
         advances[nn] = ah;
       }
     }
diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c
index 0fc3492..0c3c7bd 100644
--- a/src/truetype/ttgload.c
+++ b/src/truetype/ttgload.c
@@ -85,45 +85,30 @@
   /*************************************************************************/
   /*                                                                       */
   /* Return the vertical metrics in font units for a given glyph.          */
-  /* Greg Hitchcock from Microsoft told us that if there were no `vmtx'    */
-  /* table, typoAscender/Descender from the `OS/2' table would be used     */
-  /* instead, and if there were no `OS/2' table, use ascender/descender    */
-  /* from the `hhea' table.  But that is not what Microsoft's rasterizer   */
-  /* apparently does: It uses the ppem value as the advance height, and    */
-  /* sets the top side bearing to be zero.                                 */
+  /* See macro `TT_LOADER_SET_PP' below for explanations.                  */
   /*                                                                       */
   FT_LOCAL_DEF( void )
   TT_Get_VMetrics( TT_Face     face,
                    FT_UInt     idx,
+                   FT_Pos      yMax,
                    FT_Short*   tsb,
                    FT_UShort*  ah )
   {
     if ( face->vertical_info )
       ( (SFNT_Service)face->sfnt )->get_metrics( face, 1, idx, tsb, ah );
 
-#if 1             /* Empirically determined, at variance with what MS said */
-
-    else
-    {
-      *tsb = 0;
-      *ah  = face->root.units_per_EM;
-    }
-
-#else      /* This is what MS said to do.  It isn't what they do, however. */
-
     else if ( face->os2.version != 0xFFFFU )
     {
-      *tsb = face->os2.sTypoAscender;
+      *tsb = face->os2.sTypoAscender - yMax;
       *ah  = face->os2.sTypoAscender - face->os2.sTypoDescender;
     }
+
     else
     {
-      *tsb = face->horizontal.Ascender;
+      *tsb = face->horizontal.Ascender - yMax;
       *ah  = face->horizontal.Ascender - face->horizontal.Descender;
     }
 
-#endif
-
     FT_TRACE5(( "  advance height (font units): %d\n", *ah ));
     FT_TRACE5(( "  top side bearing (font units): %d\n", *tsb ));
   }
@@ -146,6 +131,7 @@
                      &left_bearing,
                      &advance_width );
     TT_Get_VMetrics( face, glyph_index,
+                     loader->bbox.yMax,
                      &top_bearing,
                      &advance_height );
 
@@ -1263,18 +1249,78 @@
   }
 
 
-  /* Calculate the four phantom points.                     */
-  /* The first two stand for horizontal origin and advance. */
-  /* The last two stand for vertical advance and origin.    */
+  /*
+   * Calculate the phantom points
+   *
+   * Defining the right side bearing (rsb) as
+   *
+   *   rsb = aw - (lsb + xmax - xmin)
+   *
+   * (with `aw' the advance width, `lsb' the left side bearing, and `xmin'
+   * and `xmax' the glyph's minimum and maximum x value), the OpenType
+   * specification defines the initial position of horizontal phantom points
+   * as
+   *
+   *   pp1 = (xmin - lsb, 0)      ,
+   *   pp2 = (pp1 + aw, 0)        .
+   *
+   * However, the specification lacks the precise definition of vertical
+   * phantom points.  Greg Hitchcock provided the following explanation.
+   *
+   * - a `vmtx' table is present
+   *
+   *   For any glyph, the minimum and maximum y values (`ymin' and `ymax')
+   *   are given in the `glyf' table, the top side bearing (tsb) and advance
+   *   height (ah) are given in the `vmtx' table.  The bottom side bearing
+   *   (bsb) is then calculated as
+   *
+   *     bsb = ah - (tsb + ymax - ymin)       ,
+   *
+   *   and the initial position of vertical phantom points is
+   *
+   *     pp3 = (x, ymax + tsb)       ,
+   *     pp4 = (x, pp3 - ah)         .
+   *
+   *   See below for value `x'.
+   *
+   * - no `vmtx' table in the font
+   *
+   *   If there is an `OS/2' table, we set
+   *
+   *     DefaultAscender = sTypoAscender       ,
+   *     DefaultDescender = sTypoDescender     ,
+   *
+   *   otherwise we use data from the `hhea' table:
+   *
+   *     DefaultAscender = Ascender         ,
+   *     DefaultDescender = Descender       .
+   *
+   *   With these two variables we can now set
+   *
+   *     ah = DefaultAscender - sDefaultDescender    ,
+   *     tsb = DefaultAscender - yMax                ,
+   *
+   *   and proceed as if a `vmtx' table was present.
+   *
+   * Usually we have
+   *
+   *   x = aw / 2      ,
+   *
+   * but there is a compatibility case where it can be set to
+   *
+   *   x = -DefaultDescender -
+   *         ((DefaultAscender - DefaultDescender - aw) / 2)     .
+   *
+   */
 #define TT_LOADER_SET_PP( loader )                                          \
           do {                                                              \
             (loader)->pp1.x = (loader)->bbox.xMin - (loader)->left_bearing; \
             (loader)->pp1.y = 0;                                            \
             (loader)->pp2.x = (loader)->pp1.x + (loader)->advance;          \
             (loader)->pp2.y = 0;                                            \
-            (loader)->pp3.x = 0;                                            \
-            (loader)->pp3.y = (loader)->top_bearing + (loader)->bbox.yMax;  \
-            (loader)->pp4.x = 0;                                            \
+            (loader)->pp3.x = (loader)->advance / 2;                        \
+            (loader)->pp3.y = (loader)->bbox.yMax + (loader)->top_bearing;  \
+            (loader)->pp4.x = (loader)->advance / 2;                        \
             (loader)->pp4.y = (loader)->pp3.y - (loader)->vadvance;         \
           } while ( 0 )
 
diff --git a/src/truetype/ttgload.h b/src/truetype/ttgload.h
index 05f7588..3f1699e 100644
--- a/src/truetype/ttgload.h
+++ b/src/truetype/ttgload.h
@@ -43,6 +43,7 @@ FT_BEGIN_HEADER
   FT_LOCAL( void )
   TT_Get_VMetrics( TT_Face     face,
                    FT_UInt     idx,
+                   FT_Pos      yMax,
                    FT_Short*   tsb,
                    FT_UShort*  ah );