Commit 4694ea2b9519d8128cfdc537629c292fc9d627f3

Bram Tassyns 2009-09-02T13:06:33

Improve vertical metrics calculation (Savannah bug #27364). The calculation of `vertBearingX' is not defined in the OTF font spec so FreeType does a `best effort' attempt. However, this value is defined in the PDF and PostScript specs, and that algorithm is better than the one FreeType currently uses: FreeType: Use the middle of the bounding box as the X coordinate of the vertical origin. Adobe PDF spec: Use the middle of the horizontal advance vector as the X coordinate of the vertical origin. FreeType's algorithm goes wrong if you have a really small glyph (like the full-width, circle-like dot at the end of the sentence, as used in CJK scripts) with large bearings. With the FreeType algorithm this dot gets centered on the baseline; with the PDF algorithm it gets the correct location (in the top right). Note that this is a serious issue, it's like printing the dot at the end of a Roman sentence at the center of the textline instead of on the baseline like it should. So i believe the PDF spec's algorithm should be used in FreeType as well. The `vertBearingY' value for such small glyphs is also very strange if no `vmtx' information is present, since the height of the bbox is not representable for the height of the glyph visually (the whitespace up to the baseline is part of the glyph). The fix also includes some code for a better estimate of `vertBearingY'. * src/base/ftobjs.c (ft_synthesize_vertical_metrics): `vertBearingX' is now calculated as described by the Adobe PDF Spec. Estimate for `vertBearingY' now works better for small glyphs completely above or below the baseline into account. * src/cff/cffgload.c (cff_slot_load): `vertBearingX' is now calculated as described by the Adobe PDF Spec. Vertical metrics information was always ignored when FT_CONFIG_OPTION_OLD_INTERNALS was not defined. * src/truetype/ttgload.c (compute_glyph_metrics): `vertBearingX' is now calculated as described by the Adobe PDF Spec.

diff --git a/ChangeLog b/ChangeLog
index abcb6d7..82ce4f5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,47 @@
+2009-09-02  Bram Tassyns  <bramt@enfocus.be>
+
+	Improve vertical metrics calculation (Savannah bug #27364).
+
+	The calculation of `vertBearingX' is not defined in the OTF font
+	spec so FreeType does a `best effort' attempt.  However, this value
+	is defined in the PDF and PostScript specs, and that algorithm is
+	better than the one FreeType currently uses:
+
+	  FreeType: Use the middle of the bounding box as the X coordinate
+	            of the vertical origin.
+
+	  Adobe PDF spec: Use the middle of the horizontal advance vector as
+	                  the X coordinate of the vertical origin.
+
+	FreeType's algorithm goes wrong if you have a really small glyph
+	(like the full-width, circle-like dot at the end of the sentence, as
+	used in CJK scripts) with large bearings.  With the FreeType
+	algorithm this dot gets centered on the baseline; with the PDF
+	algorithm it gets the correct location (in the top right).  Note
+	that this is a serious issue, it's like printing the dot at the end
+	of a Roman sentence at the center of the textline instead of on the
+	baseline like it should. So i believe the PDF spec's algorithm
+	should be used in FreeType as well.
+
+	The `vertBearingY' value for such small glyphs is also very strange
+	if no `vmtx' information is present, since the height of the bbox is
+	not representable for the height of the glyph visually (the
+	whitespace up to the baseline is part of the glyph).  The fix also
+	includes some code for a better estimate of `vertBearingY'.
+
+	* src/base/ftobjs.c (ft_synthesize_vertical_metrics): `vertBearingX'
+	is now calculated as described by the Adobe PDF Spec.  Estimate for
+	`vertBearingY' now works better for small glyphs completely above or
+	below the baseline into account.
+
+	* src/cff/cffgload.c (cff_slot_load): `vertBearingX' is now
+	calculated as described by the Adobe PDF Spec.  Vertical metrics
+	information was always ignored when FT_CONFIG_OPTION_OLD_INTERNALS
+	was not defined.
+
+	* src/truetype/ttgload.c (compute_glyph_metrics): `vertBearingX' is
+	now calculated as described by the Adobe PDF Spec.
+
 2009-09-01  John Tytgat <John.Tytgat@esko.com>
 
 	Fix custom cmap for empty Type 1 font (Savannah bug #27294).
diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c
index dead68e..421540c 100644
--- a/src/base/ftobjs.c
+++ b/src/base/ftobjs.c
@@ -2404,12 +2404,24 @@
   ft_synthesize_vertical_metrics( FT_Glyph_Metrics*  metrics,
                                   FT_Pos             advance )
   {
+    FT_Pos  height = metrics->height;
+
+
+    /* compensate for glyph with bbox above/below the baseline */
+    if ( metrics->horiBearingY < 0 )
+    {
+      if ( height < metrics->horiBearingY )
+        height = metrics->horiBearingY;
+    }
+    else if ( metrics->horiBearingY > 0 )
+      height -= metrics->horiBearingY;
+
     /* the factor 1.2 is a heuristical value */
     if ( !advance )
-      advance = metrics->height * 12 / 10;
+      advance = height * 12 / 10;
 
-    metrics->vertBearingX = -( metrics->width / 2 );
-    metrics->vertBearingY = ( advance - metrics->height ) / 2;
+    metrics->vertBearingX = metrics->horiBearingX - metrics->horiAdvance / 2;
+    metrics->vertBearingY = ( advance - height ) / 2;
     metrics->vertAdvance  = advance;
   }
 
diff --git a/src/cff/cffgload.c b/src/cff/cffgload.c
index e132ab7..a5b3e84 100644
--- a/src/cff/cffgload.c
+++ b/src/cff/cffgload.c
@@ -2726,9 +2726,14 @@
         glyph->root.linearHoriAdvance           = decoder.glyph_width;
         glyph->root.internal->glyph_transformed = 0;
 
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
         has_vertical_info = FT_BOOL( face->vertical_info                   &&
                                      face->vertical.number_Of_VMetrics > 0 &&
                                      face->vertical.long_metrics           );
+#else
+        has_vertical_info = FT_BOOL( face->vertical_info                   &&
+                                     face->vertical.number_Of_VMetrics > 0 );
+#endif
 
         /* get the vertical metrics from the vtmx table if we have one */
         if ( has_vertical_info )
@@ -2819,7 +2824,8 @@
         metrics->horiBearingY = cbox.yMax;
 
         if ( has_vertical_info )
-          metrics->vertBearingX = -metrics->width / 2;
+          metrics->vertBearingX = metrics->horiBearingX -
+                                    metrics->horiAdvance / 2;
         else
           ft_synthesize_vertical_metrics( metrics,
                                           metrics->vertAdvance );
diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c
index b0f6810..32cb523 100644
--- a/src/truetype/ttgload.c
+++ b/src/truetype/ttgload.c
@@ -1594,10 +1594,28 @@
     glyph->metrics.horiBearingY = bbox.yMax;
     glyph->metrics.horiAdvance  = loader->pp2.x - loader->pp1.x;
 
-    /* Now take care of vertical metrics.  In the case where there is    */
-    /* no vertical information within the font (relatively common), make */
-    /* up some metrics by `hand'...                                      */
+    /* adjust advance width to the value contained in the hdmx table */
+    if ( !face->postscript.isFixedPitch  &&
+         IS_HINTED( loader->load_flags ) )
+    {
+      FT_Byte*  widthp;
+
+
+      widthp = tt_face_get_device_metrics( face,
+                                           size->root.metrics.x_ppem,
+                                           glyph_index );
 
+      if ( widthp )
+        glyph->metrics.horiAdvance = *widthp << 6;
+    }
+
+    /* set glyph dimensions */
+    glyph->metrics.width  = bbox.xMax - bbox.xMin;
+    glyph->metrics.height = bbox.yMax - bbox.yMin;
+
+    /* Now take care of vertical metrics.  In the case where there is */
+    /* no vertical information within the font (relatively common),   */
+    /* create some metrics manually                                   */
     {
       FT_Pos  top;      /* scaled vertical top side bearing  */
       FT_Pos  advance;  /* scaled vertical advance height    */
@@ -1686,30 +1704,12 @@
       /* XXX: for now, we have no better algorithm for the lsb, but it */
       /*      should work fine.                                        */
       /*                                                               */
-      glyph->metrics.vertBearingX = ( bbox.xMin - bbox.xMax ) / 2;
+      glyph->metrics.vertBearingX = glyph->metrics.horiBearingX -
+                                      glyph->metrics.horiAdvance / 2;
       glyph->metrics.vertBearingY = top;
       glyph->metrics.vertAdvance  = advance;
     }
 
-    /* adjust advance width to the value contained in the hdmx table */
-    if ( !face->postscript.isFixedPitch  &&
-         IS_HINTED( loader->load_flags ) )
-    {
-      FT_Byte*  widthp;
-
-
-      widthp = tt_face_get_device_metrics( face,
-                                           size->root.metrics.x_ppem,
-                                           glyph_index );
-
-      if ( widthp )
-        glyph->metrics.horiAdvance = *widthp << 6;
-    }
-
-    /* set glyph dimensions */
-    glyph->metrics.width  = bbox.xMax - bbox.xMin;
-    glyph->metrics.height = bbox.yMax - bbox.yMin;
-
     return 0;
   }
 
@@ -2013,7 +2013,7 @@
             break;
           }
         }
-        else 
+        else
           glyph->outline.flags |= FT_OUTLINE_IGNORE_DROPOUTS;
       }