Back to index

php5  5.3.10
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 #include <stdio.h>
00009 #include <stdlib.h>
00010 #include <string.h>
00011 #include <math.h>
00012 #include "gd.h"
00013 #include "gdhelpers.h"
00014 
00015 #ifndef MSWIN32
00016 #include <unistd.h>
00017 #else
00018 #include <io.h>
00019 #ifndef R_OK
00020 # define R_OK 04                   /* Needed in Windows */
00021 #endif
00022 #endif
00023 
00024 #ifdef WIN32
00025 #define access _access
00026 #ifndef R_OK
00027 #define R_OK 2
00028 #endif
00029 #endif
00030 
00031 /* number of antialised colors for indexed bitmaps */
00032 /* overwrite Windows GDI define in case of windows build */
00033 #ifdef NUMCOLORS
00034 #undef NUMCOLORS
00035 #endif
00036 #define NUMCOLORS 8
00037 
00038 char *
00039 gdImageStringTTF (gdImage * im, int *brect, int fg, char *fontlist,
00040                 double ptsize, double angle, int x, int y, char *string)
00041 {
00042        /* 2.0.6: valid return */
00043        return gdImageStringFT (im, brect, fg, fontlist, ptsize, angle, x, y, string);
00044 }
00045 
00046 #ifndef HAVE_LIBFREETYPE
00047 char *
00048 gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist,
00049                double ptsize, double angle, int x, int y, char *string,
00050                gdFTStringExtraPtr strex)
00051 {
00052        return "libgd was not built with FreeType font support\n";
00053 }
00054 
00055 char *
00056 gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
00057                double ptsize, double angle, int x, int y, char *string)
00058 {
00059        return "libgd was not built with FreeType font support\n";
00060 }
00061 #else
00062 
00063 #include "gdcache.h"
00064 #include <ft2build.h>
00065 #include FT_FREETYPE_H
00066 #include FT_GLYPH_H
00067 
00068 /* number of fonts cached before least recently used is replaced */
00069 #define FONTCACHESIZE 6
00070 
00071 /* number of antialias color lookups cached */
00072 #define TWEENCOLORCACHESIZE 32
00073 
00074 /*
00075  * Line separation as a factor of font height.
00076  *      No space between if LINESPACE = 1.00
00077  *      Line separation will be rounded up to next pixel row.
00078  */
00079 #define LINESPACE 1.05
00080 
00081 /*
00082  * The character (space) used to separate alternate fonts in the
00083  * fontlist parameter to gdImageStringFT. 2.0.18: space was a oor choice for this.
00084  */
00085 #define LISTSEPARATOR ";"
00086 
00087 /*
00088  * DEFAULT_FONTPATH and PATHSEPARATOR are host type dependent and
00089  * are normally set by configure in config.h.  These are just
00090  * some last resort values that might match some Un*x system
00091  * if building this version of gd separate from graphviz.
00092  */
00093 #ifndef DEFAULT_FONTPATH
00094 #if defined(__APPLE__) || (defined(__MWERKS__) && defined(macintosh))
00095 #define DEFAULT_FONTPATH "/usr/share/fonts/truetype:/System/Library/Fonts:/Library/Fonts"
00096 #else
00097 #define DEFAULT_FONTPATH "/usr/share/fonts/truetype"
00098 #endif
00099 #endif
00100 #ifndef PATHSEPARATOR
00101 #define PATHSEPARATOR ":"
00102 #endif
00103 
00104 #ifndef TRUE
00105 #define FALSE 0
00106 #define TRUE !FALSE
00107 #endif
00108 
00109 #ifndef MAX
00110 #define MAX(a,b) ((a)>(b)?(a):(b))
00111 #endif
00112 
00113 #ifndef MIN
00114 #define MIN(a,b) ((a)<(b)?(a):(b))
00115 #endif
00116 
00117 typedef struct
00118 {
00119        char *fontlist;             /* key */
00120        FT_Library *library;
00121        FT_Face face;
00122        FT_Bool have_char_map_unicode, have_char_map_big5, have_char_map_sjis, have_char_map_apple_roman;
00123        gdCache_head_t *glyphCache;
00124 } font_t;
00125 
00126 typedef struct
00127 {
00128        char *fontlist;             /* key */
00129        FT_Library *library;
00130 } fontkey_t;
00131 
00132 typedef struct
00133 {
00134        int pixel;           /* key */
00135        int bgcolor;         /* key */
00136        int fgcolor;         /* key *//* -ve means no antialias */
00137        gdImagePtr im;              /* key */
00138        int tweencolor;
00139 } tweencolor_t;
00140 
00141 typedef struct
00142 {
00143        int pixel;           /* key */
00144        int bgcolor;         /* key */
00145        int fgcolor;         /* key *//* -ve means no antialias */
00146        gdImagePtr im;              /* key */
00147 } tweencolorkey_t;
00148 
00149 /********************************************************************
00150  * gdTcl_UtfToUniChar is borrowed from Tcl ...
00151  */
00152 /*
00153  * tclUtf.c --
00154  *
00155  *      Routines for manipulating UTF-8 strings.
00156  *
00157  * Copyright (c) 1997-1998 Sun Microsystems, Inc.
00158  *
00159  * See the file "license.terms" for information on usage and redistribution
00160  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
00161  *
00162  * SCCS: @(#) tclUtf.c 1.25 98/01/28 18:02:43
00163  */
00164 
00165 /*
00166  *---------------------------------------------------------------------------
00167  *
00168  * gdTcl_UtfToUniChar --
00169  *
00170  *      Extract the Tcl_UniChar represented by the UTF-8 string.  Bad
00171  *      UTF-8 sequences are converted to valid Tcl_UniChars and processing
00172  *      continues.  Equivalent to Plan 9 chartorune().
00173  *
00174  *      The caller must ensure that the source buffer is long enough that
00175  *      this routine does not run off the end and dereference non-existent
00176  *      memory looking for trail bytes.  If the source buffer is known to
00177  *      be '\0' terminated, this cannot happen.  Otherwise, the caller
00178  *      should call Tcl_UtfCharComplete() before calling this routine to
00179  *      ensure that enough bytes remain in the string.
00180  *
00181  * Results:
00182  *      *chPtr is filled with the Tcl_UniChar, and the return value is the
00183  *      number of bytes from the UTF-8 string that were consumed.
00184  *
00185  * Side effects:
00186  *      None.
00187  *
00188  *---------------------------------------------------------------------------
00189  */
00190 
00191 #ifdef JISX0208
00192 #include "jisx0208.h"
00193 #endif
00194 
00195 extern int any2eucjp (char *, char *, unsigned int);
00196 
00197 /* Persistent font cache until explicitly cleared */
00198 /* Fonts can be used across multiple images */
00199 
00200 /* 2.0.16: thread safety (the font cache is shared) */
00201 gdMutexDeclare(gdFontCacheMutex);
00202 static gdCache_head_t *fontCache = NULL;
00203 static FT_Library library;
00204 
00205 #define Tcl_UniChar int
00206 #define TCL_UTF_MAX 3
00207 static int gdTcl_UtfToUniChar (char *str, Tcl_UniChar * chPtr)
00208 /* str is the UTF8 next character pointer */
00209 /* chPtr is the int for the result */
00210 {
00211        int byte;
00212 
00213        /* HTML4.0 entities in decimal form, e.g. &#197; */
00214        byte = *((unsigned char *) str);
00215        if (byte == '&') {
00216               int i, n = 0;
00217 
00218               byte = *((unsigned char *) (str + 1));
00219               if (byte == '#') {
00220                      byte = *((unsigned char *) (str + 2));
00221                      if (byte == 'x' || byte == 'X') {
00222                             for (i = 3; i < 8; i++) {
00223                                    byte = *((unsigned char *) (str + i));
00224                                    if (byte >= 'A' && byte <= 'F')
00225                                           byte = byte - 'A' + 10;
00226                                    else if (byte >= 'a' && byte <= 'f')
00227                                           byte = byte - 'a' + 10;
00228                                    else if (byte >= '0' && byte <= '9')
00229                                           byte = byte - '0';
00230                                    else
00231                                           break;
00232                                    n = (n * 16) + byte;
00233                             }
00234                      } else {
00235                             for (i = 2; i < 8; i++) {
00236                                    byte = *((unsigned char *) (str + i));
00237                                    if (byte >= '0' && byte <= '9') {
00238                                           n = (n * 10) + (byte - '0');
00239                                    } else {
00240                                           break;
00241                                    }
00242                             }
00243                      }
00244                      if (byte == ';') {
00245                             *chPtr = (Tcl_UniChar) n;
00246                             return ++i;
00247                      }
00248               }
00249        }
00250 
00251        /* Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones. */
00252 
00253        byte = *((unsigned char *) str);
00254 #ifdef JISX0208
00255        if (0xA1 <= byte && byte <= 0xFE) {
00256               int ku, ten;
00257 
00258               ku = (byte & 0x7F) - 0x20;
00259               ten = (str[1] & 0x7F) - 0x20;
00260               if ((ku < 1 || ku > 92) || (ten < 1 || ten > 94)) {
00261                      *chPtr = (Tcl_UniChar) byte;
00262                      return 1;
00263               }
00264 
00265               *chPtr = (Tcl_UniChar) UnicodeTbl[ku - 1][ten - 1];
00266               return 2;
00267        } else
00268 #endif /* JISX0208 */
00269        if (byte < 0xC0) {
00270               /* Handles properly formed UTF-8 characters between
00271                * 0x01 and 0x7F.  Also treats \0 and naked trail
00272                * bytes 0x80 to 0xBF as valid characters representing
00273                * themselves.
00274                */
00275 
00276               *chPtr = (Tcl_UniChar) byte;
00277               return 1;
00278        } else if (byte < 0xE0) {
00279               if ((str[1] & 0xC0) == 0x80) {
00280                      /* Two-byte-character lead-byte followed by a trail-byte. */
00281 
00282                      *chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6) | (str[1] & 0x3F));
00283                      return 2;
00284               }
00285               /*
00286                * A two-byte-character lead-byte not followed by trail-byte
00287                * represents itself.
00288                */
00289 
00290               *chPtr = (Tcl_UniChar) byte;
00291               return 1;
00292        } else if (byte < 0xF0) {
00293               if (((str[1] & 0xC0) == 0x80) && ((str[2] & 0xC0) == 0x80)) {
00294                      /* Three-byte-character lead byte followed by two trail bytes. */
00295 
00296                      *chPtr = (Tcl_UniChar) (((byte & 0x0F) << 12) | ((str[1] & 0x3F) << 6) | (str[2] & 0x3F));
00297                      return 3;
00298               }
00299               /* A three-byte-character lead-byte not followed by two trail-bytes represents itself. */
00300 
00301               *chPtr = (Tcl_UniChar) byte;
00302               return 1;
00303        }
00304 #if TCL_UTF_MAX > 3
00305        else {
00306               int ch, total, trail;
00307 
00308               total = totalBytes[byte];
00309               trail = total - 1;
00310 
00311               if (trail > 0) {
00312                      ch = byte & (0x3F >> trail);
00313                      do {
00314                             str++;
00315                             if ((*str & 0xC0) != 0x80) {
00316                                    *chPtr = byte;
00317                                    return 1;
00318                             }
00319                             ch <<= 6;
00320                             ch |= (*str & 0x3F);
00321                             trail--;
00322                      } while (trail > 0);
00323                      *chPtr = ch;
00324                      return total;
00325               }
00326        }
00327 #endif
00328 
00329        *chPtr = (Tcl_UniChar) byte;
00330        return 1;
00331 }
00332 
00333 /********************************************************************/
00334 /* font cache functions                                             */
00335 
00336 static int fontTest (void *element, void *key)
00337 {
00338        font_t *a = (font_t *) element;
00339        fontkey_t *b = (fontkey_t *) key;
00340 
00341        return (strcmp (a->fontlist, b->fontlist) == 0);
00342 }
00343 
00344 static void *fontFetch (char **error, void *key)
00345 {
00346        font_t *a;
00347        fontkey_t *b = (fontkey_t *) key;
00348        int n;
00349        int font_found = 0;
00350        unsigned short platform, encoding;
00351        char *fontsearchpath, *fontlist;
00352        char fullname[MAXPATHLEN], cur_dir[MAXPATHLEN];
00353        char *name, *path=NULL, *dir;
00354        char *strtok_ptr;
00355        FT_Error err;
00356        FT_CharMap found = 0;
00357        FT_CharMap charmap;
00358 
00359        a = (font_t *) gdPMalloc(sizeof(font_t));
00360        a->fontlist = gdPEstrdup(b->fontlist);
00361        a->library = b->library;
00362 
00363        /*
00364         * Search the pathlist for any of a list of font names.
00365         */
00366        fontsearchpath = getenv ("GDFONTPATH");
00367        if (!fontsearchpath) {
00368               fontsearchpath = DEFAULT_FONTPATH;
00369        }
00370        fontlist = gdEstrdup(a->fontlist);
00371 
00372        /*
00373         * Must use gd_strtok_r else pointer corrupted by strtok in nested loop.
00374         */
00375        for (name = gd_strtok_r (fontlist, LISTSEPARATOR, &strtok_ptr); name; name = gd_strtok_r (0, LISTSEPARATOR, &strtok_ptr)) {
00376               /* make a fresh copy each time - strtok corrupts it. */
00377               path = gdEstrdup (fontsearchpath);
00378 
00379               /* if name is an absolute filename then test directly */
00380 #ifdef NETWARE
00381               if (*name == '/' || (name[0] != 0 && strstr(name, ":/"))) {
00382 #else
00383               if (*name == '/' || (name[0] != 0 && name[1] == ':' && (name[2] == '/' || name[2] == '\\'))) {
00384 #endif
00385                      snprintf(fullname, sizeof(fullname) - 1, "%s", name);
00386                      if (access(fullname, R_OK) == 0) {
00387                             font_found++;
00388                             break;
00389                      }
00390               }
00391               for (dir = strtok (path, PATHSEPARATOR); dir; dir = strtok (0, PATHSEPARATOR)) {
00392                      if (!strcmp(dir, ".")) {
00393                             TSRMLS_FETCH();
00394 #if HAVE_GETCWD
00395                             dir = VCWD_GETCWD(cur_dir, MAXPATHLEN);
00396 #elif HAVE_GETWD
00397                             dir = VCWD_GETWD(cur_dir);
00398 #endif
00399                             if (!dir) {
00400                                    continue;
00401                             }
00402                      }
00403 
00404 #define GD_CHECK_FONT_PATH(ext)    \
00405        snprintf(fullname, sizeof(fullname) - 1, "%s/%s%s", dir, name, ext);  \
00406        if (access(fullname, R_OK) == 0) { \
00407               font_found++; \
00408               break; \
00409        }      \
00410 
00411                      GD_CHECK_FONT_PATH("");
00412                      GD_CHECK_FONT_PATH(".ttf");
00413                      GD_CHECK_FONT_PATH(".pfa");
00414                      GD_CHECK_FONT_PATH(".pfb");
00415                      GD_CHECK_FONT_PATH(".dfont");
00416               }
00417               gdFree(path);
00418               path = NULL;
00419               if (font_found) {
00420                      break;
00421               }
00422        }
00423 
00424        if (path) {
00425               gdFree(path);
00426        }
00427 
00428        gdFree(fontlist);
00429 
00430        if (!font_found) {
00431               gdPFree(a->fontlist);
00432               gdPFree(a);
00433               *error = "Could not find/open font";
00434               return NULL;
00435        }
00436 
00437        err = FT_New_Face (*b->library, fullname, 0, &a->face);
00438        if (err) {
00439               gdPFree(a->fontlist);
00440               gdPFree(a);
00441               *error = "Could not read font";
00442               return NULL;
00443        }
00444 
00445        /* FIXME - This mapping stuff is imcomplete - where is the spec? */
00446        /* EAM   - It's worse than that. It's pointless to match character encodings here.
00447         *         As currently written, the stored a->face->charmap only matches one of
00448         *         the actual charmaps and we cannot know at this stage if it is the right
00449         *         one. We should just skip all this stuff, and check in gdImageStringFTEx
00450         *         if some particular charmap is preferred and if so whether it is held in
00451         *         one of the a->face->charmaps[0..num_charmaps].
00452         *         And why is it so bad not to find any recognized charmap?  The user may
00453         *         still know what mapping to use, even if we do not.  In that case we can
00454         *         just use the map in a->face->charmaps[num_charmaps] and be done with it.
00455         */
00456 
00457        a->have_char_map_unicode = 0;
00458        a->have_char_map_big5 = 0;
00459        a->have_char_map_sjis = 0;
00460        a->have_char_map_apple_roman = 0;
00461        for (n = 0; n < a->face->num_charmaps; n++) {
00462               charmap = a->face->charmaps[n];
00463               platform = charmap->platform_id;
00464               encoding = charmap->encoding_id;
00465 
00466 /* EAM DEBUG - Newer versions of libfree2 make it easier by defining encodings */
00467 #if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2)))
00468        if (charmap->encoding == FT_ENCODING_MS_SYMBOL
00469               || charmap->encoding == FT_ENCODING_ADOBE_CUSTOM
00470               || charmap->encoding == FT_ENCODING_ADOBE_STANDARD) {
00471               a->have_char_map_unicode = 1;
00472               found = charmap;
00473               a->face->charmap = charmap;
00474               return (void *)a;
00475        }
00476 #endif /* Freetype 2.1.3 or better */
00477 /* EAM DEBUG */
00478 
00479               if ((platform == 3 && encoding == 1)             /* Windows Unicode */
00480                      || (platform == 3 && encoding == 0)       /* Windows Symbol */
00481                      || (platform == 2 && encoding == 1)       /* ISO Unicode */
00482                      || (platform == 0))
00483               {                                         /* Apple Unicode */
00484                      a->have_char_map_unicode = 1;
00485                      found = charmap;
00486               } else if (platform == 3 && encoding == 4) {     /* Windows Big5 */
00487                      a->have_char_map_big5 = 1;
00488                      found = charmap;
00489               } else if (platform == 3 && encoding == 2) {     /* Windows Sjis */
00490                      a->have_char_map_sjis = 1;
00491                      found = charmap;
00492               } else if ((platform == 1 && encoding == 0)      /* Apple Roman */
00493                      || (platform == 2 && encoding == 0))
00494               {                                         /* ISO ASCII */
00495                      a->have_char_map_apple_roman = 1;
00496                      found = charmap;
00497               }
00498        }
00499        if (!found) {
00500               gdPFree(a->fontlist);
00501               gdPFree(a);
00502               *error = "Unable to find a CharMap that I can handle";
00503               return NULL;
00504        }
00505 
00506        /* 2.0.5: we should actually return this */
00507        a->face->charmap = found;
00508        return (void *) a;
00509 }
00510 
00511 static void fontRelease (void *element)
00512 {
00513        font_t *a = (font_t *) element;
00514 
00515        FT_Done_Face (a->face);
00516        gdPFree(a->fontlist);
00517        gdPFree((char *) element);
00518 }
00519 
00520 /********************************************************************/
00521 /* tweencolor cache functions                                            */
00522 
00523 static int tweenColorTest (void *element, void *key)
00524 {
00525        tweencolor_t *a = (tweencolor_t *) element;
00526        tweencolorkey_t *b = (tweencolorkey_t *) key;
00527 
00528        return (a->pixel == b->pixel && a->bgcolor == b->bgcolor && a->fgcolor == b->fgcolor && a->im == b->im);
00529 }
00530 
00531 /*
00532  * Computes a color in im's color table that is part way between
00533  * the background and foreground colors proportional to the gray
00534  * pixel value in the range 0-NUMCOLORS. The fg and bg colors must already
00535  * be in the color table for palette images. For truecolor images the
00536  * returned value simply has an alpha component and gdImageAlphaBlend
00537  * does the work so that text can be alpha blended across a complex
00538  * background (TBB; and for real in 2.0.2).
00539  */
00540 static void * tweenColorFetch (char **error, void *key)
00541 {
00542        tweencolor_t *a;
00543        tweencolorkey_t *b = (tweencolorkey_t *) key;
00544        int pixel, npixel, bg, fg;
00545        gdImagePtr im;
00546 
00547        a = (tweencolor_t *) gdMalloc (sizeof (tweencolor_t));
00548        pixel = a->pixel = b->pixel;
00549        bg = a->bgcolor = b->bgcolor;
00550        fg = a->fgcolor = b->fgcolor;
00551        im = a->im = b->im;
00552 
00553        /* if fg is specified by a negative color idx, then don't antialias */
00554        if (fg < 0) {
00555               if ((pixel + pixel) >= NUMCOLORS) {
00556                      a->tweencolor = -fg;
00557               } else {
00558                      a->tweencolor = bg;
00559               }
00560        } else {
00561               npixel = NUMCOLORS - pixel;
00562               if (im->trueColor) {
00563                      /* 2.0.1: use gdImageSetPixel to do the alpha blending work,
00564                       * or to just store the alpha level. All we have to do here
00565                       * is incorporate our knowledge of the percentage of this
00566                       * pixel that is really "lit" by pushing the alpha value
00567                       * up toward transparency in edge regions.
00568                       */
00569                      a->tweencolor = gdTrueColorAlpha(
00570                                           gdTrueColorGetRed(fg),
00571                                           gdTrueColorGetGreen(fg),
00572                                           gdTrueColorGetBlue(fg),
00573                                           gdAlphaMax - (gdTrueColorGetAlpha (fg) * pixel / NUMCOLORS));
00574               } else {
00575                      a->tweencolor = gdImageColorResolve(im,
00576                                           (pixel * im->red[fg] + npixel * im->red[bg]) / NUMCOLORS,
00577                                           (pixel * im->green[fg] + npixel * im->green[bg]) / NUMCOLORS,
00578                                           (pixel * im->blue[fg] + npixel * im->blue[bg]) / NUMCOLORS);
00579               }
00580        }
00581        return (void *) a;
00582 }
00583 
00584 static void tweenColorRelease (void *element)
00585 {
00586        gdFree((char *) element);
00587 }
00588 
00589 /* draw_bitmap - transfers glyph bitmap to GD image */
00590 static char * gdft_draw_bitmap (gdCache_head_t *tc_cache, gdImage * im, int fg, FT_Bitmap bitmap, int pen_x, int pen_y)
00591 {
00592        unsigned char *pixel = NULL;
00593        int *tpixel = NULL;
00594        int x, y, row, col, pc, pcr;
00595 
00596        tweencolor_t *tc_elem;
00597        tweencolorkey_t tc_key;
00598 
00599        /* copy to image, mapping colors */
00600        tc_key.fgcolor = fg;
00601        tc_key.im = im;
00602        /* Truecolor version; does not require the cache */
00603        if (im->trueColor) {
00604               for (row = 0; row < bitmap.rows; row++) {
00605                      pc = row * bitmap.pitch;
00606                      pcr = pc;
00607                      y = pen_y + row;
00608                      /* clip if out of bounds */
00609                      /* 2.0.16: clipping rectangle, not image bounds */
00610                      if ((y > im->cy2) || (y < im->cy1)) {
00611                             continue;
00612                      }
00613                      for (col = 0; col < bitmap.width; col++, pc++) {
00614                             int level;
00615                             if (bitmap.pixel_mode == ft_pixel_mode_grays) {
00616                                    /* Scale to 128 levels of alpha for gd use.
00617                                     * alpha 0 is opacity, so be sure to invert at the end
00618                                     */
00619                                    level = (bitmap.buffer[pc] * gdAlphaMax / (bitmap.num_grays - 1));
00620                             } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
00621                                    /* 2.0.5: mode_mono fix from Giuliano Pochini */
00622                                    level = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? gdAlphaTransparent : gdAlphaOpaque;
00623                             } else {
00624                                    return "Unsupported ft_pixel_mode";
00625                             }
00626                             if ((fg >= 0) && (im->trueColor)) {
00627                                    /* Consider alpha in the foreground color itself to be an
00628                                     * upper bound on how opaque things get, when truecolor is
00629                                     * available. Without truecolor this results in far too many
00630                                     * color indexes.
00631                                     */
00632                                    level = level * (gdAlphaMax - gdTrueColorGetAlpha(fg)) / gdAlphaMax;
00633                             }
00634                             level = gdAlphaMax - level;
00635                             x = pen_x + col;
00636                             /* clip if out of bounds */
00637                             /* 2.0.16: clip to clipping rectangle, Matt McNabb */
00638                             if ((x > im->cx2) || (x < im->cx1)) {
00639                                    continue;
00640                             }
00641                             /* get pixel location in gd buffer */
00642                             tpixel = &im->tpixels[y][x];
00643                             if (fg < 0) {
00644                                    if (level < (gdAlphaMax / 2)) {
00645                                           *tpixel = -fg;
00646                                    }
00647                             } else {
00648                                    if (im->alphaBlendingFlag) {
00649                                           *tpixel = gdAlphaBlend(*tpixel, (level << 24) + (fg & 0xFFFFFF));
00650                                    } else {
00651                                           *tpixel = (level << 24) + (fg & 0xFFFFFF);
00652                                    }
00653                             }
00654                      }
00655               }
00656               return (char *) NULL;
00657        }
00658        /* Non-truecolor case, restored to its more or less original form */
00659        for (row = 0; row < bitmap.rows; row++) {
00660               int pcr;
00661               pc = row * bitmap.pitch;
00662               pcr = pc;
00663               if (bitmap.pixel_mode==ft_pixel_mode_mono) {
00664                      pc *= 8;    /* pc is measured in bits for monochrome images */
00665               }
00666               y = pen_y + row;
00667 
00668               /* clip if out of bounds */
00669               if (y >= im->sy || y < 0) {
00670                      continue;
00671               }
00672 
00673               for (col = 0; col < bitmap.width; col++, pc++) {
00674                      if (bitmap.pixel_mode == ft_pixel_mode_grays) {
00675                             /*
00676                              * Round to NUMCOLORS levels of antialiasing for
00677                              * index color images since only 256 colors are
00678                              * available.
00679                              */
00680                             tc_key.pixel = ((bitmap.buffer[pc] * NUMCOLORS) + bitmap.num_grays / 2) / (bitmap.num_grays - 1);
00681                      } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
00682                             tc_key.pixel = ((bitmap.buffer[pc / 8] << (pc % 8)) & 128) ? NUMCOLORS : 0;
00683                             /* 2.0.5: mode_mono fix from Giuliano Pochini */
00684                             tc_key.pixel = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? NUMCOLORS : 0;
00685                      } else {
00686                             return "Unsupported ft_pixel_mode";
00687                      }
00688                      if (tc_key.pixel > 0) { /* if not background */
00689                             x = pen_x + col;
00690 
00691                             /* clip if out of bounds */
00692                             if (x >= im->sx || x < 0) {
00693                                    continue;
00694                             }
00695                             /* get pixel location in gd buffer */
00696                             pixel = &im->pixels[y][x];
00697                             if (tc_key.pixel == NUMCOLORS) {
00698                                    /* use fg color directly. gd 2.0.2: watch out for
00699                                     * negative indexes (thanks to David Marwood).
00700                                     */
00701                                    *pixel = (fg < 0) ? -fg : fg;
00702                             } else {
00703                                    /* find antialised color */
00704                                    tc_key.bgcolor = *pixel;
00705                                    tc_elem = (tweencolor_t *) gdCacheGet(tc_cache, &tc_key);
00706                                    *pixel = tc_elem->tweencolor;
00707                             }
00708                      }
00709               }
00710        }
00711        return (char *) NULL;
00712 }
00713 
00714 static int
00715 gdroundupdown (FT_F26Dot6 v1, int updown)
00716 {
00717        return (!updown) ? (v1 < 0 ? ((v1 - 63) >> 6) : v1 >> 6) : (v1 > 0 ? ((v1 + 63) >> 6) : v1 >> 6);
00718 }
00719 
00720 void gdFontCacheShutdown()
00721 {
00722        gdMutexLock(gdFontCacheMutex);
00723 
00724        if (fontCache) {
00725               gdCacheDelete(fontCache);
00726               fontCache = NULL;
00727               FT_Done_FreeType(library);
00728        }
00729 
00730        gdMutexUnlock(gdFontCacheMutex);
00731 }
00732 
00733 void gdFreeFontCache()
00734 {
00735        gdFontCacheShutdown();
00736 }
00737 
00738 void gdFontCacheMutexSetup()
00739 {
00740        gdMutexSetup(gdFontCacheMutex);
00741 }
00742 
00743 void gdFontCacheMutexShutdown()
00744 {
00745        gdMutexShutdown(gdFontCacheMutex);
00746 }
00747 
00748 int gdFontCacheSetup(void)
00749 {
00750        if (fontCache) {
00751               /* Already set up */
00752               return 0;
00753        }
00754        if (FT_Init_FreeType(&library)) {
00755               return -1;
00756        }
00757        fontCache = gdCacheCreate (FONTCACHESIZE, fontTest, fontFetch, fontRelease);
00758        return 0;
00759 }
00760 
00761 
00762 /********************************************************************/
00763 /* gdImageStringFT -  render a utf8 string onto a gd image          */
00764 
00765 char *
00766 gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
00767                double ptsize, double angle, int x, int y, char *string)
00768 {
00769        return gdImageStringFTEx(im, brect, fg, fontlist, ptsize, angle, x, y, string, 0);
00770 }
00771 
00772 char *
00773 gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsize, double angle, int x, int y, char *string, gdFTStringExtraPtr strex)
00774 {
00775        FT_BBox bbox, glyph_bbox;
00776        FT_Matrix matrix;
00777        FT_Vector pen, delta, penf;
00778        FT_Face face;
00779        FT_Glyph image;
00780        FT_GlyphSlot slot;
00781        FT_Bool use_kerning;
00782        FT_UInt glyph_index, previous;
00783        double sin_a = sin (angle);
00784        double cos_a = cos (angle);
00785        int len, i = 0, ch;
00786        int x1 = 0, y1 = 0;
00787        int xb = x, yb = y;
00788        int yd = 0;
00789        font_t *font;
00790        fontkey_t fontkey;
00791        char *next;
00792        char *tmpstr = NULL;
00793        int render = (im && (im->trueColor || (fg <= 255 && fg >= -255)));
00794        FT_BitmapGlyph bm;
00795        /* 2.0.13: Bob Ostermann: don't force autohint, that's just for testing freetype and doesn't look as good */
00796        int render_mode = FT_LOAD_DEFAULT;
00797        int m, mfound;
00798        /* Now tuneable thanks to Wez Furlong */
00799        double linespace = LINESPACE;
00800        /* 2.0.6: put this declaration with the other declarations! */
00801        /*
00802        *   make a new tweenColorCache on every call
00803        *   because caching colormappings between calls
00804        *   is not safe. If the im-pointer points to a
00805        *   brand new image, the cache gives out bogus
00806        *   colorindexes.          -- 27.06.2001 <krisku@arrak.fi>
00807        */
00808        gdCache_head_t  *tc_cache;
00809        /* Tuneable horizontal and vertical resolution in dots per inch */
00810        int hdpi, vdpi;
00811 
00812        if (strex && ((strex->flags & gdFTEX_LINESPACE) == gdFTEX_LINESPACE)) {
00813               linespace = strex->linespacing;
00814        }
00815        tc_cache = gdCacheCreate(TWEENCOLORCACHESIZE, tweenColorTest, tweenColorFetch, tweenColorRelease);
00816 
00817        /***** initialize font library and font cache on first call ******/
00818 
00819        gdMutexLock(gdFontCacheMutex);
00820        if (!fontCache) {
00821               if (gdFontCacheSetup() != 0) {
00822                      gdCacheDelete(tc_cache);
00823                      gdMutexUnlock(gdFontCacheMutex);
00824                      return "Failure to initialize font library";
00825               }
00826        }
00827        /*****/
00828 
00829        /* get the font (via font cache) */
00830        fontkey.fontlist = fontlist;
00831        fontkey.library = &library;
00832        font = (font_t *) gdCacheGet (fontCache, &fontkey);
00833        if (!font) {
00834               gdCacheDelete(tc_cache);
00835               gdMutexUnlock(gdFontCacheMutex);
00836               return fontCache->error;
00837        }
00838        face = font->face;          /* shortcut */
00839        slot = face->glyph;         /* shortcut */
00840 
00841        /*
00842         * Added hdpi and vdpi to support images at non-screen resolutions, i.e. 300 dpi TIFF,
00843         * or 100h x 50v dpi FAX format. 2.0.23.
00844         * 2004/02/27 Mark Shackelford, mark.shackelford@acs-inc.com
00845         */
00846        hdpi = GD_RESOLUTION;
00847        vdpi = GD_RESOLUTION;
00848        if (strex && (strex->flags & gdFTEX_RESOLUTION)) {
00849               hdpi = strex->hdpi;
00850               vdpi = strex->vdpi;
00851        }
00852 
00853        if (FT_Set_Char_Size(face, 0, (FT_F26Dot6) (ptsize * 64), hdpi, vdpi)) {
00854               gdCacheDelete(tc_cache);
00855               gdMutexUnlock(gdFontCacheMutex);
00856               return "Could not set character size";
00857        }
00858 
00859        matrix.xx = (FT_Fixed) (cos_a * (1 << 16));
00860        matrix.yx = (FT_Fixed) (sin_a * (1 << 16));
00861        matrix.xy = -matrix.yx;
00862        matrix.yy = matrix.xx;
00863 
00864        penf.x = penf.y = 0;        /* running position of non-rotated string */
00865        pen.x = pen.y = 0;          /* running position of rotated string */
00866        bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
00867 
00868        use_kerning = FT_HAS_KERNING (face);
00869        previous = 0;
00870        if (fg < 0) {
00871               render_mode |= FT_LOAD_MONOCHROME;
00872        }
00873        /* 2.0.12: allow explicit specification of the preferred map;
00874         * but we still fall back if it is not available.
00875         */
00876        m = gdFTEX_Unicode;
00877        if (strex && (strex->flags & gdFTEX_CHARMAP)) {
00878               m = strex->charmap;
00879        }
00880        /* Try all three types of maps, but start with the specified one */
00881        mfound = 0;
00882        for (i = 0; i < 3; i++) {
00883               switch (m) {
00884                      case gdFTEX_Unicode:
00885                             if (font->have_char_map_unicode) {
00886                                    mfound = 1;
00887                             }
00888                             break;
00889                      case gdFTEX_Shift_JIS:
00890                             if (font->have_char_map_sjis) {
00891                                    mfound = 1;
00892                             }
00893                             break;
00894                      case gdFTEX_Big5:
00895                             /* This was the 'else' case, we can't really 'detect' it */
00896                             mfound = 1;
00897                             break;
00898               }
00899               if (mfound) {
00900                      break;
00901               }
00902               m++;
00903               m %= 3;
00904        }
00905        if (!mfound) {
00906               /* No character set found! */
00907               gdMutexUnlock(gdFontCacheMutex);
00908               return "No character set found";
00909        }
00910 
00911 #ifndef JISX0208
00912        if (font->have_char_map_sjis) {
00913 #endif
00914               tmpstr = (char *) gdMalloc(BUFSIZ);
00915               any2eucjp(tmpstr, string, BUFSIZ);
00916               next = tmpstr;
00917 #ifndef JISX0208
00918        } else {
00919               next = string;
00920        }
00921 #endif
00922 
00923        i = 0;
00924        while (*next) {
00925               ch = *next;
00926 
00927               /* carriage returns */
00928               if (ch == '\r') {
00929                      penf.x = 0;
00930                      x1 = (int)(- penf.y * sin_a + 32) / 64;
00931                      y1 = (int)(- penf.y * cos_a + 32) / 64;
00932                      pen.x = pen.y = 0;
00933                      previous = 0;        /* clear kerning flag */
00934                      next++;
00935                      continue;
00936               }
00937               /* newlines */
00938               if (ch == '\n') {
00939                      if (!*(++next)) break;
00940                      /* 2.0.13: reset penf.x. Christopher J. Grayce */
00941                      penf.x = 0;
00942                        penf.y -= (long)(face->size->metrics.height * linespace);
00943                        penf.y = (penf.y - 32) & -64;           /* round to next pixel row */
00944                        x1 = (int)(- penf.y * sin_a + 32) / 64;
00945                        y1 = (int)(- penf.y * cos_a + 32) / 64;
00946                        xb = x + x1;
00947                        yb = y + y1;
00948                        yd = 0;
00949                        pen.x = pen.y = 0;
00950                        previous = 0;             /* clear kerning flag */
00951                        continue;
00952               }
00953 
00954 /* EAM DEBUG */
00955 #if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2)))
00956               if (font->face->family_name && font->face->charmap->encoding &&
00957                      font->face->charmap->encoding == FT_ENCODING_MS_SYMBOL && strcmp(font->face->family_name, "Symbol") == 0) {
00958                      /* I do not know the significance of the constant 0xf000.
00959                       * It was determined by inspection of the character codes
00960                       * stored in Microsoft font symbol.
00961                       * Added by Pierre (pajoye@php.net):
00962                       * Convert to the Symbol glyph range only for a Symbol family member
00963                       */
00964                      len = gdTcl_UtfToUniChar (next, &ch);
00965                      ch |= 0xf000;
00966                      next += len;
00967               } else
00968 #endif /* Freetype 2.1 or better */
00969 /* EAM DEBUG */
00970 
00971               switch (m) {
00972                      case gdFTEX_Unicode:
00973                             if (font->have_char_map_unicode) {
00974                                    /* use UTF-8 mapping from ASCII */
00975                                    len = gdTcl_UtfToUniChar(next, &ch);
00976                                    next += len;
00977                             }
00978                             break;
00979                      case gdFTEX_Shift_JIS:
00980                             if (font->have_char_map_sjis) {
00981                                    unsigned char c;
00982                                    int jiscode;
00983                                    c = *next;
00984                                    if (0xA1 <= c && c <= 0xFE) {
00985                                           next++;
00986                                           jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F);
00987 
00988                                           ch = (jiscode >> 8) & 0xFF;
00989                                           jiscode &= 0xFF;
00990 
00991                                           if (ch & 1) {
00992                                                  jiscode += 0x40 - 0x21;
00993                                           } else {
00994                                                  jiscode += 0x9E - 0x21;
00995                                           }
00996 
00997                                           if (jiscode >= 0x7F) {
00998                                                  jiscode++;
00999                                           }
01000                                           ch = (ch - 0x21) / 2 + 0x81;
01001                                           if (ch >= 0xA0) {
01002                                                  ch += 0x40;
01003                                           }
01004 
01005                                           ch = (ch << 8) + jiscode;
01006                                    } else {
01007                                           ch = c & 0xFF;       /* don't extend sign */
01008                                    }
01009                                    if (*next) next++;
01010                             }
01011                             break;
01012                      case gdFTEX_Big5: {
01013                             /*
01014                              * Big 5 mapping:
01015                              * use "JIS-8 half-width katakana" coding from 8-bit characters. Ref:
01016                              * ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs
01017                              */
01018                             ch = (*next) & 0xFF; /* don't extend sign */
01019                             next++;
01020                             if (ch >= 161 /* first code of JIS-8 pair */
01021                                    && *next) { /* don't advance past '\0' */
01022                                    /* TBB: Fix from Kwok Wah On: & 255 needed */
01023                                    ch = (ch * 256) + ((*next) & 255);
01024                                    next++;
01025                             }
01026                      }
01027                      break;
01028               }
01029 
01030               /* set rotation transform */
01031               FT_Set_Transform(face, &matrix, NULL);
01032               /* Convert character code to glyph index */
01033               glyph_index = FT_Get_Char_Index(face, ch);
01034 
01035               /* retrieve kerning distance and move pen position */
01036               if (use_kerning && previous && glyph_index) {
01037                      FT_Get_Kerning(face, previous, glyph_index, ft_kerning_default, &delta);
01038                      pen.x += delta.x;
01039                      penf.x += delta.x;
01040               }
01041 
01042               /* load glyph image into the slot (erase previous one) */
01043               if (FT_Load_Glyph(face, glyph_index, render_mode)) {
01044                      if (tmpstr) {
01045                             gdFree(tmpstr);
01046                      }
01047                      gdCacheDelete(tc_cache);
01048                      gdMutexUnlock(gdFontCacheMutex);
01049                      return "Problem loading glyph";
01050               }
01051 
01052               /* transform glyph image */
01053               FT_Get_Glyph(slot, &image);
01054               if (brect) { /* only if need brect */
01055                      FT_Glyph_Get_CBox(image, ft_glyph_bbox_gridfit, &glyph_bbox);
01056                      glyph_bbox.xMin += penf.x;
01057                      glyph_bbox.yMin += penf.y;
01058                      glyph_bbox.xMax += penf.x;
01059                      glyph_bbox.yMax += penf.y;
01060                      if (ch == ' ') { /* special case for trailing space */
01061                             glyph_bbox.xMax += slot->metrics.horiAdvance;
01062                      }
01063                      if (!i) { /* if first character, init BB corner values */
01064                             yd = slot->metrics.height - slot->metrics.horiBearingY;
01065                             bbox.xMin = glyph_bbox.xMin;
01066                             bbox.yMin = glyph_bbox.yMin;
01067                             bbox.xMax = glyph_bbox.xMax;
01068                             bbox.yMax = glyph_bbox.yMax;
01069                      } else {
01070                             FT_Pos desc;
01071 
01072                             if ( (desc = (slot->metrics.height - slot->metrics.horiBearingY)) > yd) {
01073                                    yd = desc;
01074                             }
01075                             if (bbox.xMin > glyph_bbox.xMin) {
01076                                    bbox.xMin = glyph_bbox.xMin;
01077                             }
01078                             if (bbox.yMin > glyph_bbox.yMin) {
01079                                    bbox.yMin = glyph_bbox.yMin;
01080                             }
01081                             if (bbox.xMax < glyph_bbox.xMax) {
01082                                    bbox.xMax = glyph_bbox.xMax;
01083                             }
01084                             if (bbox.yMax < glyph_bbox.yMax) {
01085                                    bbox.yMax = glyph_bbox.yMax;
01086                             }
01087                      }
01088                      i++;
01089               }
01090 
01091               if (render) {
01092                      if (image->format != ft_glyph_format_bitmap && FT_Glyph_To_Bitmap(&image, ft_render_mode_normal, 0, 1)) {
01093                             FT_Done_Glyph(image);
01094                             if (tmpstr) {
01095                                    gdFree(tmpstr);
01096                             }
01097                             gdCacheDelete(tc_cache);
01098                             gdMutexUnlock(gdFontCacheMutex);
01099                             return "Problem rendering glyph";
01100                      }
01101 
01102                      /* now, draw to our target surface */
01103                      bm = (FT_BitmapGlyph) image;
01104                      gdft_draw_bitmap(tc_cache, im, fg, bm->bitmap, x + x1 + ((pen.x + 31) >> 6) + bm->left, y + y1 + ((pen.y + 31) >> 6) - bm->top);
01105               }
01106 
01107               /* record current glyph index for kerning */
01108               previous = glyph_index;
01109 
01110               /* increment pen position */
01111               pen.x += image->advance.x >> 10;
01112               pen.y -= image->advance.y >> 10;
01113 
01114               penf.x += slot->metrics.horiAdvance;
01115 
01116               FT_Done_Glyph(image);
01117        }
01118 
01119        if (brect) { /* only if need brect */
01120               /* For perfect rounding, must get sin(a + pi/4) and sin(a - pi/4). */
01121               double d1 = sin (angle + 0.78539816339744830962);
01122               double d2 = sin (angle - 0.78539816339744830962);
01123 
01124               /* make the center of rotation at (0, 0) */
01125               FT_BBox normbox;
01126 
01127               normbox.xMin = 0;
01128               normbox.yMin = 0;
01129               normbox.xMax = bbox.xMax - bbox.xMin;
01130               normbox.yMax = bbox.yMax - bbox.yMin;
01131 
01132               brect[0] = brect[2] = brect[4] = brect[6] = (int)  (yd * sin_a);
01133               brect[1] = brect[3] = brect[5] = brect[7] = (int)(- yd * cos_a);
01134 
01135               /* rotate bounding rectangle */
01136               brect[0] += (int) (normbox.xMin * cos_a - normbox.yMin * sin_a);
01137               brect[1] += (int) (normbox.xMin * sin_a + normbox.yMin * cos_a);
01138               brect[2] += (int) (normbox.xMax * cos_a - normbox.yMin * sin_a);
01139               brect[3] += (int) (normbox.xMax * sin_a + normbox.yMin * cos_a);
01140               brect[4] += (int) (normbox.xMax * cos_a - normbox.yMax * sin_a);
01141               brect[5] += (int) (normbox.xMax * sin_a + normbox.yMax * cos_a);
01142               brect[6] += (int) (normbox.xMin * cos_a - normbox.yMax * sin_a);
01143               brect[7] += (int) (normbox.xMin * sin_a + normbox.yMax * cos_a);
01144 
01145               /* scale, round and offset brect */
01146               brect[0] = xb + gdroundupdown(brect[0], d2 > 0);
01147               brect[1] = yb - gdroundupdown(brect[1], d1 < 0);
01148               brect[2] = xb + gdroundupdown(brect[2], d1 > 0);
01149               brect[3] = yb - gdroundupdown(brect[3], d2 > 0);
01150               brect[4] = xb + gdroundupdown(brect[4], d2 < 0);
01151               brect[5] = yb - gdroundupdown(brect[5], d1 > 0);
01152               brect[6] = xb + gdroundupdown(brect[6], d1 < 0);
01153               brect[7] = yb - gdroundupdown(brect[7], d2 < 0);
01154        }
01155 
01156        if (tmpstr) {
01157               gdFree(tmpstr);
01158        }
01159        gdCacheDelete(tc_cache);
01160        gdMutexUnlock(gdFontCacheMutex);
01161        return (char *) NULL;
01162 }
01163 
01164 #endif /* HAVE_LIBFREETYPE */