Commit cba72a0b0f0256e61af8dc6cb1924b143b127dde

Werner Lemberg 2018-07-21T23:45:32

[pcf] Fix handling of the undefined glyph. This change makes the driver use the `defaultChar' property of PCF files. * src/pcf/pcf.h (PCF_FaceRec): Change type of `defaultChar' to unsigned. * src/pcf/pcfread.c (pcf_get_encodings): Read `defaultChar' as unsigned. Validate `defaultChar'. If `defaultChar' doesn't point to glyph index zero, swap glyphs with index zero and index `defaultChar' and adjust the encodings accordingly. * src/pcf/pcfdrivr.c (pcf_cmap_char_index, pcf_cmap_char_next, PCF_Glyph_Load): Undo change from 2002-06-16 which always enforced the first character in the font to be the default character.

diff --git a/ChangeLog b/ChangeLog
index d7879cd..dc31288 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2018-07-21  Werner Lemberg  <wl@gnu.org>
+
+	[pcf] Fix handling of the undefined glyph.
+
+	This change makes the driver use the `defaultChar' property of PCF
+	files.
+
+	* src/pcf/pcf.h (PCF_FaceRec): Change type of `defaultChar' to
+	unsigned.
+
+	* src/pcf/pcfread.c (pcf_get_encodings): Read `defaultChar' as
+	unsigned.
+	Validate `defaultChar'.
+	If `defaultChar' doesn't point to glyph index zero, swap glyphs with
+	index zero and index `defaultChar' and adjust the encodings
+	accordingly.
+
+	* src/pcf/pcfdrivr.c (pcf_cmap_char_index, pcf_cmap_char_next,
+	PCF_Glyph_Load): Undo change from 2002-06-16 which always enforced
+	the first character in the font to be the default character.
+
 2018-07-20  Armin Hasitzka  <prince.cherusker@gmail.com>
 
 	Move the legacy fuzz target to the `freetype-testing' repository.
diff --git a/src/pcf/pcf.h b/src/pcf/pcf.h
index f0390cb..cb98cc7 100644
--- a/src/pcf/pcf.h
+++ b/src/pcf/pcf.h
@@ -124,10 +124,14 @@ FT_BEGIN_HEADER
   } PCF_AccelRec, *PCF_Accel;
 
 
+  /*
+   * This file uses X11 terminology for PCF data; an `encoding' in X11 speak
+   * is the same as a `character code' in FreeType speak.
+   */
   typedef struct  PCF_EncodingRec_
   {
     FT_Long    enc;
-    FT_UShort  glyph;
+    FT_UShort  glyph;  /* an index into PCF_Face's `metrics' array */
 
   } PCF_EncodingRec, *PCF_Encoding;
 
@@ -153,7 +157,7 @@ FT_BEGIN_HEADER
     FT_ULong       nencodings;
     PCF_Encoding   encodings;
 
-    FT_Short       defaultChar;
+    FT_UShort      defaultChar;
 
     FT_ULong       bitmapsFormat;
 
diff --git a/src/pcf/pcfdrivr.c b/src/pcf/pcfdrivr.c
index c9b7796..1a4f453 100644
--- a/src/pcf/pcfdrivr.c
+++ b/src/pcf/pcfdrivr.c
@@ -63,6 +63,10 @@ THE SOFTWARE.
 #define FT_COMPONENT  trace_pcfdriver
 
 
+  /*
+   * This file uses X11 terminology for PCF data; an `encoding' in X11 speak
+   * is the same as a `character code' in FreeType speak.
+   */
   typedef struct  PCF_CMapRec_
   {
     FT_CMapRec    root;
@@ -123,7 +127,7 @@ THE SOFTWARE.
 
       if ( charcode == code )
       {
-        result = encodings[mid].glyph + 1;
+        result = encodings[mid].glyph;
         break;
       }
 
@@ -161,7 +165,7 @@ THE SOFTWARE.
 
       if ( charcode == code )
       {
-        result = encodings[mid].glyph + 1;
+        result = encodings[mid].glyph;
         goto Exit;
       }
 
@@ -175,7 +179,7 @@ THE SOFTWARE.
     if ( min < cmap->num_encodings )
     {
       charcode = (FT_ULong)encodings[min].enc;
-      result   = encodings[min].glyph + 1;
+      result   = encodings[min].glyph;
     }
 
   Exit:
@@ -187,6 +191,7 @@ THE SOFTWARE.
     }
     else
       *acharcode = (FT_UInt32)charcode;
+
     return result;
   }
 
@@ -512,9 +517,6 @@ THE SOFTWARE.
 
     stream = face->root.stream;
 
-    if ( glyph_index > 0 )
-      glyph_index--;
-
     metric = face->metrics + glyph_index;
 
     bitmap->rows       = (unsigned int)( metric->ascent +
diff --git a/src/pcf/pcfread.c b/src/pcf/pcfread.c
index ffea820..06a1a5e 100644
--- a/src/pcf/pcfread.c
+++ b/src/pcf/pcfread.c
@@ -933,6 +933,10 @@ THE SOFTWARE.
   }
 
 
+  /*
+   * This file uses X11 terminology for PCF data; an `encoding' in X11 speak
+   * is the same as a character code in FreeType speak.
+   */
   static FT_Error
   pcf_get_encodings( FT_Stream  stream,
                      PCF_Face   face )
@@ -943,8 +947,10 @@ THE SOFTWARE.
     int           firstCol, lastCol;
     int           firstRow, lastRow;
     FT_ULong      nencoding;
-    FT_UShort     encodingOffset;
+    FT_UShort     defaultCharRow, defaultCharCol;
+    FT_UShort     encodingOffset, defaultCharEncodingOffset;
     int           i, j;
+    FT_Byte*      pos;
     FT_ULong      k;
     PCF_Encoding  encoding = NULL;
 
@@ -964,13 +970,16 @@ THE SOFTWARE.
 
     format = FT_GET_ULONG_LE();
 
+    /* X11's reference implementation uses the equivalent to  */
+    /* `FT_GET_SHORT' for `defaultChar', however this doesn't */
+    /* make sense for most encodings.                         */
     if ( PCF_BYTE_ORDER( format ) == MSBFirst )
     {
       firstCol          = FT_GET_SHORT();
       lastCol           = FT_GET_SHORT();
       firstRow          = FT_GET_SHORT();
       lastRow           = FT_GET_SHORT();
-      face->defaultChar = FT_GET_SHORT();
+      face->defaultChar = FT_GET_USHORT();
     }
     else
     {
@@ -978,7 +987,7 @@ THE SOFTWARE.
       lastCol           = FT_GET_SHORT_LE();
       firstRow          = FT_GET_SHORT_LE();
       lastRow           = FT_GET_SHORT_LE();
-      face->defaultChar = FT_GET_SHORT_LE();
+      face->defaultChar = FT_GET_USHORT_LE();
     }
 
     FT_Stream_ExitFrame( stream );
@@ -1019,6 +1028,47 @@ THE SOFTWARE.
 
     FT_TRACE5(( "\n" ));
 
+    defaultCharRow = face->defaultChar >> 8;
+    defaultCharCol = face->defaultChar & 0xFF;
+
+    /* validate default character */
+    if ( defaultCharRow < (FT_UShort)firstRow ||
+         defaultCharRow > (FT_UShort)lastRow  ||
+         defaultCharCol < (FT_UShort)firstCol ||
+         defaultCharCol > (FT_UShort)lastCol  )
+    {
+      face->defaultChar = firstRow * 256 + firstCol;
+      FT_TRACE0(( "pcf_get_encodings:"
+                  " Invalid default character set to %d\n",
+                  face->defaultChar ));
+    }
+
+    /* FreeType mandates that glyph index 0 is the `undefined glyph',  */
+    /* which PCF calls the `default character'.  For this reason, we   */
+    /* swap the positions of glyph index 0 and the index corresponding */
+    /* to `defaultChar' in case they are different.                    */
+
+    /* `stream->cursor' still points at the beginning of the frame; */
+    /* we can thus easily get the offset to the default character   */
+    pos = stream->cursor +
+            2 * ( ( defaultCharRow - (FT_UShort)firstRow ) * 256 +
+                  defaultCharCol - (FT_UShort)firstCol );
+
+    if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+      defaultCharEncodingOffset = FT_PEEK_USHORT( pos );
+    else
+      defaultCharEncodingOffset = FT_PEEK_USHORT_LE( pos );
+
+    if ( defaultCharEncodingOffset )
+    {
+      /* do the swapping */
+      PCF_MetricRec  tmp = face->metrics[defaultCharEncodingOffset];
+
+
+      face->metrics[defaultCharEncodingOffset] = face->metrics[0];
+      face->metrics[0]                         = tmp;
+    }
+
     k = 0;
     for ( i = firstRow; i <= lastRow; i++ )
     {
@@ -1026,7 +1076,7 @@ THE SOFTWARE.
       {
         /* X11's reference implementation uses the equivalent to  */
         /* `FT_GET_SHORT', however PCF fonts with more than 32768 */
-        /* characters (e.g. `unifont.pcf') clearly show that an   */
+        /* characters (e.g., `unifont.pcf') clearly show that an  */
         /* unsigned value is needed.                              */
         if ( PCF_BYTE_ORDER( format ) == MSBFirst )
           encodingOffset = FT_GET_USHORT();
@@ -1035,6 +1085,11 @@ THE SOFTWARE.
 
         if ( encodingOffset != 0xFFFFU )
         {
+          if ( encodingOffset == defaultCharEncodingOffset )
+            encodingOffset = 0;
+          else if ( encodingOffset == 0 )
+            encodingOffset = defaultCharEncodingOffset;
+
           encoding[k].enc   = i * 256 + j;
           encoding[k].glyph = encodingOffset;