]> git.imager.perl.org - imager.git/blobdiff - font.c
- fixes to verbose mode in Makefile.PL, also added a -v switch so you
[imager.git] / font.c
diff --git a/font.c b/font.c
index 15037ad7363cd55533fe0fe223e4b26db0cfaada..6046d7a7941111a8eb543cf4d272a49dfdf708f5 100644 (file)
--- a/font.c
+++ b/font.c
@@ -7,6 +7,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#ifdef HAVE_LIBT1
+#include <t1lib.h>
+#endif
+
+
 /*
 =head1 NAME
 
@@ -44,14 +49,6 @@ Some of these functions are internal.
 
 */
 
-
-
-
-
-
-
-
-
 /* 
 =item i_init_fonts()
 
@@ -88,6 +85,11 @@ i_init_fonts(int t1log) {
 static int t1_get_flags(char const *flags);
 static char *t1_from_utf8(char const *in, int len, int *outlen);
 
+static void t1_push_error(void);
+
+static int t1_active_fonts = 0;
+static int t1_initialized = 0;
+
 /* 
 =item i_init_t1(t1log)
 
@@ -100,6 +102,15 @@ undef_int
 i_init_t1(int t1log) {
   int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
   mm_log((1,"init_t1()\n"));
+
+  if (t1_active_fonts) {
+    mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
+    return 1;
+  }
+
+  if (t1_initialized) {
+    T1_CloseLib();
+  }
   
   if (t1log)
     init_flags |= LOGFILE;
@@ -109,6 +120,9 @@ i_init_t1(int t1log) {
   }
   T1_SetLogLevel(T1LOG_DEBUG);
   i_t1_set_aa(1); /* Default Antialias value */
+
+  ++t1_initialized;
+
   return(0);
 }
 
@@ -126,6 +140,7 @@ Shuts the t1lib font rendering engine down.
 void
 i_close_t1(void) {
   T1_CloseLib();
+  t1_initialized = 0;
 }
 
 
@@ -156,6 +171,8 @@ i_t1_new(char *pfb,char *afm) {
     if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
   }
 
+  ++t1_active_fonts;
+
   return font_id;
 }
 
@@ -172,6 +189,9 @@ Frees resources for a t1 font with given font id.
 int
 i_t1_destroy(int font_id) {
   mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
+
+  --t1_active_fonts;
+
   return T1_DeleteFont(font_id);
 }
 
@@ -294,11 +314,12 @@ function to get a strings bounding box given the font id and sizes
 =cut
 */
 
-void
+int
 i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char const *flags) {
   BBox bbox;
   BBox gbbox;
   int mod_flags = t1_get_flags(flags);
+  int advance;
   
   mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
   T1_LoadFont(fontnum);  /* FIXME: Here a return code is ignored - haw haw haw */ 
@@ -312,6 +333,7 @@ i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char
     bbox = T1_GetStringBBox(fontnum,str,len,0,mod_flags);
   }
   gbbox = T1_GetFontBBox(fontnum);
+  advance = T1_GetStringWidth(fontnum, str, len, 0, mod_flags);
   
   mm_log((1,"bbox: (%d,%d,%d,%d)\n",
          (int)(bbox.llx*points/1000),
@@ -322,14 +344,20 @@ i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char
          (int)(bbox.ury*points/1000) ));
 
 
-  cords[0]=((float)bbox.llx*points)/1000;
-  cords[2]=((float)bbox.urx*points)/1000;
+  cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
+  cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
+
+  cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
+  cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
+
+  cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
+  cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
 
-  cords[1]=((float)gbbox.lly*points)/1000;
-  cords[3]=((float)gbbox.ury*points)/1000;
+  cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
+  cords[BBOX_RIGHT_BEARING] = 
+    cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
 
-  cords[4]=((float)bbox.lly*points)/1000;
-  cords[5]=((float)bbox.ury*points)/1000;
+  return BBOX_RIGHT_BEARING+1;
 }
 
 
@@ -455,16 +483,283 @@ t1_from_utf8(char const *in, int len, int *outlen) {
   return out;
 }
 
+/*
+=item i_t1_has_chars(font_num, text, len, utf8, out)
+
+Check if the given characters are defined by the font.  Note that len
+is the number of bytes, not the number of characters (when utf8 is
+non-zero).
+
+out[char index] will be true if the character exists.
+
+Accepts UTF-8, but since T1 can only have 256 characters, any chars
+with values over 255 will simply be returned as false.
+
+Returns the number of characters that were checked.
+
+=cut
+*/
+
+int
+i_t1_has_chars(int font_num, const char *text, int len, int utf8,
+               char *out) {
+  int count = 0;
+  
+  mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n", 
+          font_num, text, len, utf8));
+
+  i_clear_error();
+  if (T1_LoadFont(font_num)) {
+    t1_push_error();
+    return 0;
+  }
+
+  while (len) {
+    unsigned long c;
+    if (utf8) {
+      c = i_utf8_advance(&text, &len);
+      if (c == ~0UL) {
+        i_push_error(0, "invalid UTF8 character");
+        return 0;
+      }
+    }
+    else {
+      c = (unsigned char)*text++;
+      --len;
+    }
+    
+    if (c >= 0x100) {
+      /* limit of 256 characters for T1 */
+      *out++ = 0;
+    }
+    else {
+      char const * name = T1_GetCharName(font_num, (unsigned char)c);
+
+      if (name) {
+        *out++ = strcmp(name, ".notdef") != 0;
+      }
+      else {
+        mm_log((2, "  No name found for character %lx\n", c));
+        *out++ = 0;
+      }
+    }
+    ++count;
+  }
+
+  return count;
+}
+
+/*
+=item i_t1_face_name(font_num, name_buf, name_buf_size)
+
+Copies the face name of the given C<font_num> to C<name_buf>.  Returns
+the number of characters required to store the name (which can be
+larger than C<name_buf_size>, including the space required to store
+the terminating NUL).
+
+If name_buf is too small (as specified by name_buf_size) then the name
+will be truncated.  name_buf will always be NUL termintaed.
+
+=cut
+*/
+
+int
+i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
+  char *name;
+
+  T1_errno = 0;
+  if (T1_LoadFont(font_num)) {
+    t1_push_error();
+    return 0;
+  }
+  name = T1_GetFontName(font_num);
+
+  if (name) {
+    strncpy(name_buf, name, name_buf_size);
+    name_buf[name_buf_size-1] = '\0';
+    return strlen(name) + 1;
+  }
+  else {
+    t1_push_error();
+    return 0;
+  }
+}
+
+int
+i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, 
+                 size_t name_buf_size) {
+  char *name;
+
+  i_clear_error();
+  if (ch > 0xFF) {
+    return 0;
+  }
+  if (T1_LoadFont(font_num)) {
+    t1_push_error();
+    return 0;
+  }
+  name = T1_GetCharName(font_num, (unsigned char)ch);
+  if (name) {
+    if (strcmp(name, ".notdef")) {
+      strncpy(name_buf, name, name_buf_size);
+      name_buf[name_buf_size-1] = '\0';
+      return strlen(name) + 1;
+    }
+    else {
+      return 0;
+    }
+  }
+  else {
+    t1_push_error();
+    return 0;
+  }
+}
+
+static void
+t1_push_error(void) {
+  switch (T1_errno) {
+  case 0: 
+    i_push_error(0, "No error"); 
+    break;
+
+#ifdef T1ERR_SCAN_FONT_FORMAT
+  case T1ERR_SCAN_FONT_FORMAT:
+    i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT"); 
+    break;
+#endif
+
+#ifdef T1ERR_SCAN_FILE_OPEN_ERR
+  case T1ERR_SCAN_FILE_OPEN_ERR:
+    i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR"); 
+    break;
+#endif
+
+#ifdef T1ERR_SCAN_OUT_OF_MEMORY
+  case T1ERR_SCAN_OUT_OF_MEMORY:
+    i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY"); 
+    break;
+#endif
+
+#ifdef T1ERR_SCAN_ERROR
+  case T1ERR_SCAN_ERROR:
+    i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR"); 
+    break;
+#endif
+
+#ifdef T1ERR_SCAN_FILE_EOF
+  case T1ERR_SCAN_FILE_EOF:
+    i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF"); 
+    break;
+#endif
+
+#ifdef T1ERR_PATH_ERROR
+  case T1ERR_PATH_ERROR:
+    i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR"); 
+    break;
+#endif
+
+#ifdef T1ERR_PARSE_ERROR
+  case T1ERR_PARSE_ERROR:
+    i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR"); 
+    break;
+#endif
+
+#ifdef T1ERR_TYPE1_ABORT
+  case T1ERR_TYPE1_ABORT:
+    i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT"); 
+    break;
+#endif
+
+#ifdef T1ERR_INVALID_FONTID
+  case T1ERR_INVALID_FONTID:
+    i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID"); 
+    break;
+#endif
+
+#ifdef T1ERR_INVALID_PARAMETER
+  case T1ERR_INVALID_PARAMETER:
+    i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER"); 
+    break;
+#endif
+
+#ifdef T1ERR_OP_NOT_PERMITTED
+  case T1ERR_OP_NOT_PERMITTED:
+    i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED"); 
+    break;
+#endif
+
+#ifdef T1ERR_ALLOC_MEM
+  case T1ERR_ALLOC_MEM:
+    i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM"); 
+    break;
+#endif
+
+#ifdef T1ERR_FILE_OPEN_ERR
+  case T1ERR_FILE_OPEN_ERR:
+    i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR"); 
+    break;
+#endif
+
+#ifdef T1ERR_UNSPECIFIED
+  case T1ERR_UNSPECIFIED:
+    i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED"); 
+    break;
+#endif
+
+#ifdef T1ERR_NO_AFM_DATA
+  case T1ERR_NO_AFM_DATA:
+    i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA"); 
+    break;
+#endif
+
+#ifdef T1ERR_X11
+  case T1ERR_X11:
+    i_push_error(T1ERR_X11, "X11"); 
+    break;
+#endif
+
+#ifdef T1ERR_COMPOSITE_CHAR
+  case T1ERR_COMPOSITE_CHAR:
+    i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR"); 
+    break;
+#endif
+
+  default:
+    i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
+  }
+}
+
 #endif /* HAVE_LIBT1 */
 
 
 /* Truetype font support */
-
 #ifdef HAVE_LIBTT
 
+/* These are enabled by default when configuring Freetype 1.x
+   I haven't a clue how to reliably detect it at compile time.
+
+   We need a compilation probe in Makefile.PL
+*/
+#define FTXPOST 1
+#define FTXERR18 1
+
 #include <freetype.h>
 #define TT_CHC 5
 
+#ifdef FTXPOST
+#include <ftxpost.h>
+#endif
+
+#ifdef FTXERR18
+#include <ftxerr18.h>
+#endif
+
+/* some versions of FT1.x don't seem to define this - it's font defined
+   so it won't change */
+#ifndef TT_MS_LANGID_ENGLISH_GENERAL
+#define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
+#endif
+
 /* convert a code point into an index in the glyph cache */
 #define TT_HASH(x) ((x) & 0xFF)
 
@@ -492,6 +787,10 @@ struct TT_Fonthandle_ {
   TT_Face_Properties properties;
   TT_Instancehandle instanceh[TT_CHC];
   TT_CharMap char_map;
+#ifdef FTXPOST
+  int loaded_names;
+  TT_Error load_cond;
+#endif
 };
 
 /* Defines */
@@ -499,6 +798,7 @@ struct TT_Fonthandle_ {
 #define USTRCT(x) ((x).z)
 #define TT_VALID( handle )  ( ( handle ).z != NULL )
 
+static void i_tt_push_error(TT_Error rc);
 
 /* Prototypes */
 
@@ -553,6 +853,15 @@ i_init_tt() {
     mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
     return(1);
   }
+
+#ifdef FTXPOST
+  error = TT_Init_Post_Extension( engine );
+  if (error) {
+    mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
+    return 1;
+  }
+#endif
+
   return(0);
 }
 
@@ -676,6 +985,8 @@ i_tt_new(char *fontname) {
   TT_Fonthandle *handle;
   unsigned short i,n;
   unsigned short platform,encoding;
+
+  i_clear_error();
   
   mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
   
@@ -693,6 +1004,7 @@ i_tt_new(char *fontname) {
       mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, 
               error )); 
     }
+    i_tt_push_error(error);
     return NULL;
   }
   
@@ -726,6 +1038,10 @@ i_tt_new(char *fontname) {
     handle->instanceh[i].smooth=-1;
   }
 
+#ifdef FTXPOST
+  handle->loaded_names = 0;
+#endif
+
   mm_log((1,"i_tt_new <- 0x%X\n",handle));
   return handle;
 }
@@ -967,8 +1283,7 @@ int
 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
                char *out) {
   int count = 0;
-  int inst;
-  mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
+  mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
           handle, text, len, utf8));
 
   while (len) {
@@ -1101,7 +1416,6 @@ i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
                         TT_Raster_Map *small_bit, int cords[6], 
                         char const* txt, int len, int smooth, int utf8 ) {
   unsigned long j;
-  int i;
   TT_F26Dot6 x,y;
   
   mm_log((1,"i_tt_render_all_glyphs( handle 0x%X, inst %d, bit 0x%X, small_bit 0x%X, txt '%.*s', len %d, smooth %d, utf8 %d)\n",
@@ -1283,8 +1597,6 @@ i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float p
     return 0;
   }
 
-  /*  ascent = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem ) / handle->properties.header->Units_Per_EM; */
-  
   if ( smooth ) i_tt_done_raster_map( &small_bit );
   return 1;
 }
@@ -1314,19 +1626,20 @@ Interface to text rendering into a single channel in an image
 */
 
 undef_int
-i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, int len, int smooth, int utf8 ) {
+i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, int len, int smooth, int utf8, int align ) {
 
-  int cords[6];
-  int ascent, st_offset;
+  int cords[BOUNDING_BOX_COUNT];
+  int ascent, st_offset, y;
   TT_Raster_Map bit;
   
   i_clear_error();
   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
   
-  ascent=cords[5];
-  st_offset=cords[0];
+  ascent=cords[BBOX_ASCENT];
+  st_offset=cords[BBOX_NEG_WIDTH];
+  y = align ? yb-ascent : yb;
 
-  i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , yb-ascent, channel, smooth );
+  i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
   i_tt_done_raster_map( &bit );
 
   return 1;
@@ -1351,19 +1664,20 @@ Interface to text rendering in a single color onto an image
 */
 
 undef_int
-i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char const* txt, int len, int smooth, int utf8) {
-  int cords[6];
-  int ascent, st_offset;
+i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char const* txt, int len, int smooth, int utf8, int align) {
+  int cords[BOUNDING_BOX_COUNT];
+  int ascent, st_offset, y;
   TT_Raster_Map bit;
 
   i_clear_error();
   
   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
   
-  ascent=cords[5];
-  st_offset=cords[0];
+  ascent=cords[BBOX_ASCENT];
+  st_offset=cords[BBOX_NEG_WIDTH];
+  y = align ? yb-ascent : yb;
 
-  i_tt_dump_raster_map2( im, &bit, xb+st_offset, yb-ascent, cl, smooth ); 
+  i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); 
   i_tt_done_raster_map( &bit );
 
   return 1;
@@ -1386,8 +1700,8 @@ Function to get texts bounding boxes given the instance of the font (internal)
 
 static
 undef_int
-i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 ) {
-  int i, upm, casc, cdesc, first;
+i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
+  int upm, casc, cdesc, first;
   
   int start    = 0;
   int width    = 0;
@@ -1395,7 +1709,7 @@ i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int c
   int gascent  = 0;
   int descent  = 0;
   int ascent   = 0;
-  
+  int rightb   = 0;
 
   unsigned long j;
   unsigned char *ustr;
@@ -1440,17 +1754,15 @@ i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int c
        descent  = (gm->bbox.yMin-63) / 64;
        first = 0;
       }
-      if (i == len-1) {
+      if (!len) { /* if at end of string */
        /* the right-side bearing - in case the right-side of a 
           character goes past the right of the advance width,
           as is common for italic fonts
        */
-       int rightb = gm->advance - gm->bearingX 
+       rightb = gm->advance - gm->bearingX 
          - (gm->bbox.xMax - gm->bbox.xMin);
        /* fprintf(stderr, "font info last: %d %d %d %d\n", 
           gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
-       if (rightb < 0)
-         width -= rightb/64;
       }
 
       ascent  = (ascent  >  casc ?  ascent : casc );
@@ -1458,13 +1770,18 @@ i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int c
     }
   }
   
-  cords[0]=start;
-  cords[1]=gdescent;
-  cords[2]=width;
-  cords[3]=gascent;
-  cords[4]=descent;
-  cords[5]=ascent;
-  return 1;
+  cords[BBOX_NEG_WIDTH]=start;
+  cords[BBOX_GLOBAL_DESCENT]=gdescent;
+  cords[BBOX_POS_WIDTH]=width;
+  if (rightb < 0)
+    cords[BBOX_POS_WIDTH] -= rightb / 64;
+  cords[BBOX_GLOBAL_ASCENT]=gascent;
+  cords[BBOX_DESCENT]=descent;
+  cords[BBOX_ASCENT]=ascent;
+  cords[BBOX_ADVANCE_WIDTH] = width;
+  cords[BBOX_RIGHT_BEARING] = rightb / 64;
+
+  return BBOX_RIGHT_BEARING + 1;
 }
 
 
@@ -1498,7 +1815,172 @@ i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], i
   return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
 }
 
+/*
+=item i_tt_face_name(handle, name_buf, name_buf_size)
+
+Retrieve's the font's postscript name.
+
+This is complicated by the need to handle encodings and so on.
+
+=cut
+ */
+int
+i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
+  TT_Face_Properties props;
+  int name_count;
+  int i;
+  TT_UShort platform_id, encoding_id, lang_id, name_id;
+  TT_UShort name_len;
+  TT_String *name;
+  int want_index = -1; /* an acceptable but not perfect name */
+  int score = 0;
+
+  i_clear_error();
+  
+  TT_Get_Face_Properties(handle->face, &props);
+  name_count = props.num_Names;
+  for (i = 0; i < name_count; ++i) {
+    TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
+                   &name_id);
+
+    TT_Get_Name_String(handle->face, i, &name, &name_len);
+
+    if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
+        && name_id == TT_NAME_ID_PS_NAME) {
+      int might_want_index = -1;
+      int might_score = 0;
+      if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
+          ||
+          (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
+        /* exactly what we want */
+        want_index = i;
+        break;
+      }
+      
+      if (platform_id == TT_PLATFORM_MICROSOFT
+          && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
+        /* any english is good */
+        might_want_index = i;
+        might_score = 9;
+      }
+      /* there might be something in between */
+      else {
+        /* anything non-unicode is better than nothing */
+        might_want_index = i;
+        might_score = 1;
+      }
+      if (might_score > score) {
+        score = might_score;
+        want_index = might_want_index;
+      }
+    }
+  }
+
+  if (want_index != -1) {
+    TT_Get_Name_String(handle->face, want_index, &name, &name_len);
+    
+    strncpy(name_buf, name, name_buf_size);
+    name_buf[name_buf_size-1] = '\0';
+
+    return strlen(name) + 1;
+  }
+  else {
+    i_push_error(0, "no face name present");
+    return 0;
+  }
+}
 
+void i_tt_dump_names(TT_Fonthandle *handle) {
+  TT_Face_Properties props;
+  int name_count;
+  int i;
+  TT_UShort platform_id, encoding_id, lang_id, name_id;
+  TT_UShort name_len;
+  TT_String *name;
+  
+  TT_Get_Face_Properties(handle->face, &props);
+  name_count = props.num_Names;
+  for (i = 0; i < name_count; ++i) {
+    TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
+                   &name_id);
+    TT_Get_Name_String(handle->face, i, &name, &name_len);
+
+    printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
+           encoding_id, lang_id, name_id);
+    if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
+      printf("(unicode)\n");
+    }
+    else {
+      printf("'%s'\n", name);
+    }
+  }
+}
+
+int
+i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, 
+                 size_t name_buf_size) {
+#ifdef FTXPOST
+  TT_Error rc;
+  TT_String *psname;
+  TT_UShort index;
+
+  i_clear_error();
+
+  if (!handle->loaded_names) {
+    TT_Post post;
+    mm_log((1, "Loading PS Names"));
+    handle->load_cond = TT_Load_PS_Names(handle->face, &post);
+    ++handle->loaded_names;
+  }
+
+  if (handle->load_cond) {
+    i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
+    return 0;
+  }
+  
+  index = TT_Char_Index(handle->char_map, ch);
+  if (!index) {
+    i_push_error(0, "no such character");
+    return 0;
+  }
+
+  rc = TT_Get_PS_Name(handle->face, index, &psname);
+
+  if (rc) {
+    i_push_error(rc, "error getting name");
+    return 0;
+  }
+
+  strncpy(name_buf, psname, name_buf_size);
+  name_buf[name_buf_size-1] = '\0';
+
+  return strlen(psname) + 1;
+#else
+  mm_log((1, "FTXPOST extension not enabled\n"));
+  i_clear_error();
+  i_push_error(0, "Use of FTXPOST extension disabled");
+
+  return 0;
+#endif
+}
+
+/*
+=item i_tt_push_error(code)
+
+Push an error message and code onto the Imager error stack.
+
+=cut
+*/
+static void
+i_tt_push_error(TT_Error rc) {
+#ifdef FTXERR18
+  TT_String const *msg = TT_ErrToString18(rc);
+
+  i_push_error(rc, msg);
+#else
+  i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
+#endif
+}
 
 #endif /* HAVE_LIBTT */