]> git.imager.perl.org - imager.git/blobdiff - font.c
0.73 release
[imager.git] / font.c
diff --git a/font.c b/font.c
index fffc0d47c5a6e6d1e9e6e48929978cdc0957d1fd..706f06d6ee110d0477a6b05a714c47cdda3e0d35 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,6 +8,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#ifdef HAVE_LIBT1
+#include <t1lib.h>
+#endif
+
+
 /*
 =head1 NAME
 
@@ -57,18 +63,10 @@ i_init_fonts(int t1log) {
   mm_log((1,"Initializing fonts\n"));
 
 #ifdef HAVE_LIBT1
-  i_init_t1(t1log);
-#endif
-  
-#ifdef HAVE_LIBTT
-  i_init_tt();
-#endif
-
-#ifdef HAVE_FT2
-  if (!i_ft2_init())
+  if (i_init_t1(t1log))
     return 0;
 #endif
-
+  
   return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
 }
 
@@ -82,6 +80,9 @@ 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)
 
@@ -94,15 +95,31 @@ undef_int
 i_init_t1(int t1log) {
   int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
   mm_log((1,"init_t1()\n"));
+
+  i_clear_error();
+
+  if (t1_active_fonts) {
+    mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
+    i_push_error(0, "Cannot re-initialize T1 - active fonts");
+    return 1;
+  }
+
+  if (t1_initialized) {
+    T1_CloseLib();
+  }
   
   if (t1log)
     init_flags |= LOGFILE;
   if ((T1_InitLib(init_flags) == NULL)){
     mm_log((1,"Initialization of t1lib failed\n"));
+    i_push_error(0, "T1_InitLib failed");
     return(1);
   }
   T1_SetLogLevel(T1LOG_DEBUG);
   i_t1_set_aa(1); /* Default Antialias value */
+
+  ++t1_initialized;
+
   return(0);
 }
 
@@ -120,6 +137,7 @@ Shuts the t1lib font rendering engine down.
 void
 i_close_t1(void) {
   T1_CloseLib();
+  t1_initialized = 0;
 }
 
 
@@ -138,6 +156,11 @@ int
 i_t1_new(char *pfb,char *afm) {
   int font_id;
 
+  i_clear_error();
+
+  if (!t1_initialized && i_init_t1(0))
+    return -1;
+
   mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
   font_id = T1_AddFont(pfb);
   if (font_id<0) {
@@ -150,6 +173,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;
 }
 
@@ -166,6 +191,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);
 }
 
@@ -272,6 +300,17 @@ i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,i
   return 1;
 }
 
+static void
+t1_fix_bbox(BBox *bbox, const char *str, int len, int advance, 
+           int space_position) {
+  /* never called with len == 0 */
+  if (str[0] == space_position && bbox->llx > 0)
+    bbox->llx = 0;
+  if (str[len-1] == space_position && bbox->urx < advance)
+    bbox->urx = advance;
+  if (bbox->lly > bbox->ury)
+    bbox->lly = bbox->ury = 0; 
+}
 
 /*
 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
@@ -289,25 +328,38 @@ function to get a strings bounding box given the font id and sizes
 */
 
 int
-i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char const *flags) {
+i_t1_bbox(int fontnum,float points,const 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;
+  int space_position = T1_GetEncodingIndex(fontnum, "space");
   
   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 */ 
-  if (utf8) {
-    int worklen;
-    char *work = t1_from_utf8(str, len, &worklen);
-    bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
-    myfree(work);
+
+  if (len == 0) {
+    /* len == 0 has special meaning to T1lib, but it means there's
+       nothing to draw, so return that */
+    bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
+    advance = 0;
   }
   else {
-    bbox = T1_GetStringBBox(fontnum,str,len,0,mod_flags);
+    if (utf8) {
+      int worklen;
+      char *work = t1_from_utf8(str, len, &worklen);
+      advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
+      bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
+      t1_fix_bbox(&bbox, work, worklen, advance, space_position);
+      myfree(work);
+    }
+    else {
+      advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
+      bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
+      t1_fix_bbox(&bbox, str, len, advance, space_position);
+    }
   }
   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),
@@ -354,12 +406,11 @@ Interface to text rendering in a single color onto an image
 */
 
 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, int utf8, char const *flags) {
+i_t1_text(i_img *im,int xb,int yb,const i_color *cl,int fontnum,float points,const char* str,int len,int align, int utf8, char const *flags) {
   GLYPH *glyph;
-  int xsize,ysize,x,y,ch;
-  i_color val;
-  unsigned char c,i;
+  int xsize,ysize,y;
   int mod_flags = t1_get_flags(flags);
+  i_render r;
 
   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
 
@@ -370,7 +421,8 @@ i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str
     myfree(work);
   }
   else {
-    glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
+    /* T1_AASetString() accepts a char * not a const char */
+    glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
   }
   if (glyph == NULL)
     return 0;
@@ -386,14 +438,13 @@ i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str
   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);
+
+  i_render_init(&r, im, xsize);
+  for(y=0;y<ysize;y++) {
+    i_render_color(&r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl);
   }
+  i_render_done(&r);
+    
   return 1;
 }
 
@@ -435,7 +486,8 @@ Sets *outlen to the number of bytes used in the output string.
 
 static char *
 t1_from_utf8(char const *in, int len, int *outlen) {
-  char *out = mymalloc(len+1);
+  /* at this point len is from a perl SV, so can't approach MAXINT */
+  char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
   char *p = out;
   unsigned long c;
 
@@ -709,12 +761,13 @@ t1_push_error(void) {
 /* Truetype font support */
 #ifdef HAVE_LIBTT
 
-/* This is enabled by default when configuring Freetype 1.x
+/* 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
@@ -723,6 +776,10 @@ t1_push_error(void) {
 #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
@@ -767,6 +824,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 */
 
@@ -784,7 +842,7 @@ static int
 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 );
-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_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const 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], 
@@ -794,6 +852,7 @@ static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *tx
 
 /* 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 */
@@ -813,12 +872,18 @@ Initializes the freetype font rendering engine
 */
 
 undef_int
-i_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));
+    i_tt_push_error(error);
+    i_push_error(0, "Could not initialize freetype 1.x");
     return(1);
   }
 
@@ -826,10 +891,23 @@ i_init_tt() {
   error = TT_Init_Post_Extension( engine );
   if (error) {
     mm_log((1, "Initialization of Post extension failed = 0x%x\n", 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", 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);
 }
 
@@ -948,17 +1026,24 @@ 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 );
@@ -970,6 +1055,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;
   }
   
@@ -1047,11 +1133,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 ));
 
-  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 );
 }
 
 
@@ -1109,7 +1201,7 @@ 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;
+  unsigned char *s, *d;
   
   x1 = x_off < 0 ? -x_off : 0;
   y1 = y_off < 0 ? -y_off : 0;
@@ -1125,8 +1217,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)
@@ -1437,32 +1529,47 @@ 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;
+i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) {
+  unsigned char *bmap;
+  int 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));
   
-  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++) {
+#if 0
+      for(x=0;x<bit->width;x++) {
+       c = (unsigned char)bmap[y*(bit->cols)+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);
+      }
+#else
+      i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
+#endif
     }
-
+    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;
+       }
+      }
     }
 
   }
@@ -1486,29 +1593,40 @@ 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;
+  unsigned char *bmap;
   i_color val;
   int c,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));
   
-  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;
 }
 
 
@@ -1629,7 +1747,7 @@ 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 align) {
+i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const 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;
@@ -1765,7 +1883,7 @@ Interface to get a strings bounding box
 */
 
 undef_int
-i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], int utf8) {
+i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,int len,int cords[6], int utf8) {
   int inst;
 
   i_clear_error();
@@ -1879,6 +1997,7 @@ void i_tt_dump_names(TT_Fonthandle *handle) {
       printf("'%s'\n", name);
     }
   }
+  fflush(stdout);
 }
 
 int
@@ -1899,7 +2018,7 @@ i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
   }
 
   if (handle->load_cond) {
-    i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
+    i_push_errorf(handle->load_cond, "error loading names (%d)", handle->load_cond);
     return 0;
   }
   
@@ -1929,6 +2048,24 @@ i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
 #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 */