Back to index

tetex-bin  3.0
gdft.c
Go to the documentation of this file.
00001 
00002 /********************************************/
00003 /* gd interface to freetype library         */
00004 /*                                          */
00005 /* John Ellson   ellson@graphviz.org        */
00006 /********************************************/
00007 
00008 #ifdef HAVE_CONFIG_H
00009 #include "config.h"
00010 #endif
00011 
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <string.h>
00015 #include <math.h>
00016 #include "gd.h"
00017 #include "gdhelpers.h"
00018 #include "entities.h"
00019 
00020 /* 2.0.10: WIN32, not MSWIN32 */
00021 #ifndef WIN32
00022 #include <unistd.h>
00023 #else
00024 #include <io.h>
00025 #define R_OK 04                    /* Needed in Windows */
00026 #endif
00027 
00028 /* number of antialised colors for indexed bitmaps */
00029 #define NUMCOLORS 8
00030 
00031 static int fontConfigFlag = 0;
00032 
00033 static char *font_path(char **fontpath, char *name_list);
00034 /* translate a fontconfig fontpattern into a fontpath. 
00035        return NULL if OK, else return error string */
00036 static char *font_pattern(char **fontpath, char *fontpattern);
00037 
00038 /* 2.0.30: move these up here so we can build correctly without freetype
00039        but with fontconfig */
00040 
00041 /*
00042  * The character (space) used to separate alternate fonts in the
00043  * fontlist parameter to gdImageStringFT. 2.0.18: space was a
00044  * poor choice for this.
00045  */
00046 #define LISTSEPARATOR ";"
00047 
00048 /*
00049  * DEFAULT_FONTPATH and PATHSEPARATOR are host type dependent and
00050  * are normally set by configure in config.h.  These are just
00051  * some last resort values that might match some Un*x system
00052  * if building this version of gd separate from graphviz.
00053  */
00054 #ifndef DEFAULT_FONTPATH
00055 #if defined(__APPLE__) || (defined(__MWERKS__) && defined(macintosh))
00056 #define DEFAULT_FONTPATH "/usr/share/fonts/truetype:/System/Library/Fonts:/Library/Fonts"
00057 #else
00058 #define DEFAULT_FONTPATH "/usr/share/fonts/truetype"
00059 #endif
00060 #endif
00061 #ifndef PATHSEPARATOR
00062 #define PATHSEPARATOR ":"
00063 #endif
00064 
00065 #ifndef TRUE
00066 #define FALSE 0
00067 #define TRUE !FALSE
00068 #endif
00069 
00070 #define MAX(a,b) ((a)>(b)?(a):(b))
00071 #define MIN(a,b) ((a)<(b)?(a):(b))
00072 
00073 
00074 BGD_DECLARE(char *) gdImageStringTTF (gdImage * im, int *brect, int fg, char *fontlist,
00075                 double ptsize, double angle, int x, int y, char *string)
00076 {
00077   /* 2.0.6: valid return */
00078   return gdImageStringFT (im, brect, fg, fontlist, ptsize,
00079                        angle, x, y, string);
00080 }
00081 
00082 #ifndef HAVE_LIBFREETYPE
00083 BGD_DECLARE(char *) gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist,
00084                  double ptsize, double angle, int x, int y, char *string,
00085                  gdFTStringExtraPtr strex)
00086 {
00087   return "libgd was not built with FreeType font support\n";
00088 }
00089 
00090 BGD_DECLARE(char *) gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
00091                double ptsize, double angle, int x, int y, char *string)
00092 {
00093   return "libgd was not built with FreeType font support\n";
00094 }
00095 #else
00096 
00097 #ifndef HAVE_LIBFONTCONFIG
00098 static char * font_pattern(char **fontpath, char *fontpattern)
00099 {
00100   return "libgd was not built with FontConfig support\n";
00101 }
00102 #endif /* HAVE_LIBFONTCONFIG */
00103 
00104 #include "gdcache.h"
00105 /* 2.0.16 Christophe Thomas: starting with FreeType 2.1.6, this is
00106   mandatory, and it has been supported for a long while. */
00107 #ifdef HAVE_FT2BUILD_H
00108 #include <ft2build.h>
00109 #include FT_FREETYPE_H
00110 #include FT_GLYPH_H
00111 #include FT_SIZES_H
00112 #else
00113 #include <freetype/freetype.h>
00114 #include <freetype/ftglyph.h>
00115 #include <freetype/ftsizes.h>
00116 #endif
00117 
00118 /* number of fonts cached before least recently used is replaced */
00119 #define FONTCACHESIZE 6
00120 
00121 /* number of antialias color lookups cached */
00122 #define TWEENCOLORCACHESIZE 32
00123 
00124 /*
00125  * Line separation as a factor of font height.  
00126  *      No space between if LINESPACE = 1.00 
00127  *      Line separation will be rounded up to next pixel row.
00128  */
00129 #define LINESPACE 1.05
00130 
00131 typedef struct
00132 {
00133   char *fontlist;           /* key */
00134   int flags;                /* key */
00135   char *fontpath;
00136   FT_Library *library;
00137   FT_Face face;
00138 }
00139 font_t;
00140 
00141 typedef struct
00142 {
00143   char *fontlist;           /* key */
00144   int flags;                /* key */
00145   FT_Library *library;
00146 }
00147 fontkey_t;
00148 
00149 typedef struct
00150 {
00151   int pixel;                /* key */
00152   int bgcolor;                     /* key */
00153   int fgcolor;                     /* key *//* -ve means no antialias */
00154   gdImagePtr im;            /* key */
00155   int tweencolor;
00156 }
00157 tweencolor_t;
00158 
00159 typedef struct
00160 {
00161   int pixel;                /* key */
00162   int bgcolor;                     /* key */
00163   int fgcolor;                     /* key *//* -ve means no antialias */
00164   gdImagePtr im;            /* key */
00165 }
00166 tweencolorkey_t;
00167 
00168 /********************************************************************
00169  * gdTcl_UtfToUniChar is borrowed from Tcl ...
00170  */
00171 /*
00172  * tclUtf.c --
00173  *
00174  *      Routines for manipulating UTF-8 strings.
00175  *
00176  * Copyright (c) 1997-1998 Sun Microsystems, Inc.
00177  *
00178  * See the file "license.terms" for information on usage and redistribution
00179  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
00180  *
00181  * SCCS: @(#) tclUtf.c 1.25 98/01/28 18:02:43
00182  */
00183 
00184 /*
00185  *---------------------------------------------------------------------------
00186  *
00187  * gdTcl_UtfToUniChar --
00188  *
00189  *      Extract the Tcl_UniChar represented by the UTF-8 string.  Bad
00190  *      UTF-8 sequences are converted to valid Tcl_UniChars and processing
00191  *      continues.  Equivalent to Plan 9 chartorune().
00192  *
00193  *      The caller must ensure that the source buffer is long enough that
00194  *      this routine does not run off the end and dereference non-existent
00195  *      memory looking for trail bytes.  If the source buffer is known to
00196  *      be '\0' terminated, this cannot happen.  Otherwise, the caller
00197  *      should call Tcl_UtfCharComplete() before calling this routine to
00198  *      ensure that enough bytes remain in the string.
00199  *
00200  * Results:
00201  *      *chPtr is filled with the Tcl_UniChar, and the return value is the
00202  *      number of bytes from the UTF-8 string that were consumed.
00203  *
00204  * Side effects:
00205  *      None.
00206  *
00207  *---------------------------------------------------------------------------
00208  */
00209 
00210 #ifdef JISX0208
00211 #include "jisx0208.h"
00212 #endif
00213 
00214 static int comp_entities(const void *e1, const void *e2) {
00215   struct entities_s *en1 = (struct entities_s *) e1;
00216   struct entities_s *en2 = (struct entities_s *) e2;
00217   return strcmp(en1->name, en2->name);
00218 }
00219 
00220 #define Tcl_UniChar int
00221 #define TCL_UTF_MAX 3
00222 static int
00223 gdTcl_UtfToUniChar (char *str, Tcl_UniChar * chPtr)
00224 /* str is the UTF8 next character pointer */
00225 /* chPtr is the int for the result */
00226 {
00227   int byte;
00228   char entity_name_buf[ENTITY_NAME_LENGTH_MAX+1];
00229   char *p;
00230   struct entities_s key, *res;
00231 
00232   /* HTML4.0 entities in decimal form, e.g. &#197; */
00233   /*           or in hexadecimal form, e.g. &#x6C34; */
00234   byte = *((unsigned char *) str);
00235   if (byte == '&')
00236     {
00237       int i, n = 0;
00238 
00239       byte = *((unsigned char *) (str + 1));
00240       if (byte == '#')
00241        {
00242           byte = *((unsigned char *) (str + 2));
00243           if (byte == 'x' || byte == 'X')
00244             {
00245               for (i = 3; i < 8; i++)
00246                 {
00247                   byte = *((unsigned char *) (str + i));
00248                   if (byte >= 'A' && byte <= 'F')
00249                     byte = byte - 'A' + 10;
00250                   else if (byte >= 'a' && byte <= 'f')
00251                     byte = byte - 'a' + 10;
00252                   else if (byte >= '0' && byte <= '9')
00253                     byte = byte - '0';
00254                   else
00255                     break;
00256                   n = (n * 16) + byte;
00257                 }
00258             }
00259           else
00260             {
00261              for (i = 2; i < 8; i++)
00262                {
00263                  byte = *((unsigned char *) (str + i));
00264                  if (byte >= '0' && byte <= '9')
00265                    n = (n * 10) + (byte - '0');
00266                  else
00267                   break;
00268               }
00269            }
00270          if (byte == ';')
00271            {
00272              *chPtr = (Tcl_UniChar) n;
00273              return ++i;
00274            }
00275        }
00276       else
00277         {
00278           key.name = p = entity_name_buf;
00279           for (i = 1; i < 1 + ENTITY_NAME_LENGTH_MAX; i++)
00280             {
00281               byte = *((unsigned char *) (str + i));
00282               if (byte == '\0')
00283                 break;
00284               if (byte == ';')
00285                 {
00286                   *p++ = '\0';
00287                   res = bsearch(&key, entities, NR_OF_ENTITIES,
00288                        sizeof(entities[0]), *comp_entities);
00289                   if (res)
00290                     {
00291                       *chPtr = (Tcl_UniChar) res->value;
00292                       return ++i;
00293                     }
00294                   break;
00295                }
00296               *p++ = byte;
00297             }
00298         }
00299     }
00300 
00301   /*
00302    * Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones.
00303    */
00304 
00305   byte = *((unsigned char *) str);
00306 #ifdef JISX0208
00307   if (0xA1 <= byte && byte <= 0xFE)
00308     {
00309       int ku, ten;
00310 
00311       ku = (byte & 0x7F) - 0x20;
00312       ten = (str[1] & 0x7F) - 0x20;
00313       if ((ku < 1 || ku > 92) || (ten < 1 || ten > 94))
00314        {
00315          *chPtr = (Tcl_UniChar) byte;
00316          return 1;
00317        }
00318 
00319       *chPtr = (Tcl_UniChar) UnicodeTbl[ku - 1][ten - 1];
00320       return 2;
00321     }
00322   else
00323 #endif /* JISX0208 */
00324   if (byte < 0xC0)
00325     {
00326       /*
00327        * Handles properly formed UTF-8 characters between
00328        * 0x01 and 0x7F.  Also treats \0 and naked trail
00329        * bytes 0x80 to 0xBF as valid characters representing
00330        * themselves.
00331        */
00332 
00333       *chPtr = (Tcl_UniChar) byte;
00334       return 1;
00335     }
00336   else if (byte < 0xE0)
00337     {
00338       if ((str[1] & 0xC0) == 0x80)
00339        {
00340          /*
00341           * Two-byte-character lead-byte followed
00342           * by a trail-byte.
00343           */
00344 
00345          *chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6) | (str[1] & 0x3F));
00346          return 2;
00347        }
00348       /*
00349        * A two-byte-character lead-byte not followed by trail-byte
00350        * represents itself.
00351        */
00352 
00353       *chPtr = (Tcl_UniChar) byte;
00354       return 1;
00355     }
00356   else if (byte < 0xF0)
00357     {
00358       if (((str[1] & 0xC0) == 0x80) && ((str[2] & 0xC0) == 0x80))
00359        {
00360          /*
00361           * Three-byte-character lead byte followed by
00362           * two trail bytes.
00363           */
00364 
00365          *chPtr = (Tcl_UniChar) (((byte & 0x0F) << 12)
00366                               | ((str[1] & 0x3F) << 6) | (str[2] & 0x3F));
00367          return 3;
00368        }
00369       /*
00370        * A three-byte-character lead-byte not followed by
00371        * two trail-bytes represents itself.
00372        */
00373 
00374       *chPtr = (Tcl_UniChar) byte;
00375       return 1;
00376     }
00377 #if TCL_UTF_MAX > 3
00378   else
00379     {
00380       int ch, total, trail;
00381 
00382       total = totalBytes[byte];
00383       trail = total - 1;
00384       if (trail > 0)
00385        {
00386          ch = byte & (0x3F >> trail);
00387          do
00388            {
00389              str++;
00390              if ((*str & 0xC0) != 0x80)
00391               {
00392                 *chPtr = byte;
00393                 return 1;
00394               }
00395              ch <<= 6;
00396              ch |= (*str & 0x3F);
00397              trail--;
00398            }
00399          while (trail > 0);
00400          *chPtr = ch;
00401          return total;
00402        }
00403     }
00404 #endif
00405 
00406   *chPtr = (Tcl_UniChar) byte;
00407   return 1;
00408 }
00409 
00410 /********************************************************************/
00411 /* font cache functions                                             */
00412 
00413 static int
00414 fontTest (void *element, void *key)
00415 {
00416   font_t *a = (font_t *) element;
00417   fontkey_t *b = (fontkey_t *) key;
00418 
00419   return (strcmp (a->fontlist, b->fontlist) == 0 && a->flags == b->flags);
00420 }
00421 
00422 static int useFontConfig(int flag)
00423 {
00424        if (fontConfigFlag) {
00425               return (!(flag & gdFTEX_FONTPATHNAME));
00426        } else {
00427               return flag & gdFTEX_FONTCONFIG;
00428        }
00429 }
00430 
00431 static void *
00432 fontFetch (char **error, void *key)
00433 {
00434   font_t *a;
00435   fontkey_t *b = (fontkey_t *) key;
00436   char *suffix;
00437   FT_Error err;
00438 
00439   *error = NULL;
00440 
00441   a = (font_t *) gdMalloc (sizeof (font_t));
00442   a->fontlist = strdup (b->fontlist);
00443   a->flags = b->flags;
00444   a->library = b->library;
00445   a->fontpath = NULL;
00446 
00447 #ifdef HAVE_LIBFONTCONFIG
00448   if (!useFontConfig(b->flags)) 
00449        *error = font_path(&(a->fontpath), a->fontlist);
00450               else
00451        *error = font_pattern(&(a->fontpath), a->fontlist);
00452 #else
00453   *error = font_path(&(a->fontpath), a->fontlist);
00454 #endif /* HAVE_LIBFONTCONFIG */
00455   if (*error || !a->fontpath || !a->fontpath[0])
00456                 {
00457       /* 2.0.12: TBB: free these. Thanks to Frank Faubert. */
00458       free (a->fontlist);
00459       if (a->fontpath)
00460        free (a->fontpath);
00461       gdFree (a);
00462 
00463       if (! *error)
00464        *error = "font_path() returned an empty font pathname";
00465 
00466       return NULL;
00467     }
00468 
00469 #if 0
00470 fprintf(stderr,"fontpathname=%s\n",fullname);
00471 #endif
00472 
00473   err = FT_New_Face(*b->library, a->fontpath, 0, &a->face);
00474 
00475   /* Read kerning metrics for Postscript fonts. */
00476   if (!err
00477       && ((suffix = strstr(a->fontpath, ".pfa"))
00478          || (suffix = strstr(a->fontpath, ".pfb")))
00479       && ((strcpy(suffix, ".afm") && (access(a->fontpath, R_OK) == 0))
00480          || (strcpy(suffix, ".pfm") && (access(a->fontpath, R_OK) == 0))))
00481   {
00482     err = FT_Attach_File(a->face, a->fontpath);
00483   }
00484 
00485   if (err)
00486     {
00487       /* 2.0.12: TBB: free these. Thanks to Frank Faubert. */
00488       free (a->fontlist);
00489       free (a->fontpath);
00490       gdFree (a);
00491       *error = "Could not read font";
00492       return NULL;
00493     }
00494 
00495   return (void *) a;
00496 }
00497 
00498 static void
00499 fontRelease (void *element)
00500 {
00501   font_t *a = (font_t *) element;
00502 
00503   FT_Done_Face (a->face);
00504   gdFree (a->fontlist);
00505   gdFree (a->fontpath);
00506   gdFree ((char *) element);
00507 }
00508 
00509 /********************************************************************/
00510 /* tweencolor cache functions                                            */
00511 
00512 static int
00513 tweenColorTest (void *element, void *key)
00514 {
00515   tweencolor_t *a = (tweencolor_t *) element;
00516   tweencolorkey_t *b = (tweencolorkey_t *) key;
00517 
00518   return (a->pixel == b->pixel
00519          && a->bgcolor == b->bgcolor
00520          && a->fgcolor == b->fgcolor && a->im == b->im);
00521 }
00522 
00523 /*
00524  * Computes a color in im's color table that is part way between
00525  * the background and foreground colors proportional to the gray
00526  * pixel value in the range 0-NUMCOLORS. The fg and bg colors must already
00527  * be in the color table for palette images. For truecolor images the
00528  * returned value simply has an alpha component and gdImageAlphaBlend
00529  * does the work so that text can be alpha blended across a complex
00530  * background (TBB; and for real in 2.0.2).
00531  */
00532 static void *
00533 tweenColorFetch (char **error, void *key)
00534 {
00535   tweencolor_t *a;
00536   tweencolorkey_t *b = (tweencolorkey_t *) key;
00537   int pixel, npixel, bg, fg;
00538   gdImagePtr im;
00539 
00540   a = (tweencolor_t *) gdMalloc (sizeof (tweencolor_t));
00541   pixel = a->pixel = b->pixel;
00542   bg = a->bgcolor = b->bgcolor;
00543   fg = a->fgcolor = b->fgcolor;
00544   im = b->im;
00545 
00546   /* if fg is specified by a negative color idx, then don't antialias */
00547   if (fg < 0)
00548     {
00549       if ((pixel + pixel) >= NUMCOLORS)
00550        a->tweencolor = -fg;
00551       else
00552        a->tweencolor = bg;
00553     }
00554   else
00555     {
00556       npixel = NUMCOLORS - pixel;
00557       if (im->trueColor)
00558        {
00559          /* 2.0.1: use gdImageSetPixel to do the alpha blending work,
00560             or to just store the alpha level. All we have to do here
00561             is incorporate our knowledge of the percentage of this
00562             pixel that is really "lit" by pushing the alpha value
00563             up toward transparency in edge regions. */
00564          a->tweencolor = gdTrueColorAlpha (gdTrueColorGetRed (fg),
00565                                        gdTrueColorGetGreen (fg),
00566                                        gdTrueColorGetBlue (fg),
00567                                        gdAlphaMax -
00568                                        (gdTrueColorGetAlpha (fg) *
00569                                         pixel / NUMCOLORS));
00570        }
00571       else
00572        {
00573          a->tweencolor = gdImageColorResolve (im,
00574                                           (pixel * im->red[fg] +
00575                                           npixel * im->red[bg]) /
00576                                           NUMCOLORS,
00577                                           (pixel * im->green[fg] +
00578                                           npixel * im->green[bg]) /
00579                                           NUMCOLORS,
00580                                           (pixel * im->blue[fg] +
00581                                           npixel * im->blue[bg]) /
00582                                           NUMCOLORS);
00583        }
00584     }
00585   return (void *) a;
00586 }
00587 
00588 static void
00589 tweenColorRelease (void *element)
00590 {
00591   gdFree ((char *) element);
00592 }
00593 
00594 /* draw_bitmap - transfers glyph bitmap to GD image */
00595 static char *
00596 gdft_draw_bitmap (gdCache_head_t * tc_cache, gdImage * im, int fg,
00597                 FT_Bitmap bitmap, int pen_x, int pen_y)
00598 {
00599   unsigned char *pixel = NULL;
00600   int *tpixel = NULL;
00601   int opixel;
00602   int x, y, row, col, pc, pcr;
00603 
00604   tweencolor_t *tc_elem;
00605   tweencolorkey_t tc_key;
00606 
00607   /* copy to image, mapping colors */
00608   tc_key.fgcolor = fg;
00609   tc_key.im = im;
00610   /* Truecolor version; does not require the cache */
00611   if (im->trueColor)
00612     {
00613       for (row = 0; row < bitmap.rows; row++)
00614        {
00615          pc = row * bitmap.pitch;
00616          pcr = pc;
00617          y = pen_y + row;
00618          /* clip if out of bounds */
00619          /* 2.0.16: clipping rectangle, not image bounds */
00620          if ((y > im->cy2) || (y < im->cy1))
00621            continue;
00622          for (col = 0; col < bitmap.width; col++, pc++)
00623            {
00624              int level;
00625              if (bitmap.pixel_mode == ft_pixel_mode_grays)
00626               {
00627                 /*
00628                  * Scale to 128 levels of alpha for gd use.
00629                  * alpha 0 is opacity, so be sure to invert at the end
00630                  */
00631                 level = (bitmap.buffer[pc] * gdAlphaMax /
00632                         (bitmap.num_grays - 1));
00633               }
00634              else if (bitmap.pixel_mode == ft_pixel_mode_mono)
00635               {
00636                 /* 2.0.5: mode_mono fix from Giuliano Pochini */
00637                 level =
00638                   ((bitmap.
00639                     buffer[(col >> 3) +
00640                           pcr]) & (1 << (~col & 0x07))) ?
00641                   gdAlphaTransparent : gdAlphaOpaque;
00642               }
00643              else
00644               {
00645                 return "Unsupported ft_pixel_mode";
00646               }
00647               if (level == 0)  /* if background */
00648               continue;
00649 
00650              if ((fg >= 0) && (im->trueColor))
00651               {
00652                 /* Consider alpha in the foreground color itself to be an
00653                    upper bound on how opaque things get, when truecolor is
00654                    available. Without truecolor this results in far too many
00655                    color indexes. */
00656                 level =
00657                   level * (gdAlphaMax -
00658                           gdTrueColorGetAlpha (fg)) / gdAlphaMax;
00659               }
00660              level = gdAlphaMax - level;   /* inverting to get alpha */
00661              x = pen_x + col;
00662              /* clip if out of bounds */
00663              /* 2.0.16: clip to clipping rectangle, Matt McNabb */
00664              if ((x > im->cx2) || (x < im->cx1))
00665               continue;
00666              /* get pixel location in gd buffer */
00667              tpixel = &im->tpixels[y][x];
00668              if (fg < 0)
00669               {
00670                 if (level < (gdAlphaMax / 2))
00671                   {
00672                     *tpixel = -fg;
00673                   }
00674               }
00675              else
00676               {
00677                 if (im->alphaBlendingFlag)
00678                   {
00679                     opixel = *tpixel;
00680                     if (gdTrueColorGetAlpha(opixel) != gdAlphaTransparent)
00681                      {
00682                        *tpixel = gdAlphaBlend (opixel,
00683                                   (level << 24) + (fg & 0xFFFFFF));
00684                      }
00685                     else
00686                      {
00687                        *tpixel = (level << 24) + (fg & 0xFFFFFF);
00688                      }
00689                   }
00690                 else
00691                   {
00692                     *tpixel = (level << 24) + (fg & 0xFFFFFF);
00693                   }
00694               }
00695            }
00696        }
00697       return (char *) NULL;
00698     }
00699   /* Non-truecolor case, restored to its more or less original form */
00700   for (row = 0; row < bitmap.rows; row++)
00701     {
00702       int pcr;
00703       pc = row * bitmap.pitch;
00704       pcr = pc;
00705       if (bitmap.pixel_mode == ft_pixel_mode_mono)
00706        pc *= 8;             /* pc is measured in bits for monochrome images */
00707 
00708       y = pen_y + row;
00709 
00710       /* clip if out of bounds */
00711       if (y > im->cy2 || y < im->cy1)
00712        continue;
00713 
00714       for (col = 0; col < bitmap.width; col++, pc++)
00715        {
00716          if (bitmap.pixel_mode == ft_pixel_mode_grays)
00717            {
00718              /*
00719               * Round to NUMCOLORS levels of antialiasing for
00720               * index color images since only 256 colors are
00721               * available.
00722               */
00723              tc_key.pixel = ((bitmap.buffer[pc] * NUMCOLORS)
00724                            + bitmap.num_grays / 2)
00725               / (bitmap.num_grays - 1);
00726            }
00727          else if (bitmap.pixel_mode == ft_pixel_mode_mono)
00728            {
00729              tc_key.pixel = ((bitmap.buffer[pc / 8]
00730                             << (pc % 8)) & 128) ? NUMCOLORS : 0;
00731              /* 2.0.5: mode_mono fix from Giuliano Pochini */
00732              tc_key.pixel =
00733               ((bitmap.
00734                 buffer[(col >> 3) +
00735                       pcr]) & (1 << (~col & 0x07))) ? NUMCOLORS : 0;
00736            }
00737          else
00738            {
00739              return "Unsupported ft_pixel_mode";
00740            }
00741          if (tc_key.pixel == 0)    /* if background */
00742            continue;
00743 
00744          x = pen_x + col;
00745 
00746          /* clip if out of bounds */
00747          if (x > im->cx2 || x < im->cx1)
00748            continue;
00749          /* get pixel location in gd buffer */
00750          pixel = &im->pixels[y][x];
00751          if (tc_key.pixel == NUMCOLORS)
00752            {
00753              /* use fg color directly. gd 2.0.2: watch out for
00754                 negative indexes (thanks to David Marwood). */
00755              *pixel = (fg < 0) ? -fg : fg;
00756            }
00757          else
00758            {
00759              /* find antialised color */
00760 
00761              tc_key.bgcolor = *pixel;
00762              tc_elem = (tweencolor_t *) gdCacheGet (tc_cache, &tc_key);
00763              *pixel = tc_elem->tweencolor;
00764            }
00765        }
00766     }
00767   return (char *) NULL;
00768 }
00769 
00770 extern int any2eucjp (char *, char *, unsigned int);
00771 
00772 /* Persistent font cache until explicitly cleared */
00773 /* Fonts can be used across multiple images */
00774 
00775 /* 2.0.16: thread safety (the font cache is shared) */
00776 gdMutexDeclare (gdFontCacheMutex);
00777 static gdCache_head_t *fontCache;
00778 static FT_Library library;
00779 
00780 BGD_DECLARE(void) gdFreeFontCache ()
00781 {
00782   gdFontCacheShutdown ();
00783 }
00784 
00785 BGD_DECLARE(void) gdFontCacheShutdown ()
00786 {
00787   if (fontCache)
00788     {
00789       gdMutexShutdown (gdFontCacheMutex);
00790       gdCacheDelete (fontCache);
00791       FT_Done_FreeType (library);
00792       /* 2.0.16: Gustavo Scotti: make sure we don't free this twice */
00793       fontCache = 0;
00794     }
00795 }
00796 
00797 /********************************************************************/
00798 /* gdImageStringFT -  render a utf8 string onto a gd image          */
00799 
00800 BGD_DECLARE(char *) gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
00801                double ptsize, double angle, int x, int y, char *string)
00802 {
00803   return gdImageStringFTEx (im, brect, fg, fontlist,
00804                          ptsize, angle, x, y, string, 0);
00805 }
00806 
00807 BGD_DECLARE(int) gdFontCacheSetup (void)
00808 {
00809   if (fontCache)
00810     {
00811       /* Already set up */
00812       return 0;
00813     }
00814   gdMutexSetup (gdFontCacheMutex);
00815   if (FT_Init_FreeType (&library))
00816     {
00817       gdMutexShutdown (gdFontCacheMutex);
00818       return -1;
00819     }
00820   fontCache = gdCacheCreate (FONTCACHESIZE, fontTest, fontFetch, fontRelease);
00821   return 0;
00822 }
00823 
00824 /* the platform-independent resolution used for size and position calculations */
00825 /*    the size of the error introduced by rounding is affected by this number */
00826 #define METRIC_RES 300
00827 
00828 BGD_DECLARE(char *) gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist,
00829                  double ptsize, double angle, int x, int y, char *string,
00830                  gdFTStringExtraPtr strex)
00831 {
00832   FT_Matrix matrix;
00833   FT_Vector penf, oldpenf, delta, total_min = {0,0}, total_max = {0,0}, glyph_min, glyph_max;
00834   FT_Face face;
00835   FT_CharMap charmap;
00836   FT_Glyph image;
00837   FT_GlyphSlot slot;
00838   FT_Error err;
00839   FT_UInt glyph_index, previous;
00840   double sin_a = sin (angle);
00841   double cos_a = cos (angle);
00842   int len, i, ch;
00843   font_t *font;
00844   fontkey_t fontkey;
00845   char *next;
00846   char *tmpstr = 0;
00847   int render = (im && (im->trueColor || (fg <= 255 && fg >= -255)));
00848   FT_BitmapGlyph bm;
00849   /* 2.0.13: Bob Ostermann: don't force autohint, that's just for testing 
00850      freetype and doesn't look as good */
00851   int render_mode = FT_LOAD_DEFAULT;
00852   int encoding, encodingfound;
00853   /* Now tuneable thanks to Wez Furlong */
00854   double linespace = LINESPACE;
00855   /* 2.0.6: put this declaration with the other declarations! */
00856   /*
00857    *   make a new tweenColorCache on every call
00858    *   because caching colormappings between calls
00859    *   is not safe. If the im-pointer points to a
00860    *   brand new image, the cache gives out bogus
00861    *   colorindexes.          -- 27.06.2001 <krisku@arrak.fi>
00862    */
00863   gdCache_head_t *tc_cache;
00864   /* Tuneable horizontal and vertical resolution in dots per inch */
00865   int hdpi, vdpi, horiAdvance, xshow_alloc = 0, xshow_pos = 0;
00866   FT_Size platform_specific, platform_independent;
00867 
00868   if (strex)
00869     {
00870       if ((strex->flags & gdFTEX_LINESPACE) == gdFTEX_LINESPACE)
00871        {
00872          linespace = strex->linespacing;
00873        }
00874     }
00875   tc_cache = gdCacheCreate (TWEENCOLORCACHESIZE,
00876                          tweenColorTest, tweenColorFetch,
00877                          tweenColorRelease);
00878 
00879 /***** initialize font library and font cache on first call ******/
00880   if (!fontCache)
00881     {
00882       if (gdFontCacheSetup () != 0)
00883        {
00884          gdCacheDelete (tc_cache);
00885          return "Failure to initialize font library";
00886        }
00887     }
00888 /*****/
00889   gdMutexLock (gdFontCacheMutex);
00890   /* get the font (via font cache) */
00891   fontkey.fontlist = fontlist;
00892   if (strex)
00893        fontkey.flags = strex->flags & (gdFTEX_FONTPATHNAME |
00894               gdFTEX_FONTCONFIG);
00895   else
00896        fontkey.flags = 0;
00897   fontkey.library = &library;
00898   font = (font_t *) gdCacheGet (fontCache, &fontkey);
00899   if (!font)
00900     {
00901       gdCacheDelete (tc_cache);
00902       gdMutexUnlock (gdFontCacheMutex);
00903       return fontCache->error;
00904     }
00905   face = font->face;        /* shortcut */
00906   slot = face->glyph;              /* shortcut */
00907 
00908   /*
00909    * Added hdpi and vdpi to support images at non-screen resolutions, i.e. 300 dpi TIFF,
00910    *    or 100h x 50v dpi FAX format. 2.0.23.
00911    * 2004/02/27 Mark Shackelford, mark.shackelford@acs-inc.com
00912    */
00913   hdpi = GD_RESOLUTION;
00914   vdpi = GD_RESOLUTION;
00915   encoding = gdFTEX_Unicode;
00916   if (strex)
00917     {
00918       if (strex->flags & gdFTEX_RESOLUTION)
00919     {
00920       hdpi = strex->hdpi;
00921       vdpi = strex->vdpi;
00922     }
00923       if (strex->flags & gdFTEX_XSHOW)
00924         {
00925           strex->xshow = NULL;
00926         }
00927       /* 2.0.12: allow explicit specification of the preferred map;
00928          but we still fall back if it is not available. */
00929       if (strex->flags & gdFTEX_CHARMAP)
00930         {
00931           encoding = strex->charmap;
00932         }
00933       /* 2.0.29: we can return the font path if desired */
00934       if (strex->flags & gdFTEX_RETURNFONTPATHNAME) 
00935         strex->fontpath = strdup(font->fontpath);
00936       else
00937         strex->fontpath = 0;
00938     }
00939 
00940   matrix.xx = (FT_Fixed) (cos_a * (1 << 16));
00941   matrix.yx = (FT_Fixed) (sin_a * (1 << 16));
00942   matrix.xy = -matrix.yx;
00943   matrix.yy = matrix.xx;
00944 
00945   /* set rotation transform */
00946   FT_Set_Transform (face, &matrix, NULL);
00947 
00948   FT_New_Size (face, &platform_independent);
00949   FT_Activate_Size (platform_independent);
00950   if (FT_Set_Char_Size (face, 0, (FT_F26Dot6)(ptsize*64), METRIC_RES, METRIC_RES))
00951     {
00952       gdCacheDelete (tc_cache);
00953       gdMutexUnlock (gdFontCacheMutex);
00954       return "Could not set character size";
00955     }
00956 
00957   if (render)
00958     {
00959       FT_New_Size (face, &platform_specific);
00960       FT_Activate_Size (platform_specific);
00961       if (FT_Set_Char_Size (face, 0, (FT_F26Dot6)(ptsize*64), hdpi, vdpi))
00962         {
00963           gdCacheDelete (tc_cache);
00964           gdMutexUnlock (gdFontCacheMutex);
00965           return "Could not set character size";
00966         }
00967     }
00968 
00969   if (fg < 0)
00970       render_mode |= FT_LOAD_MONOCHROME;
00971 
00972   /* find requested charmap */
00973   encodingfound = 0;
00974   for (i = 0; i < face->num_charmaps; i++)
00975     {
00976       charmap = face->charmaps[i];
00977 
00978 #if ((defined(FREETYPE_MAJOR)) && (((FREETYPE_MAJOR == 2) && (((FREETYPE_MINOR == 1) && (FREETYPE_PATCH >= 3)) || (FREETYPE_MINOR > 1))) || (FREETYPE_MAJOR > 2)))
00979       if (encoding == gdFTEX_Unicode)
00980        {
00981          if (charmap->encoding == FT_ENCODING_MS_SYMBOL
00982              || charmap->encoding == FT_ENCODING_UNICODE
00983              || charmap->encoding == FT_ENCODING_ADOBE_CUSTOM
00984              || charmap->encoding == FT_ENCODING_ADOBE_STANDARD)
00985            {
00986              encodingfound++;
00987              break;
00988            }
00989        }
00990       else if (encoding == gdFTEX_Big5)
00991        {
00992 /* renamed sometime after freetype-2.1.4 */
00993 #ifndef FT_ENCODING_BIG5
00994 #define FT_ENCODING_BIG5 FT_ENCODING_MS_BIG5
00995 #endif
00996          if (charmap->encoding == FT_ENCODING_BIG5)
00997            {
00998              encodingfound++;
00999              break;
01000            }
01001        }
01002       else if (encoding == gdFTEX_Shift_JIS)
01003        {
01004 /* renamed sometime after freetype-2.1.4 */
01005 #ifndef FT_ENCODING_SJIS
01006 #define FT_ENCODING_SJIS FT_ENCODING_MS_SJIS
01007 #endif
01008          if (charmap->encoding == FT_ENCODING_SJIS)
01009            {
01010              encodingfound++;
01011              break;
01012            }
01013        }
01014 #else
01015       if (encoding == gdFTEX_Unicode)
01016        {
01017          if ((charmap->platform_id = 3 && charmap->encoding_id == 1)     /* Windows Unicode */
01018              || (charmap->platform_id == 3 && charmap->encoding_id == 0) /* Windows Symbol */
01019              || (charmap->platform_id == 2 && charmap->encoding_id == 1) /* ISO Unicode */
01020              || (charmap->platform_id == 0))                          /* Apple Unicode */
01021            {
01022              encodingfound++;
01023              break;
01024            }
01025        }
01026       else if (encoding == gdFTEX_Big5)
01027        {
01028           if (charmap->platform_id == 3 && charmap->encoding_id == 4)     /* Windows Big5 */
01029            {
01030              encodingfound++;
01031              break;
01032            }
01033        }
01034       else if (encoding == gdFTEX_Shift_JIS)
01035        {
01036           if (charmap->platform_id == 3 && charmap->encoding_id == 2)     /* Windows Sjis */
01037            {
01038              encodingfound++;
01039              break;
01040            }
01041        }
01042 #endif
01043     }
01044   if (encodingfound)
01045     {
01046       FT_Set_Charmap(face, charmap);
01047     }
01048   else
01049     {
01050       /* No character set found! */
01051       gdMutexUnlock (gdFontCacheMutex);
01052       return "No character set found";
01053     }
01054 
01055 #ifndef JISX0208
01056   if (encoding == gdFTEX_Shift_JIS)
01057     {
01058 #endif
01059       if ((tmpstr = (char *) gdMalloc (BUFSIZ)))
01060        {
01061          any2eucjp (tmpstr, string, BUFSIZ);
01062          next = tmpstr;
01063        }
01064       else
01065        {
01066          next = string;
01067        }
01068 #ifndef JISX0208
01069     }
01070   else
01071     {
01072       next = string;
01073     }
01074 #endif
01075 
01076 #if 0
01077 fprintf(stderr,"dpi=%d,%d metric_res=%d ptsize=%g\n",hdpi,vdpi,METRIC_RES,ptsize);
01078 #endif
01079 
01080   oldpenf.x = oldpenf.y = 0; /* for postscript xshow operator */
01081   penf.x = penf.y = 0;      /* running position of non-rotated glyphs */
01082   previous = 0;             /* index of previous glyph for kerning calculations */
01083   for (i=0; *next; i++)
01084     {
01085       FT_Activate_Size (platform_independent);
01086 
01087       ch = *next;
01088 
01089       /* carriage returns */
01090       if (ch == '\r')
01091        {
01092          penf.x = 0;
01093          previous = 0;             /* clear kerning flag */
01094          next++;
01095          continue;
01096        }
01097       /* newlines */
01098       if (ch == '\n')
01099        {
01100          /* 2.0.13: reset penf.x. Christopher J. Grayce */
01101          penf.x = 0;
01102          penf.y += linespace * ptsize * 64 * METRIC_RES / 72;
01103          penf.y &= ~63;     /* round down to 1/METRIC_RES */
01104          previous = 0;             /* clear kerning flag */
01105          next++;
01106          continue;
01107        }
01108 
01109 
01110         switch (encoding)
01111          {
01112          case gdFTEX_Unicode:
01113            {
01114              /* use UTF-8 mapping from ASCII */
01115              len = gdTcl_UtfToUniChar (next, &ch);
01116 /* EAM DEBUG */
01117 /* TBB: get this exactly right: 2.1.3 *or better*, all possible cases. */
01118 /* 2.0.24: David R. Morrison: use the more complete ifdef here. */
01119 #if ((defined(FREETYPE_MAJOR)) && (((FREETYPE_MAJOR == 2) && (((FREETYPE_MINOR == 1) && (FREETYPE_PATCH >= 3)) || (FREETYPE_MINOR > 1))) || (FREETYPE_MAJOR > 2)))
01120              if (charmap->encoding == FT_ENCODING_MS_SYMBOL)
01121 #else
01122              if (charmap->platform_id == 3 && charmap->encoding_id == 0)
01123 #endif /* Freetype 2.1 or better */
01124               {
01125                 /* I do not know the significance of the constant 0xf000. */
01126                 /* It was determined by inspection of the character codes */
01127                 /* stored in Microsoft font symbol.ttf                    */
01128                 ch |= 0xf000;
01129               }
01130 /* EAM DEBUG */
01131              next += len;
01132            }
01133            break;
01134          case gdFTEX_Shift_JIS:
01135            {
01136              unsigned char c;
01137              int jiscode;
01138              c = *next;
01139              if (0xA1 <= c && c <= 0xFE)
01140                {
01141                  next++;
01142                  jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F);
01143       
01144                 ch = (jiscode >> 8) & 0xFF;
01145                 jiscode &= 0xFF;
01146 
01147                 if (ch & 1)
01148                   jiscode += 0x40 - 0x21;
01149                 else
01150                   jiscode += 0x9E - 0x21;
01151 
01152                 if (jiscode >= 0x7F)
01153                   jiscode++;
01154                 ch = (ch - 0x21) / 2 + 0x81;
01155                 if (ch >= 0xA0)
01156                   ch += 0x40;
01157 
01158                 ch = (ch << 8) + jiscode;
01159               }
01160              else
01161               {
01162                 ch = c & 0xFF;     /* don't extend sign */
01163               }
01164              next++;
01165            }
01166            break;
01167          case gdFTEX_Big5:
01168            {
01169              /*
01170               * Big 5 mapping:
01171               * use "JIS-8 half-width katakana" coding from 8-bit characters. Ref:
01172               * ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs
01173               */
01174              ch = (*next) & 0xFF;  /* don't extend sign */
01175              next++;
01176              if (ch >= 161  /* first code of JIS-8 pair */
01177                 && *next)
01178               {             /* don't advance past '\0' */
01179                 /* TBB: Fix from Kwok Wah On: & 255 needed */
01180                 ch = (ch * 256) + ((*next) & 255);
01181                 next++;
01182               }
01183            }
01184            break;
01185          }
01186 
01187       /* Convert character code to glyph index */
01188       glyph_index = FT_Get_Char_Index (face, ch);
01189 
01190       /* retrieve kerning distance */
01191       if ( ! (strex && (strex->flags & gdFTEX_DISABLE_KERNING))
01192               && ! FT_IS_FIXED_WIDTH(face)
01193               && FT_HAS_KERNING(face)
01194               && previous
01195               && glyph_index)
01196         FT_Get_Kerning (face, previous, glyph_index, ft_kerning_default, &delta);
01197       else
01198        delta.x = delta.y = 0;
01199 
01200          penf.x += delta.x;
01201 
01202       /* When we know the position of the second or subsequent character,
01203        save the (kerned) advance from the preceeding character in the
01204        xshow vector */
01205       if (i && strex && (strex->flags & gdFTEX_XSHOW))
01206         {
01207          /* make sure we have enough allocation for two numbers
01208               so we don't have to recheck for the terminating number */
01209          if (! xshow_alloc) {
01210               xshow_alloc = 100;
01211               strex->xshow = malloc(xshow_alloc);
01212               xshow_pos = 0;
01213          } 
01214          else if (xshow_pos + 20 > xshow_alloc) {
01215               xshow_alloc += 100;
01216               strex->xshow = realloc(strex->xshow, xshow_alloc);
01217        }
01218          xshow_pos += sprintf(strex->xshow + xshow_pos, "%g ",
01219               (double)(penf.x - oldpenf.x) * hdpi / (64 * METRIC_RES));
01220         }
01221       oldpenf.x = penf.x;
01222 
01223       /* load glyph image into the slot (erase previous one) */
01224       err = FT_Load_Glyph (face, glyph_index, render_mode);
01225       if (err)
01226        {
01227          gdCacheDelete (tc_cache);
01228          gdMutexUnlock (gdFontCacheMutex);
01229          return "Problem loading glyph";
01230        }
01231 
01232       horiAdvance = slot->metrics.horiAdvance;
01233 
01234       if (brect)
01235        {                    /* only if need brect */
01236 
01237          glyph_min.x = penf.x + slot->metrics.horiBearingX;
01238          glyph_min.y = penf.y - slot->metrics.horiBearingY;
01239 
01240 #if 0
01241          if (ch == ' ')        /* special case for trailing space */
01242             {
01243               glyph_max.x = penf.x + horiAdvance;
01244             }
01245           else
01246             {
01247              glyph_max.x = glyph_min.x + slot->metrics.width;
01248             }
01249 #else
01250           glyph_max.x = penf.x + horiAdvance;
01251 #endif
01252          glyph_max.y = glyph_min.y + slot->metrics.height;
01253 
01254          if (i==0)
01255            {
01256              total_min = glyph_min;
01257              total_max = glyph_max;
01258            }
01259          else
01260            {
01261               if (glyph_min.x < total_min.x)
01262                   total_min.x = glyph_min.x;
01263               if (glyph_min.y < total_min.y)
01264                   total_min.y = glyph_min.y;
01265               if (glyph_max.x > total_max.x)
01266                   total_max.x = glyph_max.x;
01267               if (glyph_max.y > total_max.y)
01268                   total_max.y = glyph_max.y;
01269            }
01270        }
01271 
01272       if (render)
01273        {
01274           FT_Activate_Size (platform_specific);
01275 
01276           /* load glyph again into the slot (erase previous one)  - this time with scaling */
01277           err = FT_Load_Glyph (face, glyph_index, render_mode);
01278           if (err)
01279            {
01280              gdCacheDelete (tc_cache);
01281              gdMutexUnlock (gdFontCacheMutex);
01282              return "Problem loading glyph";
01283            }
01284 
01285           /* load and transform glyph image */
01286           FT_Get_Glyph (slot, &image);
01287 
01288          if (image->format != ft_glyph_format_bitmap)
01289            {
01290              err = FT_Glyph_To_Bitmap (&image, ft_render_mode_normal, 0, 1);
01291              if (err)
01292               {
01293                 gdCacheDelete (tc_cache);
01294                 gdMutexUnlock (gdFontCacheMutex);
01295                 return "Problem rendering glyph";
01296               }
01297            }
01298 
01299          /* now, draw to our target surface */
01300          bm = (FT_BitmapGlyph) image;
01301           /* position rounded down to nearest pixel at current dpi
01302                (the estimate was rounded up to next 1/METRIC_RES, so this should fit) */
01303          gdft_draw_bitmap (tc_cache, im, fg, bm->bitmap,
01304                     x + (penf.x * cos_a + penf.y * sin_a)*hdpi/(METRIC_RES*64) + bm->left,
01305                     y - (penf.x * sin_a - penf.y * cos_a)*vdpi/(METRIC_RES*64) - bm->top);
01306 
01307           FT_Done_Glyph (image);
01308        }
01309 
01310       /* record current glyph index for kerning */
01311       previous = glyph_index;
01312 
01313       penf.x += horiAdvance;
01314     }
01315 
01316   /* Save the (unkerned) advance from the last character in the xshow vector */
01317   if (strex && (strex->flags & gdFTEX_XSHOW) && strex->xshow)
01318     {
01319        sprintf(strex->xshow + xshow_pos, "%g",
01320                 (double)(penf.x - oldpenf.x) * hdpi / (64 * METRIC_RES) );
01321     }
01322 
01323   if (brect)
01324     {                       /* only if need brect */
01325       double dpix, dpiy;
01326       
01327       dpix = 64 * METRIC_RES / hdpi;
01328       dpiy = 64 * METRIC_RES / vdpi;
01329 
01330       /* increase by 1 pixel to allow for rounding */
01331       total_min.x -= METRIC_RES;
01332       total_min.y -= METRIC_RES;
01333       total_max.x += METRIC_RES;
01334       total_max.y += METRIC_RES;
01335  
01336       /* rotate bounding rectangle, scale and round to int pixels, and translate */
01337       brect[0] = x + (total_min.x * cos_a + total_max.y * sin_a)/dpix;
01338       brect[1] = y - (total_min.x * sin_a - total_max.y * cos_a)/dpiy;
01339       brect[2] = x + (total_max.x * cos_a + total_max.y * sin_a)/dpix;
01340       brect[3] = y - (total_max.x * sin_a - total_max.y * cos_a)/dpiy;
01341       brect[4] = x + (total_max.x * cos_a + total_min.y * sin_a)/dpix;
01342       brect[5] = y - (total_max.x * sin_a - total_min.y * cos_a)/dpiy;
01343       brect[6] = x + (total_min.x * cos_a + total_min.y * sin_a)/dpix;
01344       brect[7] = y - (total_min.x * sin_a - total_min.y * cos_a)/dpiy;
01345     }
01346 
01347   FT_Done_Size (platform_independent);
01348   if (render)
01349     FT_Done_Size (platform_specific);
01350 
01351   if (tmpstr)
01352     gdFree (tmpstr);
01353   gdCacheDelete (tc_cache);
01354   gdMutexUnlock (gdFontCacheMutex);
01355   return (char *) NULL;
01356 }
01357 
01358 #endif /* HAVE_LIBFREETYPE */
01359 
01360 #ifdef HAVE_LIBFONTCONFIG
01361 /* Code to find font path, with special mapping for Postscript font names.
01362  *
01363  * Dag Lem <dag@nimrod.no>
01364  */
01365 
01366 #include <fontconfig/fontconfig.h>
01367 
01368 /* #define NO_POSTSCRIPT_ALIAS 1 */
01369 #ifndef NO_POSTSCRIPT_ALIAS
01370 typedef struct _PostscriptAlias {
01371   char* name;
01372   char* family;
01373   char* style;
01374 } PostscriptAlias;
01375 
01376 /* This table maps standard Postscript font names to URW Type 1 fonts.
01377    The mapping is converted from Ghostscript (Fontmap.GS)
01378    for use with fontconfig. */
01379 static PostscriptAlias postscript_alias[] = {
01380   { "AvantGarde-Book", "URW Gothic L", "Book" },
01381   { "AvantGarde-BookOblique", "URW Gothic L", "Book Oblique" },
01382   { "AvantGarde-Demi", "URW Gothic L", "Demi" },
01383   { "AvantGarde-DemiOblique", "URW Gothic L", "Demi Oblique" },
01384 
01385   { "Bookman-Demi", "URW Bookman L", "Demi Bold" },
01386   { "Bookman-DemiItalic", "URW Bookman L", "Demi Bold Italic" },
01387   { "Bookman-Light", "URW Bookman L", "Light" },
01388   { "Bookman-LightItalic", "URW Bookman L", "Light Italic" },
01389 
01390   { "Courier", "Nimbus Mono L", "Regular" },
01391   { "Courier-Oblique", "Nimbus Mono L", "Regular Oblique" },
01392   { "Courier-Bold", "Nimbus Mono L", "Bold" },
01393   { "Courier-BoldOblique", "Nimbus Mono L", "Bold Oblique" },
01394   
01395   { "Helvetica", "Nimbus Sans L", "Regular" },
01396   { "Helvetica-Oblique", "Nimbus Sans L", "Regular Italic" },
01397   { "Helvetica-Bold", "Nimbus Sans L", "Bold" },
01398   { "Helvetica-BoldOblique", "Nimbus Sans L", "Bold Italic" },
01399 
01400   { "Helvetica-Narrow", "Nimbus Sans L", "Regular Condensed" },
01401   { "Helvetica-Narrow-Oblique", "Nimbus Sans L", "Regular Condensed Italic" },
01402   { "Helvetica-Narrow-Bold", "Nimbus Sans L", "Bold Condensed" },
01403   { "Helvetica-Narrow-BoldOblique", "Nimbus Sans L", "Bold Condensed Italic" },
01404 
01405   { "NewCenturySchlbk-Roman", "Century Schoolbook L", "Roman" },
01406   { "NewCenturySchlbk-Italic", "Century Schoolbook L", "Italic" },
01407   { "NewCenturySchlbk-Bold", "Century Schoolbook L", "Bold" },
01408   { "NewCenturySchlbk-BoldItalic", "Century Schoolbook L", "Bold Italic" },
01409 
01410   { "Palatino-Roman", "URW Palladio L", "Roman" },
01411   { "Palatino-Italic", "URW Palladio L", "Italic" },
01412   { "Palatino-Bold", "URW Palladio L", "Bold" },
01413   { "Palatino-BoldItalic", "URW Palladio L", "Bold Italic" },
01414 
01415   { "Symbol", "Standard Symbols L", "Regular" },
01416 
01417   { "Times-Roman", "Nimbus Roman No9 L", "Regular" },
01418   { "Times-Italic", "Nimbus Roman No9 L", "Regular Italic" },
01419   { "Times-Bold", "Nimbus Roman No9 L", "Medium" },
01420   { "Times-BoldItalic", "Nimbus Roman No9 L", "Medium Italic" },
01421 
01422   { "ZapfChancery-MediumItalic", "URW Chancery L", "Medium Italic" },
01423 
01424   { "ZapfDingbats", "Dingbats", "" },
01425 };
01426 #endif
01427 
01428 
01429 static FcPattern* find_font(FcPattern* pattern)
01430 {
01431   FcResult result;
01432 
01433   FcConfigSubstitute(0, pattern, FcMatchPattern);
01434   FcConfigSubstitute(0, pattern, FcMatchFont);
01435   FcDefaultSubstitute(pattern);
01436 
01437   return FcFontMatch(0, pattern, &result);
01438 }
01439 
01440 
01441 #ifndef NO_POSTSCRIPT_ALIAS
01442 static char* find_postscript_font(FcPattern **fontpattern, char* fontname)
01443 {
01444   FcPattern* font = NULL;
01445   int i;
01446 
01447   *fontpattern = NULL;
01448   for (i = 0; i < sizeof(postscript_alias)/sizeof(*postscript_alias); i++) {
01449     if (strcmp(fontname, postscript_alias[i].name) == 0) {
01450       FcChar8* family;
01451 
01452       FcPattern* pattern =
01453        FcPatternBuild(0,
01454                      FC_FAMILY, FcTypeString, postscript_alias[i].family,
01455                      FC_STYLE, FcTypeString, postscript_alias[i].style,
01456                      (char*)0);
01457       font = find_font(pattern);
01458       FcPatternDestroy(pattern);
01459 
01460       if (!font || FcPatternGetString(font, FC_FAMILY, 0, &family) != FcResultMatch)
01461        return "fontconfig: Couldn't retrieve font family name.";
01462       
01463       /* Check whether we got the font family we wanted. */
01464       if (strcmp((const char *)family, postscript_alias[i].family) != 0) {
01465        FcPatternDestroy(font);
01466        return "fontconfig: Didn't find expected font family. Perhaps URW Type 1 fonts need installing?";
01467       }
01468       break;
01469     }
01470   }
01471 
01472   *fontpattern = font;
01473   return NULL;
01474 }
01475 #endif
01476 
01477 static char * font_pattern(char **fontpath, char *fontpattern)
01478 {
01479   FcPattern* font = NULL;
01480   FcChar8* file;
01481   FcPattern* pattern;
01482 #ifndef NO_POSTSCRIPT_ALIAS
01483   char *error;
01484 #endif
01485 
01486   *fontpath = NULL;
01487 #ifndef NO_POSTSCRIPT_ALIAS
01488   error = find_postscript_font(&font, fontpattern);
01489 
01490   if (!font) {
01491     if (error)
01492       return error;
01493 #endif
01494     pattern = FcNameParse((const FcChar8 *)fontpattern);
01495     font = find_font(pattern);
01496     FcPatternDestroy(pattern);
01497 #ifndef NO_POSTSCRIPT_ALIAS
01498   }
01499 #endif
01500 
01501   if (!font || FcPatternGetString(font, FC_FILE, 0, &file) != FcResultMatch)
01502     return "fontconfig: Couldn't retrieve font file name.";
01503 
01504   *fontpath = strdup((const char *)file);
01505 
01506   FcPatternDestroy(font);
01507 
01508   return NULL;
01509 }
01510 
01511 #endif /* HAVE_LIBFONTCONFIG */
01512 
01513 /* Look up font using font names as file names. */
01514 static char * font_path(char **fontpath, char *name_list)
01515 {
01516   int font_found = 0;
01517   char *fontsearchpath, *fontlist;
01518   char *fullname = NULL;
01519   char *name, *path, *dir;
01520   char *strtok_ptr;
01521 
01522   /*
01523    * Search the pathlist for any of a list of font names.
01524    */
01525   *fontpath = NULL;
01526   fontsearchpath = getenv ("GDFONTPATH");
01527   if (!fontsearchpath)
01528     fontsearchpath = DEFAULT_FONTPATH;
01529   fontlist = strdup (name_list);
01530 
01531   /*
01532    * Must use gd_strtok_r else pointer corrupted by strtok in nested loop.
01533    */
01534   for (name = gd_strtok_r (fontlist, LISTSEPARATOR, &strtok_ptr); name;
01535        name = gd_strtok_r (0, LISTSEPARATOR, &strtok_ptr))
01536     {
01537 
01538       /* make a fresh copy each time - strtok corrupts it. */
01539       path = strdup (fontsearchpath);
01540       /*
01541        * Allocate an oversized buffer that is guaranteed to be
01542        * big enough for all paths to be tested.
01543        */
01544       /* 2.0.22: Thorben Kundinger: +8 is needed, not +6. */
01545       fullname = gdRealloc (fullname,
01546                           strlen (fontsearchpath) + strlen (name) + 8);
01547       /* if name is an absolute or relative pathname then test directly */
01548       if (strchr (name, '/')
01549          || (name[0] != 0 && name[1] == ':'
01550              && (name[2] == '/' || name[2] == '\\')))
01551        {
01552          sprintf (fullname, "%s", name);
01553          if (access (fullname, R_OK) == 0)
01554            {
01555              font_found++;
01556               /* 2.0.16: memory leak fixed, Gustavo Scotti */
01557               gdFree (path);
01558              break;
01559            }
01560        }
01561       for (dir = strtok (path, PATHSEPARATOR); dir;
01562           dir = strtok (0, PATHSEPARATOR))
01563        {
01564           if (strchr (name, '.'))
01565            {
01566              sprintf (fullname, "%s/%s", dir, name);
01567              if (access (fullname, R_OK) == 0)
01568                {
01569                  font_found++;
01570                  break;
01571                }
01572              else
01573               {
01574                 continue;
01575               }
01576             }
01577          sprintf (fullname, "%s/%s.ttf", dir, name);
01578          if (access (fullname, R_OK) == 0)
01579            {
01580              font_found++;
01581              break;
01582            }
01583          sprintf (fullname, "%s/%s.pfa", dir, name);
01584          if (access (fullname, R_OK) == 0)
01585            {
01586              font_found++;
01587              break;
01588            }
01589          sprintf (fullname, "%s/%s.pfb", dir, name);
01590          if (access (fullname, R_OK) == 0)
01591            {
01592              font_found++;
01593              break;
01594            }
01595          sprintf (fullname, "%s/%s.dfont", dir, name);
01596          if (access (fullname, R_OK) == 0)
01597            {
01598              font_found++;
01599              break;
01600            }
01601        }
01602       gdFree (path);
01603       if (font_found)
01604        break;
01605     }
01606   gdFree (fontlist);
01607   if (!font_found)
01608     {
01609       free (fullname);
01610       return "Could not find/open font";
01611     }
01612 
01613   *fontpath = fullname;
01614   return NULL;
01615 }
01616 
01617 BGD_DECLARE(int) gdFTUseFontConfig(int flag)
01618 {
01619 #ifdef HAVE_LIBFONTCONFIG
01620        fontConfigFlag = 1;
01621        return 1;
01622 #else
01623        return 0;
01624 #endif /* HAVE_LIBFONTCONFIG */
01625 }
01626