1 :
2 : /********************************************/
3 : /* gd interface to freetype library */
4 : /* */
5 : /* John Ellson ellson@graphviz.org */
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 : #include <io.h>
19 : #ifndef R_OK
20 : # define R_OK 04 /* Needed in Windows */
21 : #endif
22 : #endif
23 :
24 : #ifdef WIN32
25 : #define access _access
26 : #ifndef R_OK
27 : #define R_OK 2
28 : #endif
29 : #endif
30 :
31 : /* number of antialised colors for indexed bitmaps */
32 : /* overwrite Windows GDI define in case of windows build */
33 : #ifdef NUMCOLORS
34 : #undef NUMCOLORS
35 : #endif
36 : #define NUMCOLORS 8
37 :
38 : char *
39 : gdImageStringTTF (gdImage * im, int *brect, int fg, char *fontlist,
40 : double ptsize, double angle, int x, int y, char *string)
41 0 : {
42 : /* 2.0.6: valid return */
43 0 : return gdImageStringFT (im, brect, fg, fontlist, ptsize, angle, x, y, string);
44 : }
45 :
46 : #ifndef HAVE_LIBFREETYPE
47 : char *
48 : gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist,
49 : double ptsize, double angle, int x, int y, char *string,
50 : gdFTStringExtraPtr strex)
51 : {
52 : return "libgd was not built with FreeType font support\n";
53 : }
54 :
55 : char *
56 : gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
57 : double ptsize, double angle, int x, int y, char *string)
58 : {
59 : return "libgd was not built with FreeType font support\n";
60 : }
61 : #else
62 :
63 : #include "gdcache.h"
64 : #include <ft2build.h>
65 : #include FT_FREETYPE_H
66 : #include FT_GLYPH_H
67 :
68 : /* number of fonts cached before least recently used is replaced */
69 : #define FONTCACHESIZE 6
70 :
71 : /* number of antialias color lookups cached */
72 : #define TWEENCOLORCACHESIZE 32
73 :
74 : /*
75 : * Line separation as a factor of font height.
76 : * No space between if LINESPACE = 1.00
77 : * Line separation will be rounded up to next pixel row.
78 : */
79 : #define LINESPACE 1.05
80 :
81 : /*
82 : * The character (space) used to separate alternate fonts in the
83 : * fontlist parameter to gdImageStringFT. 2.0.18: space was a oor choice for this.
84 : */
85 : #define LISTSEPARATOR ";"
86 :
87 : /*
88 : * DEFAULT_FONTPATH and PATHSEPARATOR are host type dependent and
89 : * are normally set by configure in config.h. These are just
90 : * some last resort values that might match some Un*x system
91 : * if building this version of gd separate from graphviz.
92 : */
93 : #ifndef DEFAULT_FONTPATH
94 : #if defined(__APPLE__) || (defined(__MWERKS__) && defined(macintosh))
95 : #define DEFAULT_FONTPATH "/usr/share/fonts/truetype:/System/Library/Fonts:/Library/Fonts"
96 : #else
97 : #define DEFAULT_FONTPATH "/usr/share/fonts/truetype"
98 : #endif
99 : #endif
100 : #ifndef PATHSEPARATOR
101 : #define PATHSEPARATOR ":"
102 : #endif
103 :
104 : #ifndef TRUE
105 : #define FALSE 0
106 : #define TRUE !FALSE
107 : #endif
108 :
109 : #ifndef MAX
110 : #define MAX(a,b) ((a)>(b)?(a):(b))
111 : #endif
112 :
113 : #ifndef MIN
114 : #define MIN(a,b) ((a)<(b)?(a):(b))
115 : #endif
116 :
117 : typedef struct
118 : {
119 : char *fontlist; /* key */
120 : FT_Library *library;
121 : FT_Face face;
122 : FT_Bool have_char_map_unicode, have_char_map_big5, have_char_map_sjis, have_char_map_apple_roman;
123 : gdCache_head_t *glyphCache;
124 : } font_t;
125 :
126 : typedef struct
127 : {
128 : char *fontlist; /* key */
129 : FT_Library *library;
130 : } fontkey_t;
131 :
132 : typedef struct
133 : {
134 : int pixel; /* key */
135 : int bgcolor; /* key */
136 : int fgcolor; /* key *//* -ve means no antialias */
137 : gdImagePtr im; /* key */
138 : int tweencolor;
139 : } tweencolor_t;
140 :
141 : typedef struct
142 : {
143 : int pixel; /* key */
144 : int bgcolor; /* key */
145 : int fgcolor; /* key *//* -ve means no antialias */
146 : gdImagePtr im; /* key */
147 : } tweencolorkey_t;
148 :
149 : /********************************************************************
150 : * gdTcl_UtfToUniChar is borrowed from Tcl ...
151 : */
152 : /*
153 : * tclUtf.c --
154 : *
155 : * Routines for manipulating UTF-8 strings.
156 : *
157 : * Copyright (c) 1997-1998 Sun Microsystems, Inc.
158 : *
159 : * See the file "license.terms" for information on usage and redistribution
160 : * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
161 : *
162 : * SCCS: @(#) tclUtf.c 1.25 98/01/28 18:02:43
163 : */
164 :
165 : /*
166 : *---------------------------------------------------------------------------
167 : *
168 : * gdTcl_UtfToUniChar --
169 : *
170 : * Extract the Tcl_UniChar represented by the UTF-8 string. Bad
171 : * UTF-8 sequences are converted to valid Tcl_UniChars and processing
172 : * continues. Equivalent to Plan 9 chartorune().
173 : *
174 : * The caller must ensure that the source buffer is long enough that
175 : * this routine does not run off the end and dereference non-existent
176 : * memory looking for trail bytes. If the source buffer is known to
177 : * be '\0' terminated, this cannot happen. Otherwise, the caller
178 : * should call Tcl_UtfCharComplete() before calling this routine to
179 : * ensure that enough bytes remain in the string.
180 : *
181 : * Results:
182 : * *chPtr is filled with the Tcl_UniChar, and the return value is the
183 : * number of bytes from the UTF-8 string that were consumed.
184 : *
185 : * Side effects:
186 : * None.
187 : *
188 : *---------------------------------------------------------------------------
189 : */
190 :
191 : #ifdef JISX0208
192 : #include "jisx0208.h"
193 : #endif
194 :
195 : extern int any2eucjp (char *, char *, unsigned int);
196 :
197 : /* Persistent font cache until explicitly cleared */
198 : /* Fonts can be used across multiple images */
199 :
200 : /* 2.0.16: thread safety (the font cache is shared) */
201 : gdMutexDeclare(gdFontCacheMutex);
202 : static gdCache_head_t *fontCache = NULL;
203 : static FT_Library library;
204 :
205 : #define Tcl_UniChar int
206 : #define TCL_UTF_MAX 3
207 : static int gdTcl_UtfToUniChar (char *str, Tcl_UniChar * chPtr)
208 : /* str is the UTF8 next character pointer */
209 : /* chPtr is the int for the result */
210 174 : {
211 : int byte;
212 :
213 : /* HTML4.0 entities in decimal form, e.g. Å */
214 174 : byte = *((unsigned char *) str);
215 174 : if (byte == '&') {
216 0 : int i, n = 0;
217 :
218 0 : byte = *((unsigned char *) (str + 1));
219 0 : if (byte == '#') {
220 0 : byte = *((unsigned char *) (str + 2));
221 0 : if (byte == 'x' || byte == 'X') {
222 0 : for (i = 3; i < 8; i++) {
223 0 : byte = *((unsigned char *) (str + i));
224 0 : if (byte >= 'A' && byte <= 'F')
225 0 : byte = byte - 'A' + 10;
226 0 : else if (byte >= 'a' && byte <= 'f')
227 0 : byte = byte - 'a' + 10;
228 0 : else if (byte >= '0' && byte <= '9')
229 0 : byte = byte - '0';
230 : else
231 : break;
232 0 : n = (n * 16) + byte;
233 : }
234 : } else {
235 0 : for (i = 2; i < 8; i++) {
236 0 : byte = *((unsigned char *) (str + i));
237 0 : if (byte >= '0' && byte <= '9') {
238 0 : n = (n * 10) + (byte - '0');
239 : } else {
240 : break;
241 : }
242 : }
243 : }
244 0 : if (byte == ';') {
245 0 : *chPtr = (Tcl_UniChar) n;
246 0 : return ++i;
247 : }
248 : }
249 : }
250 :
251 : /* Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones. */
252 :
253 174 : byte = *((unsigned char *) str);
254 : #ifdef JISX0208
255 : if (0xA1 <= byte && byte <= 0xFE) {
256 : int ku, ten;
257 :
258 : ku = (byte & 0x7F) - 0x20;
259 : ten = (str[1] & 0x7F) - 0x20;
260 : if ((ku < 1 || ku > 92) || (ten < 1 || ten > 94)) {
261 : *chPtr = (Tcl_UniChar) byte;
262 : return 1;
263 : }
264 :
265 : *chPtr = (Tcl_UniChar) UnicodeTbl[ku - 1][ten - 1];
266 : return 2;
267 : } else
268 : #endif /* JISX0208 */
269 174 : if (byte < 0xC0) {
270 : /* Handles properly formed UTF-8 characters between
271 : * 0x01 and 0x7F. Also treats \0 and naked trail
272 : * bytes 0x80 to 0xBF as valid characters representing
273 : * themselves.
274 : */
275 :
276 174 : *chPtr = (Tcl_UniChar) byte;
277 174 : return 1;
278 0 : } else if (byte < 0xE0) {
279 0 : if ((str[1] & 0xC0) == 0x80) {
280 : /* Two-byte-character lead-byte followed by a trail-byte. */
281 :
282 0 : *chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6) | (str[1] & 0x3F));
283 0 : return 2;
284 : }
285 : /*
286 : * A two-byte-character lead-byte not followed by trail-byte
287 : * represents itself.
288 : */
289 :
290 0 : *chPtr = (Tcl_UniChar) byte;
291 0 : return 1;
292 0 : } else if (byte < 0xF0) {
293 0 : if (((str[1] & 0xC0) == 0x80) && ((str[2] & 0xC0) == 0x80)) {
294 : /* Three-byte-character lead byte followed by two trail bytes. */
295 :
296 0 : *chPtr = (Tcl_UniChar) (((byte & 0x0F) << 12) | ((str[1] & 0x3F) << 6) | (str[2] & 0x3F));
297 0 : return 3;
298 : }
299 : /* A three-byte-character lead-byte not followed by two trail-bytes represents itself. */
300 :
301 0 : *chPtr = (Tcl_UniChar) byte;
302 0 : return 1;
303 : }
304 : #if TCL_UTF_MAX > 3
305 : else {
306 : int ch, total, trail;
307 :
308 : total = totalBytes[byte];
309 : trail = total - 1;
310 :
311 : if (trail > 0) {
312 : ch = byte & (0x3F >> trail);
313 : do {
314 : str++;
315 : if ((*str & 0xC0) != 0x80) {
316 : *chPtr = byte;
317 : return 1;
318 : }
319 : ch <<= 6;
320 : ch |= (*str & 0x3F);
321 : trail--;
322 : } while (trail > 0);
323 : *chPtr = ch;
324 : return total;
325 : }
326 : }
327 : #endif
328 :
329 0 : *chPtr = (Tcl_UniChar) byte;
330 0 : return 1;
331 : }
332 :
333 : /********************************************************************/
334 : /* font cache functions */
335 :
336 : static int fontTest (void *element, void *key)
337 17 : {
338 17 : font_t *a = (font_t *) element;
339 17 : fontkey_t *b = (fontkey_t *) key;
340 :
341 17 : return (strcmp (a->fontlist, b->fontlist) == 0);
342 : }
343 :
344 : static void *fontFetch (char **error, void *key)
345 5 : {
346 : font_t *a;
347 5 : fontkey_t *b = (fontkey_t *) key;
348 : int n;
349 5 : int font_found = 0;
350 : unsigned short platform, encoding;
351 : char *fontsearchpath, *fontlist;
352 : char fullname[MAXPATHLEN], cur_dir[MAXPATHLEN];
353 5 : char *name, *path=NULL, *dir;
354 : char *strtok_ptr;
355 : FT_Error err;
356 5 : FT_CharMap found = 0;
357 : FT_CharMap charmap;
358 :
359 5 : a = (font_t *) gdPMalloc(sizeof(font_t));
360 5 : a->fontlist = gdPEstrdup(b->fontlist);
361 5 : a->library = b->library;
362 :
363 : /*
364 : * Search the pathlist for any of a list of font names.
365 : */
366 5 : fontsearchpath = getenv ("GDFONTPATH");
367 5 : if (!fontsearchpath) {
368 5 : fontsearchpath = DEFAULT_FONTPATH;
369 : }
370 5 : fontlist = gdEstrdup(a->fontlist);
371 :
372 : /*
373 : * Must use gd_strtok_r else pointer corrupted by strtok in nested loop.
374 : */
375 5 : for (name = gd_strtok_r (fontlist, LISTSEPARATOR, &strtok_ptr); name; name = gd_strtok_r (0, LISTSEPARATOR, &strtok_ptr)) {
376 : /* make a fresh copy each time - strtok corrupts it. */
377 5 : path = gdEstrdup (fontsearchpath);
378 :
379 : /* if name is an absolute filename then test directly */
380 : #ifdef NETWARE
381 : if (*name == '/' || (name[0] != 0 && strstr(name, ":/"))) {
382 : #else
383 5 : if (*name == '/' || (name[0] != 0 && name[1] == ':' && (name[2] == '/' || name[2] == '\\'))) {
384 : #endif
385 5 : snprintf(fullname, sizeof(fullname) - 1, "%s", name);
386 5 : if (access(fullname, R_OK) == 0) {
387 5 : font_found++;
388 5 : break;
389 : }
390 : }
391 0 : for (dir = strtok (path, PATHSEPARATOR); dir; dir = strtok (0, PATHSEPARATOR)) {
392 0 : if (!strcmp(dir, ".")) {
393 : TSRMLS_FETCH();
394 : #if HAVE_GETCWD
395 0 : dir = VCWD_GETCWD(cur_dir, MAXPATHLEN);
396 : #elif HAVE_GETWD
397 : dir = VCWD_GETWD(cur_dir);
398 : #endif
399 0 : if (!dir) {
400 0 : continue;
401 : }
402 : }
403 :
404 : #define GD_CHECK_FONT_PATH(ext) \
405 : snprintf(fullname, sizeof(fullname) - 1, "%s/%s%s", dir, name, ext); \
406 : if (access(fullname, R_OK) == 0) { \
407 : font_found++; \
408 : break; \
409 : } \
410 :
411 0 : GD_CHECK_FONT_PATH("");
412 0 : GD_CHECK_FONT_PATH(".ttf");
413 0 : GD_CHECK_FONT_PATH(".pfa");
414 0 : GD_CHECK_FONT_PATH(".pfb");
415 0 : GD_CHECK_FONT_PATH(".dfont");
416 : }
417 0 : gdFree(path);
418 0 : path = NULL;
419 0 : if (font_found) {
420 0 : break;
421 : }
422 : }
423 :
424 5 : if (path) {
425 5 : gdFree(path);
426 : }
427 :
428 5 : gdFree(fontlist);
429 :
430 5 : if (!font_found) {
431 0 : gdPFree(a->fontlist);
432 0 : gdPFree(a);
433 0 : *error = "Could not find/open font";
434 0 : return NULL;
435 : }
436 :
437 5 : err = FT_New_Face (*b->library, fullname, 0, &a->face);
438 5 : if (err) {
439 0 : gdPFree(a->fontlist);
440 0 : gdPFree(a);
441 0 : *error = "Could not read font";
442 0 : return NULL;
443 : }
444 :
445 : /* FIXME - This mapping stuff is imcomplete - where is the spec? */
446 : /* EAM - It's worse than that. It's pointless to match character encodings here.
447 : * As currently written, the stored a->face->charmap only matches one of
448 : * the actual charmaps and we cannot know at this stage if it is the right
449 : * one. We should just skip all this stuff, and check in gdImageStringFTEx
450 : * if some particular charmap is preferred and if so whether it is held in
451 : * one of the a->face->charmaps[0..num_charmaps].
452 : * And why is it so bad not to find any recognized charmap? The user may
453 : * still know what mapping to use, even if we do not. In that case we can
454 : * just use the map in a->face->charmaps[num_charmaps] and be done with it.
455 : */
456 :
457 5 : a->have_char_map_unicode = 0;
458 5 : a->have_char_map_big5 = 0;
459 5 : a->have_char_map_sjis = 0;
460 5 : a->have_char_map_apple_roman = 0;
461 19 : for (n = 0; n < a->face->num_charmaps; n++) {
462 14 : charmap = a->face->charmaps[n];
463 14 : platform = charmap->platform_id;
464 14 : encoding = charmap->encoding_id;
465 :
466 : /* EAM DEBUG - Newer versions of libfree2 make it easier by defining encodings */
467 : #if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2)))
468 14 : if (charmap->encoding == FT_ENCODING_MS_SYMBOL
469 : || charmap->encoding == FT_ENCODING_ADOBE_CUSTOM
470 : || charmap->encoding == FT_ENCODING_ADOBE_STANDARD) {
471 0 : a->have_char_map_unicode = 1;
472 0 : found = charmap;
473 0 : a->face->charmap = charmap;
474 0 : return (void *)a;
475 : }
476 : #endif /* Freetype 2.1.3 or better */
477 : /* EAM DEBUG */
478 :
479 23 : if ((platform == 3 && encoding == 1) /* Windows Unicode */
480 : || (platform == 3 && encoding == 0) /* Windows Symbol */
481 : || (platform == 2 && encoding == 1) /* ISO Unicode */
482 : || (platform == 0))
483 : { /* Apple Unicode */
484 9 : a->have_char_map_unicode = 1;
485 9 : found = charmap;
486 5 : } else if (platform == 3 && encoding == 4) { /* Windows Big5 */
487 0 : a->have_char_map_big5 = 1;
488 0 : found = charmap;
489 5 : } else if (platform == 3 && encoding == 2) { /* Windows Sjis */
490 0 : a->have_char_map_sjis = 1;
491 0 : found = charmap;
492 5 : } else if ((platform == 1 && encoding == 0) /* Apple Roman */
493 : || (platform == 2 && encoding == 0))
494 : { /* ISO ASCII */
495 5 : a->have_char_map_apple_roman = 1;
496 5 : found = charmap;
497 : }
498 : }
499 5 : if (!found) {
500 0 : gdPFree(a->fontlist);
501 0 : gdPFree(a);
502 0 : *error = "Unable to find a CharMap that I can handle";
503 0 : return NULL;
504 : }
505 :
506 : /* 2.0.5: we should actually return this */
507 5 : a->face->charmap = found;
508 5 : return (void *) a;
509 : }
510 :
511 : static void fontRelease (void *element)
512 5 : {
513 5 : font_t *a = (font_t *) element;
514 :
515 5 : FT_Done_Face (a->face);
516 5 : gdPFree(a->fontlist);
517 5 : gdPFree((char *) element);
518 5 : }
519 :
520 : /********************************************************************/
521 : /* tweencolor cache functions */
522 :
523 : static int tweenColorTest (void *element, void *key)
524 35183 : {
525 35183 : tweencolor_t *a = (tweencolor_t *) element;
526 35183 : tweencolorkey_t *b = (tweencolorkey_t *) key;
527 :
528 35183 : return (a->pixel == b->pixel && a->bgcolor == b->bgcolor && a->fgcolor == b->fgcolor && a->im == b->im);
529 : }
530 :
531 : /*
532 : * Computes a color in im's color table that is part way between
533 : * the background and foreground colors proportional to the gray
534 : * pixel value in the range 0-NUMCOLORS. The fg and bg colors must already
535 : * be in the color table for palette images. For truecolor images the
536 : * returned value simply has an alpha component and gdImageAlphaBlend
537 : * does the work so that text can be alpha blended across a complex
538 : * background (TBB; and for real in 2.0.2).
539 : */
540 : static void * tweenColorFetch (char **error, void *key)
541 126 : {
542 : tweencolor_t *a;
543 126 : tweencolorkey_t *b = (tweencolorkey_t *) key;
544 : int pixel, npixel, bg, fg;
545 : gdImagePtr im;
546 :
547 126 : a = (tweencolor_t *) gdMalloc (sizeof (tweencolor_t));
548 126 : pixel = a->pixel = b->pixel;
549 126 : bg = a->bgcolor = b->bgcolor;
550 126 : fg = a->fgcolor = b->fgcolor;
551 126 : im = a->im = b->im;
552 :
553 : /* if fg is specified by a negative color idx, then don't antialias */
554 126 : if (fg < 0) {
555 0 : if ((pixel + pixel) >= NUMCOLORS) {
556 0 : a->tweencolor = -fg;
557 : } else {
558 0 : a->tweencolor = bg;
559 : }
560 : } else {
561 126 : npixel = NUMCOLORS - pixel;
562 126 : if (im->trueColor) {
563 : /* 2.0.1: use gdImageSetPixel to do the alpha blending work,
564 : * or to just store the alpha level. All we have to do here
565 : * is incorporate our knowledge of the percentage of this
566 : * pixel that is really "lit" by pushing the alpha value
567 : * up toward transparency in edge regions.
568 : */
569 0 : a->tweencolor = gdTrueColorAlpha(
570 : gdTrueColorGetRed(fg),
571 : gdTrueColorGetGreen(fg),
572 : gdTrueColorGetBlue(fg),
573 : gdAlphaMax - (gdTrueColorGetAlpha (fg) * pixel / NUMCOLORS));
574 : } else {
575 126 : a->tweencolor = gdImageColorResolve(im,
576 : (pixel * im->red[fg] + npixel * im->red[bg]) / NUMCOLORS,
577 : (pixel * im->green[fg] + npixel * im->green[bg]) / NUMCOLORS,
578 : (pixel * im->blue[fg] + npixel * im->blue[bg]) / NUMCOLORS);
579 : }
580 : }
581 126 : return (void *) a;
582 : }
583 :
584 : static void tweenColorRelease (void *element)
585 126 : {
586 126 : gdFree((char *) element);
587 126 : }
588 :
589 : /* draw_bitmap - transfers glyph bitmap to GD image */
590 : static char * gdft_draw_bitmap (gdCache_head_t *tc_cache, gdImage * im, int fg, FT_Bitmap bitmap, int pen_x, int pen_y)
591 119 : {
592 119 : unsigned char *pixel = NULL;
593 119 : int *tpixel = NULL;
594 : int x, y, row, col, pc, pcr;
595 :
596 : tweencolor_t *tc_elem;
597 : tweencolorkey_t tc_key;
598 :
599 : /* copy to image, mapping colors */
600 119 : tc_key.fgcolor = fg;
601 119 : tc_key.im = im;
602 : /* Truecolor version; does not require the cache */
603 119 : if (im->trueColor) {
604 239 : for (row = 0; row < bitmap.rows; row++) {
605 238 : pc = row * bitmap.pitch;
606 238 : pcr = pc;
607 238 : y = pen_y + row;
608 : /* clip if out of bounds */
609 : /* 2.0.16: clipping rectangle, not image bounds */
610 238 : if ((y > im->cy2) || (y < im->cy1)) {
611 : continue;
612 : }
613 23650 : for (col = 0; col < bitmap.width; col++, pc++) {
614 : int level;
615 23435 : if (bitmap.pixel_mode == ft_pixel_mode_grays) {
616 : /* Scale to 128 levels of alpha for gd use.
617 : * alpha 0 is opacity, so be sure to invert at the end
618 : */
619 23435 : level = (bitmap.buffer[pc] * gdAlphaMax / (bitmap.num_grays - 1));
620 0 : } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
621 : /* 2.0.5: mode_mono fix from Giuliano Pochini */
622 0 : level = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? gdAlphaTransparent : gdAlphaOpaque;
623 : } else {
624 0 : return "Unsupported ft_pixel_mode";
625 : }
626 23435 : if ((fg >= 0) && (im->trueColor)) {
627 : /* Consider alpha in the foreground color itself to be an
628 : * upper bound on how opaque things get, when truecolor is
629 : * available. Without truecolor this results in far too many
630 : * color indexes.
631 : */
632 23435 : level = level * (gdAlphaMax - gdTrueColorGetAlpha(fg)) / gdAlphaMax;
633 : }
634 23435 : level = gdAlphaMax - level;
635 23435 : x = pen_x + col;
636 : /* clip if out of bounds */
637 : /* 2.0.16: clip to clipping rectangle, Matt McNabb */
638 23435 : if ((x > im->cx2) || (x < im->cx1)) {
639 : continue;
640 : }
641 : /* get pixel location in gd buffer */
642 23435 : tpixel = &im->tpixels[y][x];
643 23435 : if (fg < 0) {
644 0 : if (level < (gdAlphaMax / 2)) {
645 0 : *tpixel = -fg;
646 : }
647 : } else {
648 23435 : if (im->alphaBlendingFlag) {
649 23435 : *tpixel = gdAlphaBlend(*tpixel, (level << 24) + (fg & 0xFFFFFF));
650 : } else {
651 0 : *tpixel = (level << 24) + (fg & 0xFFFFFF);
652 : }
653 : }
654 : }
655 : }
656 1 : return (char *) NULL;
657 : }
658 : /* Non-truecolor case, restored to its more or less original form */
659 2795 : for (row = 0; row < bitmap.rows; row++) {
660 : int pcr;
661 2677 : pc = row * bitmap.pitch;
662 2677 : pcr = pc;
663 2677 : if (bitmap.pixel_mode==ft_pixel_mode_mono) {
664 0 : pc *= 8; /* pc is measured in bits for monochrome images */
665 : }
666 2677 : y = pen_y + row;
667 :
668 : /* clip if out of bounds */
669 2677 : if (y >= im->sy || y < 0) {
670 : continue;
671 : }
672 :
673 77854 : for (col = 0; col < bitmap.width; col++, pc++) {
674 75200 : if (bitmap.pixel_mode == ft_pixel_mode_grays) {
675 : /*
676 : * Round to NUMCOLORS levels of antialiasing for
677 : * index color images since only 256 colors are
678 : * available.
679 : */
680 75200 : tc_key.pixel = ((bitmap.buffer[pc] * NUMCOLORS) + bitmap.num_grays / 2) / (bitmap.num_grays - 1);
681 0 : } else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
682 0 : tc_key.pixel = ((bitmap.buffer[pc / 8] << (pc % 8)) & 128) ? NUMCOLORS : 0;
683 : /* 2.0.5: mode_mono fix from Giuliano Pochini */
684 0 : tc_key.pixel = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? NUMCOLORS : 0;
685 : } else {
686 0 : return "Unsupported ft_pixel_mode";
687 : }
688 75200 : if (tc_key.pixel > 0) { /* if not background */
689 25166 : x = pen_x + col;
690 :
691 : /* clip if out of bounds */
692 25166 : if (x >= im->sx || x < 0) {
693 : continue;
694 : }
695 : /* get pixel location in gd buffer */
696 24998 : pixel = &im->pixels[y][x];
697 24998 : if (tc_key.pixel == NUMCOLORS) {
698 : /* use fg color directly. gd 2.0.2: watch out for
699 : * negative indexes (thanks to David Marwood).
700 : */
701 15343 : *pixel = (fg < 0) ? -fg : fg;
702 : } else {
703 : /* find antialised color */
704 9655 : tc_key.bgcolor = *pixel;
705 9655 : tc_elem = (tweencolor_t *) gdCacheGet(tc_cache, &tc_key);
706 9655 : *pixel = tc_elem->tweencolor;
707 : }
708 : }
709 : }
710 : }
711 118 : return (char *) NULL;
712 : }
713 :
714 : static int
715 : gdroundupdown (FT_F26Dot6 v1, int updown)
716 176 : {
717 176 : return (!updown) ? (v1 < 0 ? ((v1 - 63) >> 6) : v1 >> 6) : (v1 > 0 ? ((v1 + 63) >> 6) : v1 >> 6);
718 : }
719 :
720 : void gdFontCacheShutdown()
721 13584 : {
722 : gdMutexLock(gdFontCacheMutex);
723 :
724 13584 : if (fontCache) {
725 5 : gdCacheDelete(fontCache);
726 5 : fontCache = NULL;
727 5 : FT_Done_FreeType(library);
728 : }
729 :
730 : gdMutexUnlock(gdFontCacheMutex);
731 13584 : }
732 :
733 : void gdFreeFontCache()
734 0 : {
735 0 : gdFontCacheShutdown();
736 0 : }
737 :
738 : void gdFontCacheMutexSetup()
739 13565 : {
740 : gdMutexSetup(gdFontCacheMutex);
741 13565 : }
742 :
743 : void gdFontCacheMutexShutdown()
744 13597 : {
745 : gdMutexShutdown(gdFontCacheMutex);
746 13597 : }
747 :
748 : int gdFontCacheSetup(void)
749 5 : {
750 5 : if (fontCache) {
751 : /* Already set up */
752 0 : return 0;
753 : }
754 5 : if (FT_Init_FreeType(&library)) {
755 0 : return -1;
756 : }
757 5 : fontCache = gdCacheCreate (FONTCACHESIZE, fontTest, fontFetch, fontRelease);
758 5 : return 0;
759 : }
760 :
761 :
762 : /********************************************************************/
763 : /* gdImageStringFT - render a utf8 string onto a gd image */
764 :
765 : char *
766 : gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
767 : double ptsize, double angle, int x, int y, char *string)
768 17 : {
769 17 : return gdImageStringFTEx(im, brect, fg, fontlist, ptsize, angle, x, y, string, 0);
770 : }
771 :
772 : char *
773 : gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsize, double angle, int x, int y, char *string, gdFTStringExtraPtr strex)
774 22 : {
775 : FT_BBox bbox, glyph_bbox;
776 : FT_Matrix matrix;
777 : FT_Vector pen, delta, penf;
778 : FT_Face face;
779 : FT_Glyph image;
780 : FT_GlyphSlot slot;
781 : FT_Bool use_kerning;
782 : FT_UInt glyph_index, previous;
783 22 : double sin_a = sin (angle);
784 22 : double cos_a = cos (angle);
785 22 : int len, i = 0, ch;
786 22 : int x1 = 0, y1 = 0;
787 22 : int xb = x, yb = y;
788 22 : int yd = 0;
789 : font_t *font;
790 : fontkey_t fontkey;
791 : char *next;
792 22 : char *tmpstr = NULL;
793 22 : int render = (im && (im->trueColor || (fg <= 255 && fg >= -255)));
794 : FT_BitmapGlyph bm;
795 : /* 2.0.13: Bob Ostermann: don't force autohint, that's just for testing freetype and doesn't look as good */
796 22 : int render_mode = FT_LOAD_DEFAULT;
797 : int m, mfound;
798 : /* Now tuneable thanks to Wez Furlong */
799 22 : double linespace = LINESPACE;
800 : /* 2.0.6: put this declaration with the other declarations! */
801 : /*
802 : * make a new tweenColorCache on every call
803 : * because caching colormappings between calls
804 : * is not safe. If the im-pointer points to a
805 : * brand new image, the cache gives out bogus
806 : * colorindexes. -- 27.06.2001 <krisku@arrak.fi>
807 : */
808 : gdCache_head_t *tc_cache;
809 : /* Tuneable horizontal and vertical resolution in dots per inch */
810 : int hdpi, vdpi;
811 :
812 22 : if (strex && ((strex->flags & gdFTEX_LINESPACE) == gdFTEX_LINESPACE)) {
813 0 : linespace = strex->linespacing;
814 : }
815 22 : tc_cache = gdCacheCreate(TWEENCOLORCACHESIZE, tweenColorTest, tweenColorFetch, tweenColorRelease);
816 :
817 : /***** initialize font library and font cache on first call ******/
818 :
819 : gdMutexLock(gdFontCacheMutex);
820 22 : if (!fontCache) {
821 5 : if (gdFontCacheSetup() != 0) {
822 0 : gdCacheDelete(tc_cache);
823 : gdMutexUnlock(gdFontCacheMutex);
824 0 : return "Failure to initialize font library";
825 : }
826 : }
827 : /*****/
828 :
829 : /* get the font (via font cache) */
830 22 : fontkey.fontlist = fontlist;
831 22 : fontkey.library = &library;
832 22 : font = (font_t *) gdCacheGet (fontCache, &fontkey);
833 22 : if (!font) {
834 0 : gdCacheDelete(tc_cache);
835 : gdMutexUnlock(gdFontCacheMutex);
836 0 : return fontCache->error;
837 : }
838 22 : face = font->face; /* shortcut */
839 22 : slot = face->glyph; /* shortcut */
840 :
841 : /*
842 : * Added hdpi and vdpi to support images at non-screen resolutions, i.e. 300 dpi TIFF,
843 : * or 100h x 50v dpi FAX format. 2.0.23.
844 : * 2004/02/27 Mark Shackelford, mark.shackelford@acs-inc.com
845 : */
846 22 : hdpi = GD_RESOLUTION;
847 22 : vdpi = GD_RESOLUTION;
848 22 : if (strex && (strex->flags & gdFTEX_RESOLUTION)) {
849 0 : hdpi = strex->hdpi;
850 0 : vdpi = strex->vdpi;
851 : }
852 :
853 22 : if (FT_Set_Char_Size(face, 0, (FT_F26Dot6) (ptsize * 64), hdpi, vdpi)) {
854 0 : gdCacheDelete(tc_cache);
855 : gdMutexUnlock(gdFontCacheMutex);
856 0 : return "Could not set character size";
857 : }
858 :
859 22 : matrix.xx = (FT_Fixed) (cos_a * (1 << 16));
860 22 : matrix.yx = (FT_Fixed) (sin_a * (1 << 16));
861 22 : matrix.xy = -matrix.yx;
862 22 : matrix.yy = matrix.xx;
863 :
864 22 : penf.x = penf.y = 0; /* running position of non-rotated string */
865 22 : pen.x = pen.y = 0; /* running position of rotated string */
866 22 : bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
867 :
868 22 : use_kerning = FT_HAS_KERNING (face);
869 22 : previous = 0;
870 22 : if (fg < 0) {
871 3 : render_mode |= FT_LOAD_MONOCHROME;
872 : }
873 : /* 2.0.12: allow explicit specification of the preferred map;
874 : * but we still fall back if it is not available.
875 : */
876 22 : m = gdFTEX_Unicode;
877 22 : if (strex && (strex->flags & gdFTEX_CHARMAP)) {
878 0 : m = strex->charmap;
879 : }
880 : /* Try all three types of maps, but start with the specified one */
881 22 : mfound = 0;
882 22 : for (i = 0; i < 3; i++) {
883 22 : switch (m) {
884 : case gdFTEX_Unicode:
885 22 : if (font->have_char_map_unicode) {
886 22 : mfound = 1;
887 : }
888 22 : break;
889 : case gdFTEX_Shift_JIS:
890 0 : if (font->have_char_map_sjis) {
891 0 : mfound = 1;
892 : }
893 0 : break;
894 : case gdFTEX_Big5:
895 : /* This was the 'else' case, we can't really 'detect' it */
896 0 : mfound = 1;
897 : break;
898 : }
899 22 : if (mfound) {
900 22 : break;
901 : }
902 0 : m++;
903 0 : m %= 3;
904 : }
905 22 : if (!mfound) {
906 : /* No character set found! */
907 : gdMutexUnlock(gdFontCacheMutex);
908 0 : return "No character set found";
909 : }
910 :
911 : #ifndef JISX0208
912 22 : if (font->have_char_map_sjis) {
913 : #endif
914 0 : tmpstr = (char *) gdMalloc(BUFSIZ);
915 0 : any2eucjp(tmpstr, string, BUFSIZ);
916 0 : next = tmpstr;
917 : #ifndef JISX0208
918 : } else {
919 22 : next = string;
920 : }
921 : #endif
922 :
923 22 : i = 0;
924 221 : while (*next) {
925 178 : ch = *next;
926 :
927 : /* carriage returns */
928 178 : if (ch == '\r') {
929 0 : penf.x = 0;
930 0 : x1 = (int)(- penf.y * sin_a + 32) / 64;
931 0 : y1 = (int)(- penf.y * cos_a + 32) / 64;
932 0 : pen.x = pen.y = 0;
933 0 : previous = 0; /* clear kerning flag */
934 0 : next++;
935 0 : continue;
936 : }
937 : /* newlines */
938 178 : if (ch == '\n') {
939 4 : if (!*(++next)) break;
940 : /* 2.0.13: reset penf.x. Christopher J. Grayce */
941 3 : penf.x = 0;
942 3 : penf.y -= (long)(face->size->metrics.height * linespace);
943 3 : penf.y = (penf.y - 32) & -64; /* round to next pixel row */
944 3 : x1 = (int)(- penf.y * sin_a + 32) / 64;
945 3 : y1 = (int)(- penf.y * cos_a + 32) / 64;
946 3 : xb = x + x1;
947 3 : yb = y + y1;
948 3 : yd = 0;
949 3 : pen.x = pen.y = 0;
950 3 : previous = 0; /* clear kerning flag */
951 3 : continue;
952 : }
953 :
954 : /* EAM DEBUG */
955 : #if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2)))
956 174 : if (font->face->family_name && font->face->charmap->encoding &&
957 : font->face->charmap->encoding == FT_ENCODING_MS_SYMBOL && strcmp(font->face->family_name, "Symbol") == 0) {
958 : /* I do not know the significance of the constant 0xf000.
959 : * It was determined by inspection of the character codes
960 : * stored in Microsoft font symbol.
961 : * Added by Pierre (pajoye@php.net):
962 : * Convert to the Symbol glyph range only for a Symbol family member
963 : */
964 0 : len = gdTcl_UtfToUniChar (next, &ch);
965 0 : ch |= 0xf000;
966 0 : next += len;
967 : } else
968 : #endif /* Freetype 2.1 or better */
969 : /* EAM DEBUG */
970 :
971 174 : switch (m) {
972 : case gdFTEX_Unicode:
973 174 : if (font->have_char_map_unicode) {
974 : /* use UTF-8 mapping from ASCII */
975 174 : len = gdTcl_UtfToUniChar(next, &ch);
976 174 : next += len;
977 : }
978 174 : break;
979 : case gdFTEX_Shift_JIS:
980 0 : if (font->have_char_map_sjis) {
981 : unsigned char c;
982 : int jiscode;
983 0 : c = *next;
984 0 : if (0xA1 <= c && c <= 0xFE) {
985 0 : next++;
986 0 : jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F);
987 :
988 0 : ch = (jiscode >> 8) & 0xFF;
989 0 : jiscode &= 0xFF;
990 :
991 0 : if (ch & 1) {
992 0 : jiscode += 0x40 - 0x21;
993 : } else {
994 0 : jiscode += 0x9E - 0x21;
995 : }
996 :
997 0 : if (jiscode >= 0x7F) {
998 0 : jiscode++;
999 : }
1000 0 : ch = (ch - 0x21) / 2 + 0x81;
1001 0 : if (ch >= 0xA0) {
1002 0 : ch += 0x40;
1003 : }
1004 :
1005 0 : ch = (ch << 8) + jiscode;
1006 : } else {
1007 0 : ch = c & 0xFF; /* don't extend sign */
1008 : }
1009 0 : if (*next) next++;
1010 : }
1011 0 : break;
1012 : case gdFTEX_Big5: {
1013 : /*
1014 : * Big 5 mapping:
1015 : * use "JIS-8 half-width katakana" coding from 8-bit characters. Ref:
1016 : * ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs
1017 : */
1018 0 : ch = (*next) & 0xFF; /* don't extend sign */
1019 0 : next++;
1020 0 : if (ch >= 161 /* first code of JIS-8 pair */
1021 : && *next) { /* don't advance past '\0' */
1022 : /* TBB: Fix from Kwok Wah On: & 255 needed */
1023 0 : ch = (ch * 256) + ((*next) & 255);
1024 0 : next++;
1025 : }
1026 : }
1027 : break;
1028 : }
1029 :
1030 : /* set rotation transform */
1031 174 : FT_Set_Transform(face, &matrix, NULL);
1032 : /* Convert character code to glyph index */
1033 174 : glyph_index = FT_Get_Char_Index(face, ch);
1034 :
1035 : /* retrieve kerning distance and move pen position */
1036 174 : if (use_kerning && previous && glyph_index) {
1037 0 : FT_Get_Kerning(face, previous, glyph_index, ft_kerning_default, &delta);
1038 0 : pen.x += delta.x;
1039 0 : penf.x += delta.x;
1040 : }
1041 :
1042 : /* load glyph image into the slot (erase previous one) */
1043 174 : if (FT_Load_Glyph(face, glyph_index, render_mode)) {
1044 0 : if (tmpstr) {
1045 0 : gdFree(tmpstr);
1046 : }
1047 0 : gdCacheDelete(tc_cache);
1048 : gdMutexUnlock(gdFontCacheMutex);
1049 0 : return "Problem loading glyph";
1050 : }
1051 :
1052 : /* transform glyph image */
1053 174 : FT_Get_Glyph(slot, &image);
1054 174 : if (brect) { /* only if need brect */
1055 174 : FT_Glyph_Get_CBox(image, ft_glyph_bbox_gridfit, &glyph_bbox);
1056 174 : glyph_bbox.xMin += penf.x;
1057 174 : glyph_bbox.yMin += penf.y;
1058 174 : glyph_bbox.xMax += penf.x;
1059 174 : glyph_bbox.yMax += penf.y;
1060 174 : if (ch == ' ') { /* special case for trailing space */
1061 4 : glyph_bbox.xMax += slot->metrics.horiAdvance;
1062 : }
1063 174 : if (!i) { /* if first character, init BB corner values */
1064 22 : yd = slot->metrics.height - slot->metrics.horiBearingY;
1065 22 : bbox.xMin = glyph_bbox.xMin;
1066 22 : bbox.yMin = glyph_bbox.yMin;
1067 22 : bbox.xMax = glyph_bbox.xMax;
1068 22 : bbox.yMax = glyph_bbox.yMax;
1069 : } else {
1070 : FT_Pos desc;
1071 :
1072 152 : if ( (desc = (slot->metrics.height - slot->metrics.horiBearingY)) > yd) {
1073 18 : yd = desc;
1074 : }
1075 152 : if (bbox.xMin > glyph_bbox.xMin) {
1076 1 : bbox.xMin = glyph_bbox.xMin;
1077 : }
1078 152 : if (bbox.yMin > glyph_bbox.yMin) {
1079 16 : bbox.yMin = glyph_bbox.yMin;
1080 : }
1081 152 : if (bbox.xMax < glyph_bbox.xMax) {
1082 128 : bbox.xMax = glyph_bbox.xMax;
1083 : }
1084 152 : if (bbox.yMax < glyph_bbox.yMax) {
1085 11 : bbox.yMax = glyph_bbox.yMax;
1086 : }
1087 : }
1088 174 : i++;
1089 : }
1090 :
1091 174 : if (render) {
1092 119 : if (image->format != ft_glyph_format_bitmap && FT_Glyph_To_Bitmap(&image, ft_render_mode_normal, 0, 1)) {
1093 0 : FT_Done_Glyph(image);
1094 0 : if (tmpstr) {
1095 0 : gdFree(tmpstr);
1096 : }
1097 0 : gdCacheDelete(tc_cache);
1098 : gdMutexUnlock(gdFontCacheMutex);
1099 0 : return "Problem rendering glyph";
1100 : }
1101 :
1102 : /* now, draw to our target surface */
1103 119 : bm = (FT_BitmapGlyph) image;
1104 119 : 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);
1105 : }
1106 :
1107 : /* record current glyph index for kerning */
1108 174 : previous = glyph_index;
1109 :
1110 : /* increment pen position */
1111 174 : pen.x += image->advance.x >> 10;
1112 174 : pen.y -= image->advance.y >> 10;
1113 :
1114 174 : penf.x += slot->metrics.horiAdvance;
1115 :
1116 174 : FT_Done_Glyph(image);
1117 : }
1118 :
1119 22 : if (brect) { /* only if need brect */
1120 : /* For perfect rounding, must get sin(a + pi/4) and sin(a - pi/4). */
1121 22 : double d1 = sin (angle + 0.78539816339744830962);
1122 22 : double d2 = sin (angle - 0.78539816339744830962);
1123 :
1124 : /* make the center of rotation at (0, 0) */
1125 : FT_BBox normbox;
1126 :
1127 22 : normbox.xMin = 0;
1128 22 : normbox.yMin = 0;
1129 22 : normbox.xMax = bbox.xMax - bbox.xMin;
1130 22 : normbox.yMax = bbox.yMax - bbox.yMin;
1131 :
1132 22 : brect[0] = brect[2] = brect[4] = brect[6] = (int) (yd * sin_a);
1133 22 : brect[1] = brect[3] = brect[5] = brect[7] = (int)(- yd * cos_a);
1134 :
1135 : /* rotate bounding rectangle */
1136 22 : brect[0] += (int) (normbox.xMin * cos_a - normbox.yMin * sin_a);
1137 22 : brect[1] += (int) (normbox.xMin * sin_a + normbox.yMin * cos_a);
1138 22 : brect[2] += (int) (normbox.xMax * cos_a - normbox.yMin * sin_a);
1139 22 : brect[3] += (int) (normbox.xMax * sin_a + normbox.yMin * cos_a);
1140 22 : brect[4] += (int) (normbox.xMax * cos_a - normbox.yMax * sin_a);
1141 22 : brect[5] += (int) (normbox.xMax * sin_a + normbox.yMax * cos_a);
1142 22 : brect[6] += (int) (normbox.xMin * cos_a - normbox.yMax * sin_a);
1143 22 : brect[7] += (int) (normbox.xMin * sin_a + normbox.yMax * cos_a);
1144 :
1145 : /* scale, round and offset brect */
1146 22 : brect[0] = xb + gdroundupdown(brect[0], d2 > 0);
1147 22 : brect[1] = yb - gdroundupdown(brect[1], d1 < 0);
1148 22 : brect[2] = xb + gdroundupdown(brect[2], d1 > 0);
1149 22 : brect[3] = yb - gdroundupdown(brect[3], d2 > 0);
1150 22 : brect[4] = xb + gdroundupdown(brect[4], d2 < 0);
1151 22 : brect[5] = yb - gdroundupdown(brect[5], d1 > 0);
1152 22 : brect[6] = xb + gdroundupdown(brect[6], d1 < 0);
1153 22 : brect[7] = yb - gdroundupdown(brect[7], d2 < 0);
1154 : }
1155 :
1156 22 : if (tmpstr) {
1157 0 : gdFree(tmpstr);
1158 : }
1159 22 : gdCacheDelete(tc_cache);
1160 : gdMutexUnlock(gdFontCacheMutex);
1161 22 : return (char *) NULL;
1162 : }
1163 :
1164 : #endif /* HAVE_LIBFREETYPE */
|