]> git.imager.perl.org - imager.git/blobdiff - font.c
properly increment $VERSION for Imager::Matrix2d
[imager.git] / font.c
diff --git a/font.c b/font.c
index 1b764675236175bf32a7eef075b58feb7dba698d..f6f69a877ca1730ef236df7449037c809b5e78ad 100644 (file)
--- a/font.c
+++ b/font.c
@@ -1,4 +1,5 @@
-#include "image.h"
+#include "imager.h"
+#include "imrender.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -7,9 +8,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-
-
-
+#ifdef HAVE_LIBT1
+#endif
 
 
 /*
@@ -23,13 +23,13 @@ font.c - implements font handling functions for t1 and truetype fonts
 
   #ifdef HAVE_LIBT1
   fontnum = i_t1_new(path_to_pfb, path_to_afm);
-  i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
+  i_t1_bbox(fontnum, points, "foo", 3, i_img_dim cords[BOUNDING_BOX_COUNT]);
   rc = i_t1_destroy(fontnum);
   #endif
 
   #ifdef HAVE_LIBTT
   handle = i_tt_new(path_to_ttf);
-  rc = i_tt_bbox(handle, points, "foo", 3, int cords[6]);
+  rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
   i_tt_destroy(handle);
 
   // and much more
@@ -50,363 +50,101 @@ Some of these functions are internal.
 */
 
 
+/* 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.
 
-
-
-
-
-
-/* 
-=item i_init_fonts()
-
-Initialize font rendering libraries if they are avaliable.
-
-=cut
+   We need a compilation probe in Makefile.PL
 */
+#define FTXPOST 1
+#define FTXERR18 1
 
-undef_int 
-i_init_fonts() {
-  mm_log((1,"Initializing fonts\n"));
+#include <freetype.h>
+#define TT_CHC 5
 
-#ifdef HAVE_LIBT1
-  init_t1();
-#endif
-  
-#ifdef HAVE_LIBTT
-  init_tt();
+#ifdef FTXPOST
+#include <ftxpost.h>
 #endif
 
-#ifdef HAVE_FT2
-  if (!i_ft2_init())
-    return 0;
+#ifdef FTXERR18
+#include <ftxerr18.h>
 #endif
 
-  return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
-}
-
-
-
-
-#ifdef HAVE_LIBT1
-
-
-
-/* 
-=item i_init_t1()
-
-Initializes the t1lib font rendering engine.
-
-=cut
-*/
-
-undef_int
-init_t1() {
-  mm_log((1,"init_t1()\n"));
-  if ((T1_InitLib(LOGFILE|IGNORE_CONFIGFILE|IGNORE_FONTDATABASE) == NULL)){
-    mm_log((1,"Initialization of t1lib failed\n"));
-    return(1);
-  }
-  T1_SetLogLevel(T1LOG_DEBUG);
-  i_t1_set_aa(1); /* Default Antialias value */
-  return(0);
-}
-
-
-/* 
-=item i_close_t1()
-
-Shuts the t1lib font rendering engine down.
-
-  This it seems that this function is never used.
-
-=cut
-*/
-
-void
-i_close_t1(void) {
-  T1_CloseLib();
-}
-
-
-/*
-=item i_t1_new(pfb, afm)
-
-Loads the fonts with the given filenames, returns its font id
-
- pfb -  path to pfb file for font
- afm -  path to afm file for font
-
-=cut
-*/
-
-int
-i_t1_new(char *pfb,char *afm) {
-  int font_id;
-  mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
-  font_id = T1_AddFont(pfb);
-  if (font_id<0) {
-    mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
-    return font_id;
-  }
-  
-  if (afm != NULL) {
-    mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
-    if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
-  }
-  return font_id;
-}
-
-/*
-=item i_t1_destroy(font_id)
-
-Frees resources for a t1 font with given font id.
-
-   font_id - number of the font to free
-
-=cut
-*/
-
-int
-i_t1_destroy(int font_id) {
-  mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
-  return T1_DeleteFont(font_id);
-}
-
-
-/*
-=item i_t1_set_aa(st)
-
-Sets the antialiasing level of the t1 library.
-
-   st - 0 =  NONE, 1 = LOW, 2 =  HIGH.
-
-=cut
-*/
-
-void
-i_t1_set_aa(int st) {
-  int i;
-  unsigned long cst[17];
-  switch(st) {
-  case 0:
-    T1_AASetBitsPerPixel( 8 );
-    T1_AASetLevel( T1_AA_NONE );
-    T1_AANSetGrayValues( 0, 255 );
-    mm_log((1,"setting T1 antialias to none\n"));
-    break;
-  case 1:
-    T1_AASetBitsPerPixel( 8 );
-    T1_AASetLevel( T1_AA_LOW );
-    T1_AASetGrayValues( 0,65,127,191,255 );
-    mm_log((1,"setting T1 antialias to low\n"));
-    break;
-  case 2:
-    T1_AASetBitsPerPixel(8);
-    T1_AASetLevel(T1_AA_HIGH);
-    for(i=0;i<17;i++) cst[i]=(i*255)/16;
-    T1_AAHSetGrayValues( cst );
-    mm_log((1,"setting T1 antialias to high\n"));
-  }
-}
-
-
-/* 
-=item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
-
-Interface to text rendering into a single channel in an image
-
-   im        pointer to image structure
-   xb        x coordinate of start of string
-   yb        y coordinate of start of string ( see align )
-   channel - destination channel
-   fontnum - t1 library font id
-   points  - number of points in fontheight
-   str     - string to render
-   len     - string length
-   align   - (0 - top of font glyph | 1 - baseline )
-
-=cut
-*/
-
-undef_int
-i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align) {
-  GLYPH *glyph;
-  int xsize,ysize,x,y;
-  i_color val;
-
-  unsigned int ch_mask_store;
-  
-  if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
-
-  glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL);
-  if (glyph == NULL)
-    return 0;
-
-  mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
-  mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
-  mm_log((1," advanceX: %d  advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
-  mm_log((1,"bpp: %d\n",glyph->bpp));
-  
-  xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
-  ysize=glyph->metrics.ascent-glyph->metrics.descent;
-  
-  mm_log((1,"width: %d height: %d\n",xsize,ysize));
-
-  ch_mask_store=im->ch_mask;
-  im->ch_mask=1<<channel;
-
-  if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
-  
-  for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
-    val.channel[channel]=glyph->bits[y*xsize+x];
-    i_ppix(im,x+xb,y+yb,&val);
-  }
-  
-  im->ch_mask=ch_mask_store;
-  return 1;
-}
-
-
-/*
-=item i_t1_bbox(handle, fontnum, points, str, len, cords)
-
-function to get a strings bounding box given the font id and sizes
-
-   handle  - pointer to font handle   
-   fontnum - t1 library font id
-   points  - number of points in fontheight
-   str     - string to measure
-   len     - string length
-   cords   - the bounding box (modified in place)
-
-=cut
-*/
-
-void
-i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6]) {
-  BBox bbox;
-  BBox gbbox;
-  
-  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 */ 
-  bbox = T1_GetStringBBox(fontnum,str,len,0,T1_KERNING);
-  gbbox = T1_GetFontBBox(fontnum);
-  
-  mm_log((1,"bbox: (%d,%d,%d,%d)\n",
-         (int)(bbox.llx*points/1000),
-         (int)(gbbox.lly*points/1000),
-         (int)(bbox.urx*points/1000),
-         (int)(gbbox.ury*points/1000),
-         (int)(bbox.lly*points/1000),
-         (int)(bbox.ury*points/1000) ));
-
-
-  cords[0]=((float)bbox.llx*points)/1000;
-  cords[2]=((float)bbox.urx*points)/1000;
-
-  cords[1]=((float)gbbox.lly*points)/1000;
-  cords[3]=((float)gbbox.ury*points)/1000;
-
-  cords[4]=((float)bbox.lly*points)/1000;
-  cords[5]=((float)bbox.ury*points)/1000;
-}
-
-
-/*
-=item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
-
-Interface to text rendering in a single color onto an image
-
-   im      - pointer to image structure
-   xb      - x coordinate of start of string
-   yb      - y coordinate of start of string ( see align )
-   cl      - color to draw the text in
-   fontnum - t1 library font id
-   points  - number of points in fontheight
-   str     - char pointer to string to render
-   len     - string length
-   align   - (0 - top of font glyph | 1 - baseline )
-
-=cut
-*/
-
-undef_int
-i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str,int len,int align) {
-  GLYPH *glyph;
-  int xsize,ysize,x,y,ch;
-  i_color val;
-  unsigned char c,i;
-
-  if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
-
-  glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL);
-  if (glyph == NULL)
-    return 0;
-
-  mm_log((1,"metrics:  ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
-  mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
-  mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
-  mm_log((1,"bpp: %d\n",glyph->bpp));
-  
-  xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
-  ysize=glyph->metrics.ascent-glyph->metrics.descent;
-  
-  mm_log((1,"width: %d height: %d\n",xsize,ysize));
-
-  if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
-  
-  for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
-    c=glyph->bits[y*xsize+x];
-    i=255-c;
-    i_gpix(im,x+xb,y+yb,&val);
-    for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
-    i_ppix(im,x+xb,y+yb,&val);
-  }
-  return 1;
-}
-
-
-#endif /* HAVE_LIBT1 */
-
-
-
-
-
-
-
-
-
-
-/* Truetype font support */
-
-#ifdef HAVE_LIBTT
+/* 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)
+
+typedef struct i_glyph_entry_ {
+  TT_Glyph glyph;
+  unsigned long ch;
+} i_tt_glyph_entry;
+
+#define TT_NOCHAR (~0UL)
+
+struct TT_Instancehandle_ {
+  TT_Instance instance;
+  TT_Instance_Metrics imetrics;
+  TT_Glyph_Metrics gmetrics[256];
+  i_tt_glyph_entry glyphs[256];
+  int smooth;
+  int order;
+  i_img_dim ptsize;
+};
+
+typedef struct TT_Instancehandle_ TT_Instancehandle;
+
+struct TT_Fonthandle_ {
+  TT_Face face;
+  TT_Face_Properties properties;
+  TT_Instancehandle instanceh[TT_CHC];
+  TT_CharMap char_map;
+#ifdef FTXPOST
+  int loaded_names;
+  TT_Error load_cond;
+#endif
+};
 
 /* Defines */
 
 #define USTRCT(x) ((x).z)
 #define TT_VALID( handle )  ( ( handle ).z != NULL )
 
+static void i_tt_push_error(TT_Error rc);
 
 /* Prototypes */
 
-static  int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
-static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
+static  int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth );
+static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth );
 static void i_tt_done_raster_map( TT_Raster_Map *bit );
 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
-static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
-static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j );
-static void i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int x_off, int y_off, int smooth );
-static void i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int cords[6], char* txt, int len, int smooth );
-static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
-static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
-static  int i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char* txt, int len, int smooth );
-static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6] );
+static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off );
+static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
+static void 
+i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, 
+                   TT_Raster_Map *bit, TT_Raster_Map *small_bit, 
+                   i_img_dim x_off, i_img_dim y_off, int smooth );
+static int
+i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, 
+                        TT_Raster_Map *small_bit, i_img_dim cords[6], 
+                        char const* txt, size_t len, int smooth, int utf8 );
+static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth );
+static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth );
+static  int
+i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6], 
+                double points, char const* txt, size_t len, int smooth, int utf8 );
+static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[6], int utf8 );
 
 
 /* static globals needed */
 
+static int TT_initialized = 0;
 static TT_Engine    engine;
 static int  LTT_dpi    = 72; /* FIXME: this ought to be a part of the call interface */
 static int  LTT_hinted = 1;  /* FIXME: this too */
@@ -426,14 +164,45 @@ Initializes the freetype font rendering engine
 */
 
 undef_int
-init_tt() {
+i_init_tt(void) {
   TT_Error  error;
+  TT_Byte palette[] = { 0, 64, 127, 191, 255 };
+
+  i_clear_error();
+
   mm_log((1,"init_tt()\n"));
   error = TT_Init_FreeType( &engine );
   if ( error ){
-    mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
+    mm_log((1,"Initialization of freetype failed, code = 0x%x\n",
+           (unsigned)error));
+    i_tt_push_error(error);
+    i_push_error(0, "Could not initialize freetype 1.x");
     return(1);
   }
+
+#ifdef FTXPOST
+  error = TT_Init_Post_Extension( engine );
+  if (error) {
+    mm_log((1, "Initialization of Post extension failed = 0x%x\n",
+           (unsigned)error));
+    
+    i_tt_push_error(error);
+    i_push_error(0, "Could not initialize FT 1.x POST extension");
+    return 1;
+  }
+#endif
+
+  error = TT_Set_Raster_Gray_Palette(engine, palette);
+  if (error) {
+    mm_log((1, "Initialization of gray levels failed = 0x%x\n",
+           (unsigned)error));
+    i_tt_push_error(error);
+    i_push_error(0, "Could not initialize FT 1.x POST extension");
+    return 1;
+  }
+
+  TT_initialized = 1;
+
   return(0);
 }
 
@@ -454,35 +223,56 @@ allocates room and returns its cache entry
 
 static
 int
-i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
+i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) {
   int i,idx;
   TT_Error error;
   
-  mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",handle,points,smooth));
+  mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
+          handle, i_DFc(points), smooth));
   
   if (smooth == -1) { /* Smooth doesn't matter for this search */
-    for(i=0;i<TT_CHC;i++) if (handle->instanceh[i].ptsize==points) {
-      mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
-      return i;
+    for(i=0;i<TT_CHC;i++) {
+      if (handle->instanceh[i].ptsize==points) {
+        mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
+        return i;
+      }
     }
     smooth=1; /* We will be adding a font - add it as smooth then */
   } else { /* Smooth doesn't matter for this search */
-    for(i=0;i<TT_CHC;i++) if (handle->instanceh[i].ptsize==points && handle->instanceh[i].smooth==smooth) {
-      mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
-      return i;
+    for(i=0;i<TT_CHC;i++) {
+      if (handle->instanceh[i].ptsize == points 
+          && handle->instanceh[i].smooth == smooth) {
+        mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
+        return i;
+      }
     }
   }
   
   /* Found the instance in the cache - return the cache index */
   
-  for(idx=0;idx<TT_CHC;idx++) if (!(handle->instanceh[idx].order)) break; /* find the lru item */
+  for(idx=0;idx<TT_CHC;idx++) {
+    if (!(handle->instanceh[idx].order)) break; /* find the lru item */
+  }
 
   mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
-  mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",USTRCT(handle->instanceh[idx].instance) ));
+  mm_log((1,"i_tt_get_instance: lru pointer %p\n",
+          USTRCT(handle->instanceh[idx].instance) ));
   
   if ( USTRCT(handle->instanceh[idx].instance) ) {
     mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
-    TT_Done_Instance( handle->instanceh[idx].instance ); /* Free instance if needed */
+
+    /* Free cached glyphs */
+    for(i=0;i<256;i++)
+      if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
+       TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
+
+    for(i=0;i<256;i++) {
+      handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
+      USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
+    }
+
+    /* Free instance if needed */
+    TT_Done_Instance( handle->instanceh[idx].instance );
   }
   
   /* create and initialize instance */
@@ -493,7 +283,8 @@ i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
          ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
   
   if ( error ) {
-    mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
+    mm_log((1, "Could not create and initialize instance: error %x.\n",
+           (unsigned)error ));
     return -1;
   }
   
@@ -507,10 +298,13 @@ i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
   handle->instanceh[idx].smooth=smooth;
   TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
 
-  /* Zero the memory for the glyph storage so they are not thought as cached if they haven't been cached
-     since this new font was loaded */
+  /* Zero the memory for the glyph storage so they are not thought as
+     cached if they haven't been cached since this new font was loaded */
 
-  for(i=0;i<256;i++) USTRCT(handle->instanceh[idx].glyphs[i])=NULL;
+  for(i=0;i<256;i++) {
+    handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
+    USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
+  }
   
   return idx;
 }
@@ -528,39 +322,59 @@ the font handle's cache
 */
 
 TT_Fonthandle*
-i_tt_new(char *fontname) {
+i_tt_new(const char *fontname) {
   TT_Error error;
   TT_Fonthandle *handle;
   unsigned short i,n;
   unsigned short platform,encoding;
+
+  if (!TT_initialized && i_init_tt()) {
+    i_push_error(0, "Could not initialize FT1 engine");
+    return NULL;
+  }
+
+  i_clear_error();
   
   mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
   
   /* allocate memory for the structure */
   
-  handle=mymalloc( sizeof(TT_Fonthandle) );
+  handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
 
   /* load the typeface */
   error = TT_Open_Face( engine, fontname, &handle->face );
   if ( error ) {
-    if ( error == TT_Err_Could_Not_Open_File ) { mm_log((1, "Could not find/open %s.\n", fontname )) }
-    else { mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, error )); }
+    if ( error == TT_Err_Could_Not_Open_File ) {
+      mm_log((1, "Could not find/open %s.\n", fontname ));
+    }
+    else {
+      mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, 
+              (unsigned)error )); 
+    }
+    i_tt_push_error(error);
     return NULL;
   }
   
   TT_Get_Face_Properties( handle->face, &(handle->properties) );
+
   /* First, look for a Unicode charmap */
-  
   n = handle->properties.num_CharMaps;
   USTRCT( handle->char_map )=NULL; /* Invalidate character map */
   
   for ( i = 0; i < n; i++ ) {
     TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
-    if ( (platform == 3 && encoding == 1 ) || (platform == 0 && encoding == 0 ) ) {
+    if ( (platform == 3 && encoding == 1 ) 
+         || (platform == 0 && encoding == 0 ) ) {
+      mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", 
+              platform, encoding));
       TT_Get_CharMap( handle->face, i, &(handle->char_map) );
       break;
     }
   }
+  if (!USTRCT(handle->char_map) && n != 0) {
+    /* just use the first one */
+    TT_Get_CharMap( handle->face, 0, &(handle->char_map));
+  }
 
   /* Zero the pointsizes - and ordering */
   
@@ -571,7 +385,11 @@ i_tt_new(char *fontname) {
     handle->instanceh[i].smooth=-1;
   }
 
-  mm_log((1,"i_tt_new <- 0x%X\n",handle));
+#ifdef FTXPOST
+  handle->loaded_names = 0;
+#endif
+
+  mm_log((1,"i_tt_new <- %p\n",handle));
   return handle;
 }
 
@@ -596,9 +414,10 @@ Allocates internal memory for the bitmap as needed by the parameters (internal)
 
 static
 void
-i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
+i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ) {
 
-  mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
+  mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF
+         ", smooth %d)\n", bit, i_DFc(width), i_DFc(height), smooth));
   
   bit->rows  = height;
   bit->width = ( width + 3 ) & -4;
@@ -611,11 +430,17 @@ i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
     bit->cols  = ( bit->width + 7 ) / 8;    /* convert to # of bytes     */
     bit->size  = bit->rows * bit->cols;     /* number of bytes in buffer */
   }
+
+  /* rows can be 0 for some glyphs, for example ' ' */
+  if (bit->rows && bit->size / bit->rows != bit->cols) {
+    i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
+            bit->width, bit->rows);
+  }
   
-  mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %d)\n", bit->width, bit->cols, bit->rows, bit->size ));
+  mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %ld)\n", bit->width, bit->cols, bit->rows, bit->size ));
 
-  bit->bitmap = (void *) mymalloc( bit->size );
-  if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
+  bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
+  if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
 }
 
 
@@ -670,10 +495,10 @@ function that blits one raster map into another (internal)
 
 static
 void
-i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
-  int  x,  y;
-  int  x1, x2, y1, y2;
-  char *s, *d;
+i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ) {
+  i_img_dim  x,  y;
+  i_img_dim  x1, x2, y1, y2;
+  unsigned char *s, *d;
   
   x1 = x_off < 0 ? -x_off : 0;
   y1 = y_off < 0 ? -y_off : 0;
@@ -689,8 +514,8 @@ i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
   /* do the real work now */
 
   for ( y = y1; y < y2; ++y ) {
-    s = ( (char*)src->bitmap ) + y * src->cols + x1;
-    d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
+    s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
+    d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
     
     for ( x = x1; x < x2; ++x ) {
       if (*s > *d)
@@ -733,17 +558,27 @@ Function to see if a glyph exists and if so cache it (internal)
 
 static
 int
-i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j) { /* FIXME: Check if unsigned char is enough */
+i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
   unsigned short load_flags, code;
   TT_Error error;
 
-  mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",handle,inst,j,j));
-  mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));
+  mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n",
+          handle,inst,j, (int)((j >= ' ' && j <= '~') ? j : '.')));
+  
+  /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
 
-  if ( TT_VALID(handle->instanceh[inst].glyphs[j]) ) {
-    mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
+  if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
+       && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
+    mm_log((1,"i_tt_get_glyph: %lu in cache\n",j));
     return 1;
   }
+
+  if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
+    /* clean up the entry */
+    TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
+    USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
+    handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
+  }
   
   /* Ok - it wasn't cached - try to get it in */
   load_flags = TTLOAD_SCALE_GLYPH;
@@ -754,19 +589,86 @@ i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j) { /* FIXME: Ch
     if ( code >= handle->properties.num_Glyphs ) code = 0;
   } else code = TT_Char_Index( handle->char_map, j );
   
-  if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[j])) ) 
-    mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
-  if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[j], code, load_flags)) )
-    mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
-  
+  if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
+    mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
+    i_push_error(error, "TT_New_Glyph()");
+    return 0;
+  }
+  if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
+    mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
+    /* Don't leak */
+    TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
+    USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
+    i_push_error(error, "TT_Load_Glyph()");
+    return 0;
+  }
+
   /* At this point the glyph should be allocated and loaded */
+  handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
+
   /* Next get the glyph metrics */
-  
-  error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[j], &handle->instanceh[inst].gmetrics[j] );
-  mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
+  error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
+                                &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
+  if (error) {
+    mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error ));
+    TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
+    USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
+    handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
+    i_push_error(error, "TT_Get_Glyph_Metrics()");
+    return 0;
+  }
+
   return 1;
 }
 
+/*
+=item i_tt_has_chars(handle, 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).
+
+Returns the number of characters that were checked.
+
+=cut
+*/
+
+size_t
+i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
+               char *out) {
+  size_t count = 0;
+  mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n", 
+          handle, text, (long)len, utf8));
+
+  while (len) {
+    unsigned long c;
+    int index;
+    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 (TT_VALID(handle->char_map)) {
+      index = TT_Char_Index(handle->char_map, c);
+    }
+    else {
+      index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
+      if (index >= handle->properties.num_Glyphs)
+        index = 0;
+    }
+    *out++ = index != 0;
+    ++count;
+  }
+
+  return count;
+}
 
 /* 
 =item i_tt_destroy(handle)
@@ -782,6 +684,7 @@ pixmaps and glyphs
 void
 i_tt_destroy( TT_Fonthandle *handle) {
   TT_Close_Face( handle->face );
+  myfree( handle );
   
   /* FIXME: Should these be freed automatically by the library? 
 
@@ -824,10 +727,11 @@ Renders a single glyph into the bit rastermap (internal)
 
 static
 void
-i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int x_off, int y_off, int smooth ) {
+i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, i_img_dim x_off, i_img_dim y_off, int smooth ) {
   
-  mm_log((1,"i_tt_render_glyph(glyph 0x0%X, gmetrics 0x0%X, bit 0x%X, small_bit 0x%X, x_off %d, y_off %d, smooth %d)\n",
-         USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
+  mm_log((1,"i_tt_render_glyph(glyph %p, gmetrics %p, bit %p, small_bit %p, x_off %" i_DF ", y_off %" i_DF ", smooth %d)\n",
+         USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off),
+         i_DFc(y_off), smooth));
   
   if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
   else {
@@ -862,28 +766,44 @@ calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
 */
 
 static
-void
-i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int cords[6], char* txt, int len, int smooth ) {
-  unsigned char j;
-  int i;
+int
+i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
+                        TT_Raster_Map *small_bit, i_img_dim cords[6], 
+                        char const* txt, size_t len, int smooth, int utf8 ) {
+  unsigned long j;
   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)\n",
-         handle, inst, bit, small_bit, len, txt, len, smooth));
+  mm_log((1,"i_tt_render_all_glyphs( handle %p, inst %d, bit %p, small_bit %p, txt '%.*s', len %ld, smooth %d, utf8 %d)\n",
+         handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
   
   /* 
      y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
   */
 
   x=-cords[0]; /* FIXME: If you font is antialiased this should be expanded by one to allow for aa expansion and the allocation too - do before passing here */
-  y=-cords[1];
-  
-  for ( i = 0; i < len; i++ ) {
-    j = txt[i];
-    if ( !i_tt_get_glyph(handle,inst,j) ) continue;
-    i_tt_render_glyph( handle->instanceh[inst].glyphs[j], &handle->instanceh[inst].gmetrics[j], bit, small_bit, x, y, smooth );
-    x += handle->instanceh[inst].gmetrics[j].advance / 64;
+  y=-cords[4];
+  
+  while (len) {
+    if (utf8) {
+      j = i_utf8_advance(&txt, &len);
+      if (j == ~0UL) {
+        i_push_error(0, "invalid UTF8 character");
+        return 0;
+      }
+    }
+    else {
+      j = (unsigned char)*txt++;
+      --len;
+    }
+    if ( !i_tt_get_glyph(handle,inst,j) ) 
+      continue;
+    i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
+                       &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, 
+                       small_bit, x, y, smooth );
+    x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
   }
+
+  return 1;
 }
 
 
@@ -907,32 +827,37 @@ Function to dump a raster onto an image in color used by i_tt_text() (internal).
 
 static
 void
-i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
-  char *bmap;
-  i_color val;
-  int c, i, ch, x, y;
-  mm_log((1,"i_tt_dump_raster_map2(im 0x%x, bit 0x%X, xb %d, yb %d, cl 0x%X)\n",im,bit,xb,yb,cl));
+i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth ) {
+  unsigned char *bmap;
+  i_img_dim x, y;
+  mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
+         im, bit, i_DFc(xb), i_DFc(yb), cl));
   
-  bmap = (char *)bit->bitmap;
+  bmap = bit->bitmap;
 
   if ( smooth ) {
 
-    for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
-      c=(255*bmap[y*(bit->cols)+x])/4;
-      i=255-c;
-      i_gpix(im,x+xb,y+yb,&val);
-      for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
-      i_ppix(im,x+xb,y+yb,&val);
+    i_render r;
+    i_render_init(&r, im, bit->cols);
+    for(y=0;y<bit->rows;y++) {
+      i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
     }
-
+    i_render_done(&r);
   } else {
-
-    for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
-      c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
-      i=255-c;
-      i_gpix(im,x+xb,y+yb,&val);
-      for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
-      i_ppix(im,x+xb,y+yb,&val);
+    for(y=0;y<bit->rows;y++) {
+      unsigned mask = 0x80;
+      unsigned char *p = bmap + y * bit->cols;
+
+      for(x = 0; x < bit->width; x++) {
+       if (*p & mask) {
+         i_ppix(im, x+xb, y+yb, cl);
+       }
+       mask >>= 1;
+       if (!mask) {
+         mask = 0x80;
+         ++p;
+       }
+      }
     }
 
   }
@@ -955,30 +880,43 @@ Function to dump a raster onto a single channel image in color (internal)
 
 static
 void
-i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, int xb, int yb, int channel, int smooth ) {
-  char *bmap;
+i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, i_img_dim xb, i_img_dim yb, int channel, int smooth ) {
+  unsigned char *bmap;
   i_color val;
-  int c,x,y;
+  int c;
+  i_img_dim x,y;
+  int old_mask = im->ch_mask;
+  im->ch_mask = 1 << channel;
 
-  mm_log((1,"i_tt_dump_raster_channel(im 0x%x, bit 0x%X, xb %d, yb %d, channel %d)\n",im,bit,xb,yb,channel));
+  mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
+         im, bit, i_DFc(xb), i_DFc(yb), channel));
   
-  bmap = (char *)bit->bitmap;
+  bmap = bit->bitmap;
   
   if ( smooth ) {
     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
-      c=(255*bmap[y*(bit->cols)+x])/4;
-      i_gpix(im,x+xb,y+yb,&val);
-      val.channel[channel]=c;
+      c = bmap[y*(bit->cols)+x];
+      val.channel[channel] = c;
       i_ppix(im,x+xb,y+yb,&val);
     }
   } else {
-    for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
-      c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
-      i_gpix(im,x+xb,y+yb,&val);
-      val.channel[channel]=c;
-      i_ppix(im,x+xb,y+yb,&val);
+    for(y=0;y<bit->rows;y++) {
+      unsigned mask = 0x80;
+      unsigned char *p = bmap + y * bit->cols;
+
+      for(x=0;x<bit->width;x++) {
+       val.channel[channel] = (*p & mask) ? 255 : 0;
+       i_ppix(im,x+xb,y+yb,&val);
+       
+       mask >>= 1;
+       if (!mask) {
+         ++p;
+         mask = 0x80;
+       }
+      }
     }
   }
+  im->ch_mask = old_mask;
 }
 
 
@@ -1000,9 +938,9 @@ interface for generating single channel raster of text (internal)
 
 static
 int
-i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char* txt, int len, int smooth ) {
+i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6], double points, char const* txt, size_t len, int smooth, int utf8 ) {
   int inst;
-  int width, height;
+  i_img_dim width, height;
   TT_Raster_Map small_bit;
   
   /* find or install an instance */
@@ -1012,21 +950,27 @@ i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float p
   }
   
   /* calculate bounding box */
-  i_tt_bbox_inst( handle, inst, txt, len, cords );
+  if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
+    return 0;
+    
   
   width  = cords[2]-cords[0];
-  height = cords[3]-cords[1];
+  height = cords[5]-cords[4];
   
-  mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); 
+  mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
+         i_DFc(width), i_DFc(height) )); 
   
   i_tt_init_raster_map ( bit, width, height, smooth );
   i_tt_clear_raster_map( bit );
   if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
   
-  i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, smooth );
+  if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 
+                               smooth, utf8 )) {
+    if ( smooth ) 
+      i_tt_done_raster_map( &small_bit );
+    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;
 }
@@ -1039,7 +983,7 @@ i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float p
 
 
 /*
-=item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth)
+=item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
 
 Interface to text rendering into a single channel in an image
 
@@ -1056,18 +1000,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* txt, int len, int smooth ) {
+i_tt_cp( TT_Fonthandle *handle, i_img *im, i_img_dim xb, i_img_dim yb, int channel, double points, char const* txt, size_t len, int smooth, int utf8, int align ) {
 
-  int cords[6];
-  int ascent, st_offset;
+  i_img_dim cords[BOUNDING_BOX_COUNT];
+  i_img_dim ascent, st_offset, y;
   TT_Raster_Map bit;
   
-  if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth ) ) return 0;
+  i_clear_error();
+  if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
   
-  ascent=cords[3];
-  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;
@@ -1075,7 +1021,7 @@ i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float po
 
 
 /* 
-=item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth) 
+=item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8
 
 Interface to text rendering in a single color onto an image
 
@@ -1092,17 +1038,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* txt, int len, int smooth) {
-  int cords[6];
-  int ascent, st_offset;
+i_tt_text( TT_Fonthandle *handle, i_img *im, i_img_dim xb, i_img_dim yb, const i_color *cl, double points, char const* txt, size_t len, int smooth, int utf8, int align) {
+  i_img_dim cords[BOUNDING_BOX_COUNT];
+  i_img_dim ascent, st_offset, y;
   TT_Raster_Map bit;
+
+  i_clear_error();
   
-  if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth ) ) return 0;
+  if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
   
-  ascent=cords[3];
-  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;
@@ -1110,7 +1059,7 @@ i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float
 
 
 /*
-=item i_tt_bbox_inst(handle, inst, txt, len, cords) 
+=item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8
 
 Function to get texts bounding boxes given the instance of the font (internal)
 
@@ -1125,13 +1074,23 @@ 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 i, upm, ascent, descent, gascent, gdescent, width, casc, cdesc, first, start;
-  unsigned int j;
+i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
+  int upm, casc, cdesc, first;
+  
+  int start    = 0;
+  i_img_dim width    = 0;
+  int gdescent = 0;
+  int gascent  = 0;
+  int descent  = 0;
+  int ascent   = 0;
+  int rightb   = 0;
+
+  unsigned long j;
   unsigned char *ustr;
   ustr=(unsigned char*)txt;
-  
-  mm_log((1,"i_tt_box_inst(handle 0x%X,inst %d,txt '%.*s', len %d)\n",handle,inst,len,txt,len));
+
+  mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
+         handle, inst, (int)len, txt, (long)len, utf8));
 
   upm     = handle->properties.header->Units_Per_EM;
   gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
@@ -1143,15 +1102,26 @@ i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int c
   mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
 
   first=1;
-  for ( i = 0; i < len; ++i ) {
-    j = ustr[i];
+  while (len) {
+    if (utf8) {
+      j = i_utf8_advance(&txt, &len);
+      if (j == ~0UL) {
+        i_push_error(0, "invalid UTF8 character");
+        return 0;
+      }
+    }
+    else {
+      j = (unsigned char)*txt++;
+      --len;
+    }
     if ( i_tt_get_glyph(handle,inst,j) ) {
-      TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + j;
+      TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
       width += gm->advance   / 64;
       casc   = (gm->bbox.yMax+63) / 64;
       cdesc  = (gm->bbox.yMin-63) / 64;
 
-      mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", j, casc, cdesc));
+      mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", 
+              (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
 
       if (first) {
        start    = gm->bbox.xMin / 64;
@@ -1159,17 +1129,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 );
@@ -1177,18 +1145,23 @@ 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;
 }
 
 
 /*
-=item i_tt_bbox(handle, points, txt, len, cords)
+=item i_tt_bbox(handle, points, txt, len, cords, utf8)
 
 Interface to get a strings bounding box
 
@@ -1202,20 +1175,190 @@ Interface to get a strings bounding box
 */
 
 undef_int
-i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6]) {
+i_tt_bbox( TT_Fonthandle *handle, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
   int inst;
-  
-  mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d)\n",handle,points,len,txt,len));
+
+  i_clear_error();
+  mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
+         handle, points, (int)len, txt, (long)len, utf8));
 
   if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
+    i_push_errorf(0, "i_tt_get_instance(%g)", points);
     mm_log((1,"i_tt_text: get instance failed\n"));
     return 0;
   }
 
-  return i_tt_bbox_inst(handle, inst, txt, len, cords);
+  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
+ */
+size_t
+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);
+    }
+  }
+  fflush(stdout);
+}
+
+size_t
+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(handle->load_cond, "error loading names (%#x)",
+                 (unsigned)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 */