~ubuntu-branches/ubuntu/lucid/graphviz/lucid-security

« back to all changes in this revision

Viewing changes to gd/gdft.c

  • Committer: Bazaar Package Importer
  • Author(s): Stephen M Moraco
  • Date: 2002-02-05 18:52:12 UTC
  • Revision ID: james.westby@ubuntu.com-20020205185212-8i04c70te00rc40y
Tags: upstream-1.7.16
ImportĀ upstreamĀ versionĀ 1.7.16

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/********************************************/
 
3
/* gd interface to freetype library         */
 
4
/*                                          */
 
5
/* John Ellson   ellson@lucent.com          */
 
6
/********************************************/
 
7
 
 
8
#include <stdio.h>
 
9
#include <stdlib.h>
 
10
#include <string.h>
 
11
#include <math.h>
 
12
#include "gd.h"
 
13
#include "gdhelpers.h"
 
14
 
 
15
#ifndef MSWIN32
 
16
#include <unistd.h>
 
17
#else
 
18
#define R_OK 4
 
19
#endif
 
20
 
 
21
/* number of antialised colors for indexed bitmaps */
 
22
#define NUMCOLORS 8
 
23
 
 
24
#ifndef HAVE_LIBFREETYPE
 
25
char *
 
26
gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
 
27
                 double ptsize, double angle, int x, int y, char *string)
 
28
{
 
29
#ifdef HAVE_LIBTTF
 
30
  return gdImageStringTTF(im,brect,fg,fontlist,ptsize,angle,x,y,string);
 
31
#else
 
32
  return "libgd was not built with FreeType font support\n";
 
33
#endif
 
34
}
 
35
#else
 
36
 
 
37
#include "gdcache.h"
 
38
#include "freetype/freetype.h"
 
39
#include "freetype/ftglyph.h"
 
40
 
 
41
/* number of fonts cached before least recently used is replaced */
 
42
#define FONTCACHESIZE 6
 
43
 
 
44
/* number of antialias color lookups cached */
 
45
#define TWEENCOLORCACHESIZE 32
 
46
 
 
47
/*
 
48
 * Line separation as a factor of font height.  
 
49
 *      No space between if LINESPACE = 1.00 
 
50
 *      Line separation will be rounded up to next pixel row.
 
51
 */
 
52
#define LINESPACE 1.05
 
53
 
 
54
/*
 
55
 * The character (space) used to separate alternate fonts in the
 
56
 * fontlist parameter to gdImageStringFT.
 
57
 */
 
58
#define LISTSEPARATOR " "
 
59
 
 
60
/*
 
61
 * DEFAULT_FONTPATH and PATHSEPARATOR are host type dependent and
 
62
 * are normally set by configure in gvconfig.h.  These are just
 
63
 * some last resort values that might match some Un*x system
 
64
 * if building this version of gd separate from graphviz.
 
65
 */
 
66
#ifndef DEFAULT_FONTPATH
 
67
#define DEFAULT_FONTPATH "/usr/share/fonts/truetype"
 
68
#endif
 
69
#ifndef PATHSEPARATOR
 
70
#define PATHSEPARATOR ":"
 
71
#endif
 
72
 
 
73
#ifndef TRUE
 
74
#define FALSE 0
 
75
#define TRUE !FALSE
 
76
#endif
 
77
 
 
78
#define MAX(a,b) ((a)>(b)?(a):(b))
 
79
#define MIN(a,b) ((a)<(b)?(a):(b))
 
80
 
 
81
typedef struct
 
82
{
 
83
  char *fontlist;               /* key */
 
84
  FT_Library *library;
 
85
  FT_Face face;
 
86
  FT_Bool have_char_map_unicode, have_char_map_big5, have_char_map_sjis,
 
87
    have_char_map_apple_roman;
 
88
  gdCache_head_t *glyphCache;
 
89
}
 
90
font_t;
 
91
 
 
92
typedef struct
 
93
  {
 
94
    char *fontlist;             /* key */
 
95
    FT_Library *library;
 
96
  }
 
97
fontkey_t;
 
98
 
 
99
typedef struct
 
100
  {
 
101
    int pixel;                  /* key */
 
102
    int bgcolor;                /* key */
 
103
    int fgcolor;                /* key *//* -ve means no antialias */
 
104
    gdImagePtr im;              /* key */
 
105
    int tweencolor;
 
106
  }
 
107
tweencolor_t;
 
108
 
 
109
typedef struct
 
110
  {
 
111
    int pixel;                  /* key */
 
112
    int bgcolor;                /* key */
 
113
    int fgcolor;                /* key *//* -ve means no antialias */
 
114
    gdImagePtr im;              /* key */
 
115
  }
 
116
tweencolorkey_t;
 
117
 
 
118
/********************************************************************
 
119
 * gdTcl_UtfToUniChar is borrowed from Tcl ...
 
120
 */
 
121
/*
 
122
 * tclUtf.c --
 
123
 *
 
124
 *      Routines for manipulating UTF-8 strings.
 
125
 *
 
126
 * Copyright (c) 1997-1998 Sun Microsystems, Inc.
 
127
 *
 
128
 * See the file "license.terms" for information on usage and redistribution
 
129
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 
130
 *
 
131
 * SCCS: @(#) tclUtf.c 1.25 98/01/28 18:02:43
 
132
 */
 
133
 
 
134
/*
 
135
 *---------------------------------------------------------------------------
 
136
 *
 
137
 * gdTcl_UtfToUniChar --
 
138
 *
 
139
 *      Extract the Tcl_UniChar represented by the UTF-8 string.  Bad
 
140
 *      UTF-8 sequences are converted to valid Tcl_UniChars and processing
 
141
 *      continues.  Equivalent to Plan 9 chartorune().
 
142
 *
 
143
 *      The caller must ensure that the source buffer is long enough that
 
144
 *      this routine does not run off the end and dereference non-existent
 
145
 *      memory looking for trail bytes.  If the source buffer is known to
 
146
 *      be '\0' terminated, this cannot happen.  Otherwise, the caller
 
147
 *      should call Tcl_UtfCharComplete() before calling this routine to
 
148
 *      ensure that enough bytes remain in the string.
 
149
 *
 
150
 * Results:
 
151
 *      *chPtr is filled with the Tcl_UniChar, and the return value is the
 
152
 *      number of bytes from the UTF-8 string that were consumed.
 
153
 *
 
154
 * Side effects:
 
155
 *      None.
 
156
 *
 
157
 *---------------------------------------------------------------------------
 
158
 */
 
159
 
 
160
#ifdef JISX0208
 
161
#include "jisx0208.h"
 
162
#endif
 
163
 
 
164
#define Tcl_UniChar int
 
165
#define TCL_UTF_MAX 3
 
166
static int
 
167
gdTcl_UtfToUniChar (char *str, Tcl_UniChar * chPtr)
 
168
/* str is the UTF8 next character pointer */
 
169
/* chPtr is the int for the result */
 
170
{
 
171
  int byte;
 
172
 
 
173
  /* HTML4.0 entities in decimal form, e.g. &#197; */
 
174
  byte = *((unsigned char *) str);
 
175
  if (byte == '&')
 
176
    {
 
177
      int i, n = 0;
 
178
 
 
179
      byte = *((unsigned char *) (str + 1));
 
180
      if (byte == '#')
 
181
        {
 
182
          for (i = 2; i < 8; i++)
 
183
            {
 
184
              byte = *((unsigned char *) (str + i));
 
185
              if (byte >= '0' && byte <= '9')
 
186
                {
 
187
                  n = (n * 10) + (byte - '0');
 
188
                }
 
189
              else
 
190
                break;
 
191
            }
 
192
          if (byte == ';')
 
193
            {
 
194
              *chPtr = (Tcl_UniChar) n;
 
195
              return ++i;
 
196
            }
 
197
        }
 
198
    }
 
199
 
 
200
  /*
 
201
   * Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones.
 
202
   */
 
203
 
 
204
  byte = *((unsigned char *) str);
 
205
#ifdef JISX0208
 
206
  if (0xA1 <= byte && byte <= 0xFE)
 
207
    {
 
208
      int ku, ten;
 
209
 
 
210
      ku = (byte & 0x7F) - 0x20;
 
211
      ten = (str[1] & 0x7F) - 0x20;
 
212
      if ((ku < 1 || ku > 92) || (ten < 1 || ten > 94))
 
213
        {
 
214
          *chPtr = (Tcl_UniChar) byte;
 
215
          return 1;
 
216
        }
 
217
 
 
218
      *chPtr = (Tcl_UniChar) UnicodeTbl[ku - 1][ten - 1];
 
219
      return 2;
 
220
    }
 
221
  else
 
222
#endif /* JISX0208 */
 
223
  if (byte < 0xC0)
 
224
    {
 
225
      /*
 
226
       * Handles properly formed UTF-8 characters between
 
227
       * 0x01 and 0x7F.  Also treats \0 and naked trail
 
228
       * bytes 0x80 to 0xBF as valid characters representing
 
229
       * themselves.
 
230
       */
 
231
 
 
232
      *chPtr = (Tcl_UniChar) byte;
 
233
      return 1;
 
234
    }
 
235
  else if (byte < 0xE0)
 
236
    {
 
237
      if ((str[1] & 0xC0) == 0x80)
 
238
        {
 
239
          /*
 
240
           * Two-byte-character lead-byte followed
 
241
           * by a trail-byte.
 
242
           */
 
243
 
 
244
          *chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6)
 
245
                                  | (str[1] & 0x3F));
 
246
          return 2;
 
247
        }
 
248
      /*
 
249
       * A two-byte-character lead-byte not followed by trail-byte
 
250
       * represents itself.
 
251
       */
 
252
 
 
253
      *chPtr = (Tcl_UniChar) byte;
 
254
      return 1;
 
255
    }
 
256
  else if (byte < 0xF0)
 
257
    {
 
258
      if (((str[1] & 0xC0) == 0x80) && ((str[2] & 0xC0) == 0x80))
 
259
        {
 
260
          /*
 
261
           * Three-byte-character lead byte followed by
 
262
           * two trail bytes.
 
263
           */
 
264
 
 
265
          *chPtr = (Tcl_UniChar) (((byte & 0x0F) << 12)
 
266
                                | ((str[1] & 0x3F) << 6) | (str[2] & 0x3F));
 
267
          return 3;
 
268
        }
 
269
      /*
 
270
       * A three-byte-character lead-byte not followed by
 
271
       * two trail-bytes represents itself.
 
272
       */
 
273
 
 
274
      *chPtr = (Tcl_UniChar) byte;
 
275
      return 1;
 
276
    }
 
277
#if TCL_UTF_MAX > 3
 
278
  else
 
279
    {
 
280
      int ch, total, trail;
 
281
 
 
282
      total = totalBytes[byte];
 
283
      trail = total - 1;
 
284
      if (trail > 0)
 
285
        {
 
286
          ch = byte & (0x3F >> trail);
 
287
          do
 
288
            {
 
289
              str++;
 
290
              if ((*str & 0xC0) != 0x80)
 
291
                {
 
292
                  *chPtr = byte;
 
293
                  return 1;
 
294
                }
 
295
              ch <<= 6;
 
296
              ch |= (*str & 0x3F);
 
297
              trail--;
 
298
            }
 
299
          while (trail > 0);
 
300
          *chPtr = ch;
 
301
          return total;
 
302
        }
 
303
    }
 
304
#endif
 
305
 
 
306
  *chPtr = (Tcl_UniChar) byte;
 
307
  return 1;
 
308
}
 
309
 
 
310
/********************************************************************/
 
311
/* font cache functions                                             */
 
312
 
 
313
static int
 
314
fontTest (void *element, void *key)
 
315
{
 
316
  font_t *a = (font_t *) element;
 
317
  fontkey_t *b = (fontkey_t *) key;
 
318
 
 
319
  return (strcmp (a->fontlist, b->fontlist) == 0);
 
320
}
 
321
 
 
322
static void *
 
323
fontFetch (char **error, void *key)
 
324
{
 
325
  font_t *a;
 
326
  fontkey_t *b = (fontkey_t *) key;
 
327
  int n;
 
328
  int font_found = 0;
 
329
  unsigned short platform, encoding;
 
330
  char *fontsearchpath, *fontlist;
 
331
  char *fullname = NULL;
 
332
  char *name, *path, *dir;
 
333
  char *strtok_ptr;
 
334
  FT_Error err;
 
335
  FT_CharMap found = 0;
 
336
  FT_CharMap charmap;
 
337
 
 
338
  a = (font_t *) gdMalloc (sizeof (font_t));
 
339
  a->fontlist = strdup (b->fontlist);
 
340
  a->library = b->library;
 
341
 
 
342
  /*
 
343
   * Search the pathlist for any of a list of font names.
 
344
   */
 
345
  fontsearchpath = getenv ("GDFONTPATH");
 
346
  if (!fontsearchpath)
 
347
    fontsearchpath = DEFAULT_FONTPATH;
 
348
  fontlist = strdup (a->fontlist);
 
349
 
 
350
  /*
 
351
   * Must use gd_strtok_r else pointer corrupted by strtok in nested loop.
 
352
   */
 
353
  for (name = gd_strtok_r (fontlist, LISTSEPARATOR, &strtok_ptr); name;
 
354
       name = gd_strtok_r (0, LISTSEPARATOR, &strtok_ptr))
 
355
    {
 
356
 
 
357
      /* make a fresh copy each time - strtok corrupts it. */
 
358
      path = strdup (fontsearchpath);
 
359
      /*
 
360
       * Allocate an oversized buffer that is guaranteed to be
 
361
       * big enough for all paths to be tested.
 
362
       */
 
363
      fullname = gdRealloc (fullname,
 
364
                            strlen (fontsearchpath) + strlen (name) + 6);
 
365
      /* if name is an absolute filename then test directly */
 
366
      if (*name == '/' || (name[0] != 0 && name[1] == ':' && (name[2] == '/' || name[2] == '\\')))
 
367
        {
 
368
          sprintf (fullname, "%s", name);
 
369
          if (access (fullname, R_OK) == 0)
 
370
            {
 
371
              font_found++;
 
372
              break;
 
373
            }
 
374
        }
 
375
      for (dir = strtok (path, PATHSEPARATOR); dir;
 
376
           dir = strtok (0, PATHSEPARATOR))
 
377
        {
 
378
          sprintf (fullname, "%s/%s.ttf", dir, name);
 
379
          if (access (fullname, R_OK) == 0)
 
380
            {
 
381
              font_found++;
 
382
              break;
 
383
            }
 
384
        }
 
385
      if (font_found)
 
386
         break;
 
387
      gdFree (path);
 
388
    }
 
389
  gdFree (fontlist);
 
390
  if (!font_found)
 
391
    {
 
392
      *error = "Could not find/open font";
 
393
      return NULL;
 
394
    }
 
395
 
 
396
  err = FT_New_Face (*b->library, fullname, 0, &a->face);
 
397
  if (err)
 
398
    {
 
399
      *error = "Could not read font";
 
400
      return NULL;
 
401
    }
 
402
  gdFree (fullname);
 
403
 
 
404
/* FIXME - This mapping stuff is imcomplete - where is the spec? */
 
405
 
 
406
  a->have_char_map_unicode = 0;
 
407
  a->have_char_map_big5 = 0;
 
408
  a->have_char_map_sjis = 0;
 
409
  a->have_char_map_apple_roman = 0;
 
410
  for (n = 0; n < a->face->num_charmaps; n++)
 
411
    {
 
412
      charmap = a->face->charmaps[n];
 
413
      platform = charmap->platform_id;
 
414
      encoding = charmap->encoding_id;
 
415
      if ((platform == 3 && encoding == 1)      /* Windows Unicode */
 
416
          || (platform == 3 && encoding == 0)   /* Windows Symbol */
 
417
          || (platform == 2 && encoding == 1)   /* ISO Unicode */
 
418
          || (platform == 0))
 
419
        {                       /* Apple Unicode */
 
420
          a->have_char_map_unicode = 1;
 
421
          found = charmap;
 
422
        }
 
423
      else if (platform == 3 && encoding == 4)
 
424
        {                       /* Windows Big5 */
 
425
          a->have_char_map_big5 = 1;
 
426
          found = charmap;
 
427
        }
 
428
      else if (platform == 3 && encoding == 2)
 
429
        {                       /* Windows Sjis */
 
430
          a->have_char_map_sjis = 1;
 
431
          found = charmap;
 
432
        }
 
433
      else if ((platform == 1 && encoding == 0)         /* Apple Roman */
 
434
               || (platform == 2 && encoding == 0))
 
435
        {                       /* ISO ASCII */
 
436
          a->have_char_map_apple_roman = 1;
 
437
          found = charmap;
 
438
        }
 
439
    }
 
440
  if (!found)
 
441
    {
 
442
      *error = "Unable to find a CharMap that I can handle";
 
443
      return NULL;
 
444
    }
 
445
 
 
446
  return (void *) a;
 
447
}
 
448
 
 
449
static void
 
450
fontRelease (void *element)
 
451
{
 
452
  font_t *a = (font_t *) element;
 
453
 
 
454
  FT_Done_Face (a->face);
 
455
  gdFree (a->fontlist);
 
456
  gdFree ((char *) element);
 
457
}
 
458
 
 
459
/********************************************************************/
 
460
/* tweencolor cache functions                                            */
 
461
 
 
462
static int
 
463
tweenColorTest (void *element, void *key)
 
464
{
 
465
  tweencolor_t *a = (tweencolor_t *) element;
 
466
  tweencolorkey_t *b = (tweencolorkey_t *) key;
 
467
 
 
468
  return (a->pixel == b->pixel
 
469
          && a->bgcolor == b->bgcolor
 
470
          && a->fgcolor == b->fgcolor
 
471
          && a->im == b->im);
 
472
}
 
473
 
 
474
/*
 
475
 * Computes a color in im's color table that is part way between
 
476
 * the background and foreground colors proportional to the gray
 
477
 * pixel value in the range 0-NUMCOLORS. The fg and bg colors must already
 
478
 * be in the color table.
 
479
 */
 
480
static void *
 
481
tweenColorFetch (char **error, void *key)
 
482
{
 
483
  tweencolor_t *a;
 
484
  tweencolorkey_t *b = (tweencolorkey_t *) key;
 
485
  int pixel, npixel, bg, fg;
 
486
  gdImagePtr im;
 
487
 
 
488
  a = (tweencolor_t *) gdMalloc (sizeof (tweencolor_t));
 
489
  pixel = a->pixel = b->pixel;
 
490
  bg = a->bgcolor = b->bgcolor;
 
491
  fg = a->fgcolor = b->fgcolor;
 
492
  im = b->im;
 
493
 
 
494
  /* if fg is specified by a negative color idx, then don't antialias */
 
495
  if (fg < 0)
 
496
    {
 
497
      a->tweencolor = -fg;
 
498
    }
 
499
  else
 
500
    {
 
501
      npixel = NUMCOLORS - pixel;
 
502
      if (im->trueColor)
 
503
        {
 
504
          /* 2.0.1: use gdImageSetPixel to do the alpha blending work,
 
505
             or to just store the alpha level. All we have to do here
 
506
             is incorporate our knowledge of the percentage of this
 
507
             pixel that is really "lit" by pushing the alpha value
 
508
             up toward transparency in edge regions. */
 
509
          a->tweencolor = gdTrueColorAlpha (
 
510
                                             gdTrueColorGetRed (fg),
 
511
                                             gdTrueColorGetGreen (fg),
 
512
                                             gdTrueColorGetBlue (fg),
 
513
               gdAlphaMax - (gdTrueColorGetAlpha (fg) * pixel / NUMCOLORS));
 
514
        }
 
515
      else
 
516
        {
 
517
          a->tweencolor = gdImageColorResolve (im,
 
518
                   (pixel * im->red[fg] + npixel * im->red[bg]) / NUMCOLORS,
 
519
               (pixel * im->green[fg] + npixel * im->green[bg]) / NUMCOLORS,
 
520
                (pixel * im->blue[fg] + npixel * im->blue[bg]) / NUMCOLORS);
 
521
        }
 
522
    }
 
523
  return (void *) a;
 
524
}
 
525
 
 
526
static void
 
527
tweenColorRelease (void *element)
 
528
{
 
529
  gdFree ((char *) element);
 
530
}
 
531
 
 
532
/* draw_bitmap - transfers glyph bitmap to GD image */
 
533
static char *
 
534
gdft_draw_bitmap (gdCache_head_t *tc_cache, gdImage * im, int fg, FT_Bitmap bitmap, int pen_x, int pen_y)
 
535
{
 
536
  unsigned char *pixel = NULL;
 
537
  int *tpixel = NULL;
 
538
  int x, y, row, col, pc;
 
539
 
 
540
  tweencolor_t *tc_elem;
 
541
  tweencolorkey_t tc_key;
 
542
 
 
543
  /* copy to image, mapping colors */
 
544
  tc_key.fgcolor = fg;
 
545
  tc_key.im = im;
 
546
  for (row = 0; row < bitmap.rows; row++)
 
547
    {
 
548
      pc = row * bitmap.pitch;
 
549
      if(bitmap.pixel_mode==ft_pixel_mode_mono)
 
550
              pc *= 8;    /* pc is measured in bits for monochrome images */
 
551
 
 
552
      y = pen_y + row;
 
553
 
 
554
      /* clip if out of bounds */
 
555
      if (y >= im->sy || y < 0)
 
556
        continue;
 
557
 
 
558
      for (col = 0; col < bitmap.width; col++, pc++)
 
559
        {
 
560
          if (bitmap.pixel_mode == ft_pixel_mode_grays)
 
561
            {
 
562
              /*
 
563
               * Round to NUMCOLORS levels of antialiasing for
 
564
               * index color images since only 256 colors are
 
565
               * available.
 
566
               */
 
567
              tc_key.pixel = ((bitmap.buffer[pc] * NUMCOLORS)
 
568
                              + bitmap.num_grays / 2)
 
569
                / (bitmap.num_grays - 1);
 
570
            }
 
571
          else if (bitmap.pixel_mode == ft_pixel_mode_mono)
 
572
            {
 
573
              tc_key.pixel = ((bitmap.buffer[pc / 8]
 
574
                               << (pc % 8)) & 128) ? NUMCOLORS : 0;
 
575
            }
 
576
          else
 
577
            {
 
578
              return "Unsupported ft_pixel_mode";
 
579
            }
 
580
 
 
581
          if (tc_key.pixel > 0)
 
582
            {                   /* if not background */
 
583
              x = pen_x + col;
 
584
 
 
585
              /* clip if out of bounds */
 
586
              if (x >= im->sx || x < 0)
 
587
                continue;
 
588
              /* get pixel location in gd buffer */
 
589
              if (im->trueColor)
 
590
                {
 
591
                  tpixel = &im->tpixels[y][x];
 
592
                }
 
593
              else
 
594
                {
 
595
                  pixel = &im->pixels[y][x];
 
596
                }
 
597
              if (tc_key.pixel == NUMCOLORS)
 
598
                {
 
599
                  /* use fg color directly */
 
600
                  if (im->trueColor)
 
601
                    {
 
602
                      *tpixel = abs( fg );
 
603
                    }
 
604
                  else
 
605
                    {
 
606
                      *pixel = abs( fg );
 
607
                    }
 
608
                }
 
609
              else
 
610
                {
 
611
                  /* find antialised color */
 
612
                  if (im->trueColor)
 
613
                    {
 
614
                      tc_key.bgcolor = *tpixel;
 
615
                    }
 
616
                  else
 
617
                    {
 
618
                      tc_key.bgcolor = *pixel;
 
619
                    }
 
620
                  tc_elem = (tweencolor_t *) gdCacheGet (
 
621
                                                          tc_cache, &tc_key);
 
622
                  if (im->trueColor)
 
623
                    {
 
624
                      *tpixel = tc_elem->tweencolor;
 
625
                    }
 
626
                  else
 
627
                    {
 
628
                      *pixel = tc_elem->tweencolor;
 
629
                    }
 
630
                }
 
631
            }
 
632
        }
 
633
    }
 
634
  return (char *) NULL;
 
635
}
 
636
 
 
637
static int
 
638
gdroundupdown (FT_F26Dot6 v1, int updown)
 
639
{
 
640
  return (!updown)
 
641
    ? (v1 < 0 ? ((v1 - 63) >> 6) : v1 >> 6)
 
642
    : (v1 > 0 ? ((v1 + 63) >> 6) : v1 >> 6);
 
643
}
 
644
 
 
645
 
 
646
extern int any2eucjp (char *, char *, unsigned int);
 
647
 
 
648
/********************************************************************/
 
649
/* gdImageStringFT -  render a utf8 string onto a gd image          */
 
650
 
 
651
char *
 
652
gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
 
653
                 double ptsize, double angle, int x, int y, char *string)
 
654
{
 
655
  FT_BBox bbox, glyph_bbox;
 
656
  FT_Matrix matrix;
 
657
  FT_Vector pen, delta, penf;
 
658
  FT_Face face;
 
659
  FT_Glyph image;
 
660
  FT_GlyphSlot slot;
 
661
  FT_Error err;
 
662
  FT_Bool use_kerning;
 
663
  FT_UInt glyph_index, previous;
 
664
  double sin_a = sin (angle);
 
665
  double cos_a = cos (angle);
 
666
  int len, i = 0, ch;
 
667
  int x1 = 0, y1 = 0;
 
668
  font_t *font;
 
669
  fontkey_t fontkey;
 
670
  char *next;
 
671
  char *tmpstr = 0;
 
672
  int render = (im && (im->trueColor || (fg <= 255 && fg >= -255)));
 
673
  FT_BitmapGlyph bm;
 
674
  int render_mode = FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT;
 
675
 
 
676
 
 
677
/***** initialize font library and font cache on first call ******/
 
678
  static gdCache_head_t *fontCache;
 
679
  static FT_Library library;
 
680
 
 
681
  /* 
 
682
   *   make a new tweenColorCache on every call 
 
683
   *   because caching colormappings between calls
 
684
   *   is not safe. If the im-pointer points to a 
 
685
   *   brand new image, the cache gives out bogus
 
686
   *   colorindexes.          -- 27.06.2001 <krisku@arrak.fi>
 
687
   */
 
688
  gdCache_head_t  *tc_cache; 
 
689
 
 
690
  tc_cache = gdCacheCreate( TWEENCOLORCACHESIZE,
 
691
                tweenColorTest, tweenColorFetch, tweenColorRelease );
 
692
 
 
693
  if (!fontCache)
 
694
    {
 
695
      if (FT_Init_FreeType (&library))
 
696
        {
 
697
          gdCacheDelete( tc_cache );
 
698
          return "Failure to initialize font library";
 
699
        }
 
700
      fontCache = gdCacheCreate (FONTCACHESIZE,
 
701
                                 fontTest, fontFetch, fontRelease);
 
702
    }
 
703
/*****/
 
704
 
 
705
  /* get the font (via font cache) */
 
706
  fontkey.fontlist = fontlist;
 
707
  fontkey.library = &library;
 
708
  font = (font_t *) gdCacheGet (fontCache, &fontkey);
 
709
  if (!font)
 
710
    {
 
711
      gdCacheDelete( tc_cache );
 
712
      return fontCache->error;
 
713
    }
 
714
  face = font->face;            /* shortcut */
 
715
  slot = face->glyph;           /* shortcut */
 
716
 
 
717
  if (FT_Set_Char_Size (face, 0, (FT_F26Dot6) (ptsize * 64),
 
718
                        GD_RESOLUTION, GD_RESOLUTION))
 
719
    {
 
720
      gdCacheDelete( tc_cache );
 
721
      return "Could not set character size";
 
722
    }
 
723
 
 
724
  matrix.xx = (FT_Fixed) (cos_a * (1 << 16));
 
725
  matrix.yx = (FT_Fixed) (sin_a * (1 << 16));
 
726
  matrix.xy = -matrix.yx;
 
727
  matrix.yy = matrix.xx;
 
728
 
 
729
  penf.x = penf.y = 0;          /* running position of non-rotated string */
 
730
  pen.x = pen.y = 0;            /* running position of rotated string */
 
731
  bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
 
732
  use_kerning = FT_HAS_KERNING (face);
 
733
  previous = 0;
 
734
  if (fg < 0)
 
735
    {
 
736
      render_mode |= FT_LOAD_MONOCHROME;
 
737
    }
 
738
 
 
739
#ifndef JISX0208
 
740
  if (font->have_char_map_sjis)
 
741
    {
 
742
#endif
 
743
      if ((tmpstr = (char *) gdMalloc (BUFSIZ)))
 
744
        {
 
745
          any2eucjp (tmpstr, string, BUFSIZ);
 
746
          next = tmpstr;
 
747
        }
 
748
      else
 
749
        {
 
750
          next = string;
 
751
        }
 
752
#ifndef JISX0208
 
753
    }
 
754
  else
 
755
    {
 
756
      next = string;
 
757
    }
 
758
#endif
 
759
  while (*next)
 
760
    {
 
761
      ch = *next;
 
762
 
 
763
      /* carriage returns */
 
764
      if (ch == '\r')
 
765
        {
 
766
          penf.x = 0;
 
767
          x1 = (penf.x * cos_a - penf.y * sin_a + 32) / 64;
 
768
          y1 = (penf.x * sin_a + penf.y * cos_a + 32) / 64;
 
769
          pen.x = pen.y = 0;
 
770
          previous = 0;         /* clear kerning flag */
 
771
          next++;
 
772
          continue;
 
773
        }
 
774
      /* newlines */
 
775
      if (ch == '\n')
 
776
        {
 
777
          penf.y -= face->size->metrics.height * LINESPACE;
 
778
          penf.y = (penf.y - 32) & -64;         /* round to next pixel row */
 
779
          x1 = (penf.x * cos_a - penf.y * sin_a + 32) / 64;
 
780
          y1 = (penf.x * sin_a + penf.y * cos_a + 32) / 64;
 
781
          pen.x = pen.y = 0;
 
782
          previous = 0;         /* clear kerning flag */
 
783
          next++;
 
784
          continue;
 
785
        }
 
786
 
 
787
      if (font->have_char_map_unicode)
 
788
        {
 
789
          /* use UTF-8 mapping from ASCII */
 
790
          len = gdTcl_UtfToUniChar (next, &ch);
 
791
          next += len;
 
792
        }
 
793
      else if (font->have_char_map_sjis)
 
794
        {
 
795
          unsigned char c;
 
796
          int jiscode;
 
797
 
 
798
          c = *next;
 
799
          if (0xA1 <= c && c <= 0xFE)
 
800
            {
 
801
              next++;
 
802
              jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F);
 
803
 
 
804
              ch = (jiscode >> 8) & 0xFF;
 
805
              jiscode &= 0xFF;
 
806
 
 
807
              if (ch & 1)
 
808
                jiscode += 0x40 - 0x21;
 
809
              else
 
810
                jiscode += 0x9E - 0x21;
 
811
 
 
812
              if (jiscode >= 0x7F)
 
813
                jiscode++;
 
814
              ch = (ch - 0x21) / 2 + 0x81;
 
815
              if (ch >= 0xA0)
 
816
                ch += 0x40;
 
817
 
 
818
              ch = (ch << 8) + jiscode;
 
819
            }
 
820
          else
 
821
            {
 
822
              ch = c & 0xFF;    /* don't extend sign */
 
823
            }
 
824
          next++;
 
825
        }
 
826
      else
 
827
        {
 
828
          /*
 
829
           * Big 5 mapping:
 
830
           * use "JIS-8 half-width katakana" coding from 8-bit characters. Ref:
 
831
           * ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs
 
832
           */
 
833
          ch = (*next) & 0xFF;  /* don't extend sign */
 
834
          next++;
 
835
          if (ch >= 161         /* first code of JIS-8 pair */
 
836
              && *next)
 
837
            {                   /* don't advance past '\0' */
 
838
              /* TBB: Fix from Kwok Wah On: & 255 needed */
 
839
              ch = (ch * 256) + ((*next) & 255);
 
840
              next++;
 
841
            }
 
842
        }
 
843
 
 
844
      /* set rotation transform */
 
845
      FT_Set_Transform(face, &matrix, NULL);
 
846
 
 
847
      /* Convert character code to glyph index */
 
848
      glyph_index = FT_Get_Char_Index (face, ch);
 
849
 
 
850
      /* retrieve kerning distance and move pen position */
 
851
      if (use_kerning && previous && glyph_index)
 
852
        {
 
853
          FT_Get_Kerning (face, previous, glyph_index,
 
854
                          ft_kerning_default, &delta);
 
855
          pen.x += delta.x;
 
856
        }
 
857
 
 
858
      /* load glyph image into the slot (erase previous one) */
 
859
      err = FT_Load_Glyph (face, glyph_index, render_mode);
 
860
      if (err)
 
861
        {
 
862
          gdCacheDelete( tc_cache );
 
863
          return "Problem loading glyph";
 
864
        }
 
865
 
 
866
      /* transform glyph image */
 
867
      FT_Get_Glyph (slot, &image);
 
868
      if (brect)
 
869
        {                       /* only if need brect */
 
870
          FT_Glyph_Get_CBox (image, ft_glyph_bbox_gridfit, &glyph_bbox);
 
871
          glyph_bbox.xMin += penf.x;
 
872
          glyph_bbox.yMin += penf.y;
 
873
          glyph_bbox.xMax += penf.x;
 
874
          glyph_bbox.yMax += penf.y;
 
875
          if (!i)
 
876
            {                   /* if first character, init BB corner values */
 
877
              bbox.xMin = glyph_bbox.xMin;
 
878
              bbox.yMin = glyph_bbox.yMin;
 
879
              bbox.xMax = glyph_bbox.xMax;
 
880
              bbox.yMax = glyph_bbox.yMax;
 
881
            }
 
882
          else
 
883
            {
 
884
              if (bbox.xMin > glyph_bbox.xMin)
 
885
                bbox.xMin = glyph_bbox.xMin;
 
886
              if (bbox.yMin > glyph_bbox.yMin)
 
887
                bbox.yMin = glyph_bbox.yMin;
 
888
              if (bbox.xMax < glyph_bbox.xMax)
 
889
                bbox.xMax = glyph_bbox.xMax;
 
890
              if (bbox.yMax < glyph_bbox.yMax)
 
891
                bbox.yMax = glyph_bbox.yMax;
 
892
            }
 
893
          i++;
 
894
        }
 
895
 
 
896
      if (render)
 
897
        {
 
898
          if (image->format != ft_glyph_format_bitmap)
 
899
            {
 
900
              err = FT_Glyph_To_Bitmap (&image, ft_render_mode_normal, 0, 1);
 
901
              if (err)
 
902
                {
 
903
                  gdCacheDelete( tc_cache );
 
904
                  return "Problem rendering glyph";
 
905
                }
 
906
            }
 
907
 
 
908
          /* now, draw to our target surface */
 
909
          bm = (FT_BitmapGlyph) image;
 
910
          gdft_draw_bitmap (tc_cache, im, fg, bm->bitmap,
 
911
                            x + x1 + ((pen.x + 31) >> 6) + bm->left,
 
912
                            y - y1 + ((pen.y + 31) >> 6) - bm->top);
 
913
        }
 
914
 
 
915
      /* record current glyph index for kerning */
 
916
      previous = glyph_index;
 
917
 
 
918
      /* increment pen position */
 
919
      pen.x += image->advance.x >> 10;
 
920
      pen.y -= image->advance.y >> 10;
 
921
 
 
922
      penf.x += slot->metrics.horiAdvance;
 
923
 
 
924
      FT_Done_Glyph (image);
 
925
    }
 
926
 
 
927
  if (brect)
 
928
    {                           /* only if need brect */
 
929
      /* For perfect rounding, must get sin(a + pi/4) and sin(a - pi/4). */
 
930
      double d1 = sin (angle + 0.78539816339744830962);
 
931
      double d2 = sin (angle - 0.78539816339744830962);
 
932
 
 
933
      /* rotate bounding rectangle */
 
934
      brect[0] = (int) (bbox.xMin * cos_a - bbox.yMin * sin_a);
 
935
      brect[1] = (int) (bbox.xMin * sin_a + bbox.yMin * cos_a);
 
936
      brect[2] = (int) (bbox.xMax * cos_a - bbox.yMin * sin_a);
 
937
      brect[3] = (int) (bbox.xMax * sin_a + bbox.yMin * cos_a);
 
938
      brect[4] = (int) (bbox.xMax * cos_a - bbox.yMax * sin_a);
 
939
      brect[5] = (int) (bbox.xMax * sin_a + bbox.yMax * cos_a);
 
940
      brect[6] = (int) (bbox.xMin * cos_a - bbox.yMax * sin_a);
 
941
      brect[7] = (int) (bbox.xMin * sin_a + bbox.yMax * cos_a);
 
942
 
 
943
      /* scale, round and offset brect */
 
944
      brect[0] = x + gdroundupdown (brect[0], d2 > 0);
 
945
      brect[1] = y - gdroundupdown (brect[1], d1 < 0);
 
946
      brect[2] = x + gdroundupdown (brect[2], d1 > 0);
 
947
      brect[3] = y - gdroundupdown (brect[3], d2 > 0);
 
948
      brect[4] = x + gdroundupdown (brect[4], d2 < 0);
 
949
      brect[5] = y - gdroundupdown (brect[5], d1 > 0);
 
950
      brect[6] = x + gdroundupdown (brect[6], d1 < 0);
 
951
      brect[7] = y - gdroundupdown (brect[7], d2 < 0);
 
952
    }
 
953
 
 
954
  if (tmpstr)
 
955
    gdFree (tmpstr);
 
956
  gdCacheDelete( tc_cache );
 
957
  return (char *) NULL;
 
958
}
 
959
 
 
960
#endif /* HAVE_LIBFREETYPE */