xref: /PHP-7.4/ext/gd/libgd/gdft.c (revision ba2b50d0)
17a8cade3SRasmus Lerdorf 
27a8cade3SRasmus Lerdorf /********************************************/
37a8cade3SRasmus Lerdorf /* gd interface to freetype library         */
47a8cade3SRasmus Lerdorf /*                                          */
561026e39SIlia Alshanetsky /* John Ellson   ellson@graphviz.org        */
67a8cade3SRasmus Lerdorf /********************************************/
77a8cade3SRasmus Lerdorf 
87a8cade3SRasmus Lerdorf #include <stdio.h>
97a8cade3SRasmus Lerdorf #include <stdlib.h>
107a8cade3SRasmus Lerdorf #include <string.h>
117a8cade3SRasmus Lerdorf #include <math.h>
127a8cade3SRasmus Lerdorf #include "gd.h"
137a8cade3SRasmus Lerdorf #include "gdhelpers.h"
147a8cade3SRasmus Lerdorf 
157a8cade3SRasmus Lerdorf #ifndef MSWIN32
167a8cade3SRasmus Lerdorf #include <unistd.h>
177a8cade3SRasmus Lerdorf #else
18a24534a1SRasmus Lerdorf #include <io.h>
19085ceca8SWez Furlong #ifndef R_OK
20085ceca8SWez Furlong # define R_OK 04			/* Needed in Windows */
21085ceca8SWez Furlong #endif
227a8cade3SRasmus Lerdorf #endif
2302fcdec6SIlia Alshanetsky 
24b9c0c918SMarcus Boerger #ifdef WIN32
256b0b7e3fSFrank M. Kromann #define access _access
2602fcdec6SIlia Alshanetsky #ifndef R_OK
2702fcdec6SIlia Alshanetsky #define R_OK 2
2802fcdec6SIlia Alshanetsky #endif
29b9c0c918SMarcus Boerger #endif
307a8cade3SRasmus Lerdorf 
317a8cade3SRasmus Lerdorf /* number of antialised colors for indexed bitmaps */
3265eb0d81SMarcus Boerger /* overwrite Windows GDI define in case of windows build */
3365eb0d81SMarcus Boerger #ifdef NUMCOLORS
3465eb0d81SMarcus Boerger #undef NUMCOLORS
3565eb0d81SMarcus Boerger #endif
367a8cade3SRasmus Lerdorf #define NUMCOLORS 8
377a8cade3SRasmus Lerdorf 
387a8cade3SRasmus Lerdorf char *
gdImageStringTTF(gdImage * im,int * brect,int fg,char * fontlist,double ptsize,double angle,int x,int y,char * string)397a8cade3SRasmus Lerdorf gdImageStringTTF (gdImage * im, int *brect, int fg, char *fontlist,
407a8cade3SRasmus Lerdorf 		  double ptsize, double angle, int x, int y, char *string)
417a8cade3SRasmus Lerdorf {
4236db28c8SIlia Alshanetsky 	/* 2.0.6: valid return */
4336db28c8SIlia Alshanetsky 	return gdImageStringFT (im, brect, fg, fontlist, ptsize, angle, x, y, string);
447a8cade3SRasmus Lerdorf }
457a8cade3SRasmus Lerdorf 
467a8cade3SRasmus Lerdorf #ifndef HAVE_LIBFREETYPE
4792514865SIlia Alshanetsky char *
gdImageStringFTEx(gdImage * im,int * brect,int fg,char * fontlist,double ptsize,double angle,int x,int y,char * string,gdFTStringExtraPtr strex)4892514865SIlia Alshanetsky gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist,
4992514865SIlia Alshanetsky 		 double ptsize, double angle, int x, int y, char *string,
5092514865SIlia Alshanetsky 		 gdFTStringExtraPtr strex)
5192514865SIlia Alshanetsky {
5236db28c8SIlia Alshanetsky 	return "libgd was not built with FreeType font support\n";
5392514865SIlia Alshanetsky }
5492514865SIlia Alshanetsky 
557a8cade3SRasmus Lerdorf char *
gdImageStringFT(gdImage * im,int * brect,int fg,char * fontlist,double ptsize,double angle,int x,int y,char * string)567a8cade3SRasmus Lerdorf gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
577a8cade3SRasmus Lerdorf 		 double ptsize, double angle, int x, int y, char *string)
587a8cade3SRasmus Lerdorf {
5936db28c8SIlia Alshanetsky 	return "libgd was not built with FreeType font support\n";
607a8cade3SRasmus Lerdorf }
617a8cade3SRasmus Lerdorf #else
627a8cade3SRasmus Lerdorf 
637a8cade3SRasmus Lerdorf #include "gdcache.h"
640f6cafa7SRasmus Lerdorf #include <ft2build.h>
650f6cafa7SRasmus Lerdorf #include FT_FREETYPE_H
6653e56151SIlia Alshanetsky #include FT_GLYPH_H
677a8cade3SRasmus Lerdorf 
687a8cade3SRasmus Lerdorf /* number of fonts cached before least recently used is replaced */
697a8cade3SRasmus Lerdorf #define FONTCACHESIZE 6
707a8cade3SRasmus Lerdorf 
717a8cade3SRasmus Lerdorf /* number of antialias color lookups cached */
727a8cade3SRasmus Lerdorf #define TWEENCOLORCACHESIZE 32
737a8cade3SRasmus Lerdorf 
747a8cade3SRasmus Lerdorf /*
7536db28c8SIlia Alshanetsky  * Line separation as a factor of font height.
7636db28c8SIlia Alshanetsky  *      No space between if LINESPACE = 1.00
777a8cade3SRasmus Lerdorf  *      Line separation will be rounded up to next pixel row.
787a8cade3SRasmus Lerdorf  */
797a8cade3SRasmus Lerdorf #define LINESPACE 1.05
807a8cade3SRasmus Lerdorf 
817a8cade3SRasmus Lerdorf /*
827a8cade3SRasmus Lerdorf  * The character (space) used to separate alternate fonts in the
838b079070SIlia Alshanetsky  * fontlist parameter to gdImageStringFT. 2.0.18: space was a oor choice for this.
847a8cade3SRasmus Lerdorf  */
858b079070SIlia Alshanetsky #define LISTSEPARATOR ";"
867a8cade3SRasmus Lerdorf 
877a8cade3SRasmus Lerdorf /*
887a8cade3SRasmus Lerdorf  * DEFAULT_FONTPATH and PATHSEPARATOR are host type dependent and
8961026e39SIlia Alshanetsky  * are normally set by configure in config.h.  These are just
907a8cade3SRasmus Lerdorf  * some last resort values that might match some Un*x system
917a8cade3SRasmus Lerdorf  * if building this version of gd separate from graphviz.
927a8cade3SRasmus Lerdorf  */
937a8cade3SRasmus Lerdorf #ifndef DEFAULT_FONTPATH
948d6cfb79SIlia Alshanetsky #if defined(__APPLE__) || (defined(__MWERKS__) && defined(macintosh))
958d6cfb79SIlia Alshanetsky #define DEFAULT_FONTPATH "/usr/share/fonts/truetype:/System/Library/Fonts:/Library/Fonts"
968d6cfb79SIlia Alshanetsky #else
977a8cade3SRasmus Lerdorf #define DEFAULT_FONTPATH "/usr/share/fonts/truetype"
987a8cade3SRasmus Lerdorf #endif
998d6cfb79SIlia Alshanetsky #endif
1007a8cade3SRasmus Lerdorf #ifndef PATHSEPARATOR
1017a8cade3SRasmus Lerdorf #define PATHSEPARATOR ":"
1027a8cade3SRasmus Lerdorf #endif
1037a8cade3SRasmus Lerdorf 
1047a8cade3SRasmus Lerdorf #ifndef TRUE
1057a8cade3SRasmus Lerdorf #define FALSE 0
1067a8cade3SRasmus Lerdorf #define TRUE !FALSE
1077a8cade3SRasmus Lerdorf #endif
1087a8cade3SRasmus Lerdorf 
109ef1d4e1dSIlia Alshanetsky #ifndef MAX
1107a8cade3SRasmus Lerdorf #define MAX(a,b) ((a)>(b)?(a):(b))
111ef1d4e1dSIlia Alshanetsky #endif
112ef1d4e1dSIlia Alshanetsky 
113ef1d4e1dSIlia Alshanetsky #ifndef MIN
1147a8cade3SRasmus Lerdorf #define MIN(a,b) ((a)<(b)?(a):(b))
115ef1d4e1dSIlia Alshanetsky #endif
1167a8cade3SRasmus Lerdorf 
1177a8cade3SRasmus Lerdorf typedef struct
1187a8cade3SRasmus Lerdorf {
11936db28c8SIlia Alshanetsky 	char *fontlist;		/* key */
12036db28c8SIlia Alshanetsky 	FT_Library *library;
12136db28c8SIlia Alshanetsky 	FT_Face face;
12236db28c8SIlia Alshanetsky 	FT_Bool have_char_map_unicode, have_char_map_big5, have_char_map_sjis, have_char_map_apple_roman;
12336db28c8SIlia Alshanetsky 	gdCache_head_t *glyphCache;
12436db28c8SIlia Alshanetsky } font_t;
1257a8cade3SRasmus Lerdorf 
1267a8cade3SRasmus Lerdorf typedef struct
12736db28c8SIlia Alshanetsky {
12836db28c8SIlia Alshanetsky 	char *fontlist;		/* key */
129796bfb0bSScott MacVicar 	int preferred_map;
13036db28c8SIlia Alshanetsky 	FT_Library *library;
13136db28c8SIlia Alshanetsky } fontkey_t;
1327a8cade3SRasmus Lerdorf 
1337a8cade3SRasmus Lerdorf typedef struct
13436db28c8SIlia Alshanetsky {
13536db28c8SIlia Alshanetsky 	int pixel;		/* key */
13636db28c8SIlia Alshanetsky 	int bgcolor;		/* key */
13736db28c8SIlia Alshanetsky 	int fgcolor;		/* key *//* -ve means no antialias */
13836db28c8SIlia Alshanetsky 	gdImagePtr im;		/* key */
13936db28c8SIlia Alshanetsky 	int tweencolor;
14036db28c8SIlia Alshanetsky } tweencolor_t;
1417a8cade3SRasmus Lerdorf 
1427a8cade3SRasmus Lerdorf typedef struct
14336db28c8SIlia Alshanetsky {
14436db28c8SIlia Alshanetsky 	int pixel;		/* key */
14536db28c8SIlia Alshanetsky 	int bgcolor;		/* key */
14636db28c8SIlia Alshanetsky 	int fgcolor;		/* key *//* -ve means no antialias */
14736db28c8SIlia Alshanetsky 	gdImagePtr im;		/* key */
14836db28c8SIlia Alshanetsky } tweencolorkey_t;
1497a8cade3SRasmus Lerdorf 
1507a8cade3SRasmus Lerdorf /********************************************************************
1517a8cade3SRasmus Lerdorf  * gdTcl_UtfToUniChar is borrowed from Tcl ...
1527a8cade3SRasmus Lerdorf  */
1537a8cade3SRasmus Lerdorf /*
1547a8cade3SRasmus Lerdorf  * tclUtf.c --
1557a8cade3SRasmus Lerdorf  *
1567a8cade3SRasmus Lerdorf  *      Routines for manipulating UTF-8 strings.
1577a8cade3SRasmus Lerdorf  *
1587a8cade3SRasmus Lerdorf  * Copyright (c) 1997-1998 Sun Microsystems, Inc.
1597a8cade3SRasmus Lerdorf  *
1607a8cade3SRasmus Lerdorf  * See the file "license.terms" for information on usage and redistribution
1617a8cade3SRasmus Lerdorf  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1627a8cade3SRasmus Lerdorf  *
1637a8cade3SRasmus Lerdorf  * SCCS: @(#) tclUtf.c 1.25 98/01/28 18:02:43
1647a8cade3SRasmus Lerdorf  */
1657a8cade3SRasmus Lerdorf 
1667a8cade3SRasmus Lerdorf /*
1677a8cade3SRasmus Lerdorf  *---------------------------------------------------------------------------
1687a8cade3SRasmus Lerdorf  *
1697a8cade3SRasmus Lerdorf  * gdTcl_UtfToUniChar --
1707a8cade3SRasmus Lerdorf  *
1717a8cade3SRasmus Lerdorf  *      Extract the Tcl_UniChar represented by the UTF-8 string.  Bad
1727a8cade3SRasmus Lerdorf  *      UTF-8 sequences are converted to valid Tcl_UniChars and processing
1737a8cade3SRasmus Lerdorf  *      continues.  Equivalent to Plan 9 chartorune().
1747a8cade3SRasmus Lerdorf  *
1757a8cade3SRasmus Lerdorf  *      The caller must ensure that the source buffer is long enough that
1767a8cade3SRasmus Lerdorf  *      this routine does not run off the end and dereference non-existent
1777a8cade3SRasmus Lerdorf  *      memory looking for trail bytes.  If the source buffer is known to
1787a8cade3SRasmus Lerdorf  *      be '\0' terminated, this cannot happen.  Otherwise, the caller
1797a8cade3SRasmus Lerdorf  *      should call Tcl_UtfCharComplete() before calling this routine to
1807a8cade3SRasmus Lerdorf  *      ensure that enough bytes remain in the string.
1817a8cade3SRasmus Lerdorf  *
1827a8cade3SRasmus Lerdorf  * Results:
1837a8cade3SRasmus Lerdorf  *      *chPtr is filled with the Tcl_UniChar, and the return value is the
1847a8cade3SRasmus Lerdorf  *      number of bytes from the UTF-8 string that were consumed.
1857a8cade3SRasmus Lerdorf  *
1867a8cade3SRasmus Lerdorf  * Side effects:
1877a8cade3SRasmus Lerdorf  *      None.
1887a8cade3SRasmus Lerdorf  *
1897a8cade3SRasmus Lerdorf  *---------------------------------------------------------------------------
1907a8cade3SRasmus Lerdorf  */
1917a8cade3SRasmus Lerdorf 
1927a8cade3SRasmus Lerdorf #ifdef JISX0208
1937a8cade3SRasmus Lerdorf #include "jisx0208.h"
1947a8cade3SRasmus Lerdorf #endif
1957a8cade3SRasmus Lerdorf 
196c3b7cb19SAntony Dovgal extern int any2eucjp (char *, char *, unsigned int);
197c3b7cb19SAntony Dovgal 
198c3b7cb19SAntony Dovgal /* Persistent font cache until explicitly cleared */
199c3b7cb19SAntony Dovgal /* Fonts can be used across multiple images */
200c3b7cb19SAntony Dovgal 
201c3b7cb19SAntony Dovgal /* 2.0.16: thread safety (the font cache is shared) */
202c3b7cb19SAntony Dovgal gdMutexDeclare(gdFontCacheMutex);
203c3b7cb19SAntony Dovgal static gdCache_head_t *fontCache = NULL;
204c3b7cb19SAntony Dovgal static FT_Library library;
205c3b7cb19SAntony Dovgal 
2067a8cade3SRasmus Lerdorf #define Tcl_UniChar int
2077a8cade3SRasmus Lerdorf #define TCL_UTF_MAX 3
gdTcl_UtfToUniChar(char * str,Tcl_UniChar * chPtr)20836db28c8SIlia Alshanetsky static int gdTcl_UtfToUniChar (char *str, Tcl_UniChar * chPtr)
2097a8cade3SRasmus Lerdorf /* str is the UTF8 next character pointer */
2107a8cade3SRasmus Lerdorf /* chPtr is the int for the result */
2117a8cade3SRasmus Lerdorf {
21236db28c8SIlia Alshanetsky 	int byte;
21336db28c8SIlia Alshanetsky 
21436db28c8SIlia Alshanetsky 	/* HTML4.0 entities in decimal form, e.g. &#197; */
21536db28c8SIlia Alshanetsky 	byte = *((unsigned char *) str);
21636db28c8SIlia Alshanetsky 	if (byte == '&') {
21736db28c8SIlia Alshanetsky 		int i, n = 0;
21836db28c8SIlia Alshanetsky 
21936db28c8SIlia Alshanetsky 		byte = *((unsigned char *) (str + 1));
22036db28c8SIlia Alshanetsky 		if (byte == '#') {
22196aedc6eSPierre Joye 			byte = *((unsigned char *) (str + 2));
22296aedc6eSPierre Joye 			if (byte == 'x' || byte == 'X') {
22396aedc6eSPierre Joye 				for (i = 3; i < 8; i++) {
22496aedc6eSPierre Joye 					byte = *((unsigned char *) (str + i));
22596aedc6eSPierre Joye 					if (byte >= 'A' && byte <= 'F')
22696aedc6eSPierre Joye 						byte = byte - 'A' + 10;
22796aedc6eSPierre Joye 					else if (byte >= 'a' && byte <= 'f')
22896aedc6eSPierre Joye 						byte = byte - 'a' + 10;
22996aedc6eSPierre Joye 					else if (byte >= '0' && byte <= '9')
23096aedc6eSPierre Joye 						byte = byte - '0';
23196aedc6eSPierre Joye 					else
23296aedc6eSPierre Joye 						break;
23396aedc6eSPierre Joye 					n = (n * 16) + byte;
23496aedc6eSPierre Joye 				}
23596aedc6eSPierre Joye 			} else {
23696aedc6eSPierre Joye 				for (i = 2; i < 8; i++) {
23796aedc6eSPierre Joye 					byte = *((unsigned char *) (str + i));
23896aedc6eSPierre Joye 					if (byte >= '0' && byte <= '9') {
23996aedc6eSPierre Joye 						n = (n * 10) + (byte - '0');
24096aedc6eSPierre Joye 					} else {
24196aedc6eSPierre Joye 						break;
24296aedc6eSPierre Joye 					}
24336db28c8SIlia Alshanetsky 				}
24436db28c8SIlia Alshanetsky 			}
24536db28c8SIlia Alshanetsky 			if (byte == ';') {
24636db28c8SIlia Alshanetsky 				*chPtr = (Tcl_UniChar) n;
24736db28c8SIlia Alshanetsky 				return ++i;
24836db28c8SIlia Alshanetsky 			}
2497a8cade3SRasmus Lerdorf 		}
2507a8cade3SRasmus Lerdorf 	}
2517a8cade3SRasmus Lerdorf 
25236db28c8SIlia Alshanetsky 	/* Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones. */
2537a8cade3SRasmus Lerdorf 
25436db28c8SIlia Alshanetsky 	byte = *((unsigned char *) str);
2557a8cade3SRasmus Lerdorf #ifdef JISX0208
25636db28c8SIlia Alshanetsky 	if (0xA1 <= byte && byte <= 0xFE) {
25736db28c8SIlia Alshanetsky 		int ku, ten;
25836db28c8SIlia Alshanetsky 
25936db28c8SIlia Alshanetsky 		ku = (byte & 0x7F) - 0x20;
26036db28c8SIlia Alshanetsky 		ten = (str[1] & 0x7F) - 0x20;
26136db28c8SIlia Alshanetsky 		if ((ku < 1 || ku > 92) || (ten < 1 || ten > 94)) {
26236db28c8SIlia Alshanetsky 			*chPtr = (Tcl_UniChar) byte;
26336db28c8SIlia Alshanetsky 			return 1;
26436db28c8SIlia Alshanetsky 		}
2657a8cade3SRasmus Lerdorf 
26636db28c8SIlia Alshanetsky 		*chPtr = (Tcl_UniChar) UnicodeTbl[ku - 1][ten - 1];
26736db28c8SIlia Alshanetsky 		return 2;
26836db28c8SIlia Alshanetsky 	} else
2697a8cade3SRasmus Lerdorf #endif /* JISX0208 */
27036db28c8SIlia Alshanetsky 	if (byte < 0xC0) {
27136db28c8SIlia Alshanetsky 		/* Handles properly formed UTF-8 characters between
27236db28c8SIlia Alshanetsky 		 * 0x01 and 0x7F.  Also treats \0 and naked trail
27336db28c8SIlia Alshanetsky 		 * bytes 0x80 to 0xBF as valid characters representing
27436db28c8SIlia Alshanetsky 		 * themselves.
27536db28c8SIlia Alshanetsky 		 */
27636db28c8SIlia Alshanetsky 
27736db28c8SIlia Alshanetsky 		*chPtr = (Tcl_UniChar) byte;
27836db28c8SIlia Alshanetsky 		return 1;
27936db28c8SIlia Alshanetsky 	} else if (byte < 0xE0) {
28036db28c8SIlia Alshanetsky 		if ((str[1] & 0xC0) == 0x80) {
28136db28c8SIlia Alshanetsky 			/* Two-byte-character lead-byte followed by a trail-byte. */
28236db28c8SIlia Alshanetsky 
28336db28c8SIlia Alshanetsky 			*chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6) | (str[1] & 0x3F));
28436db28c8SIlia Alshanetsky 			return 2;
28536db28c8SIlia Alshanetsky 		}
28636db28c8SIlia Alshanetsky 		/*
28736db28c8SIlia Alshanetsky 		 * A two-byte-character lead-byte not followed by trail-byte
28836db28c8SIlia Alshanetsky 		 * represents itself.
28936db28c8SIlia Alshanetsky 		 */
29036db28c8SIlia Alshanetsky 
29136db28c8SIlia Alshanetsky 		*chPtr = (Tcl_UniChar) byte;
29236db28c8SIlia Alshanetsky 		return 1;
29336db28c8SIlia Alshanetsky 	} else if (byte < 0xF0) {
29436db28c8SIlia Alshanetsky 		if (((str[1] & 0xC0) == 0x80) && ((str[2] & 0xC0) == 0x80)) {
29536db28c8SIlia Alshanetsky 			/* Three-byte-character lead byte followed by two trail bytes. */
29636db28c8SIlia Alshanetsky 
29736db28c8SIlia Alshanetsky 			*chPtr = (Tcl_UniChar) (((byte & 0x0F) << 12) | ((str[1] & 0x3F) << 6) | (str[2] & 0x3F));
29836db28c8SIlia Alshanetsky 			return 3;
29936db28c8SIlia Alshanetsky 		}
30036db28c8SIlia Alshanetsky 		/* A three-byte-character lead-byte not followed by two trail-bytes represents itself. */
30136db28c8SIlia Alshanetsky 
30236db28c8SIlia Alshanetsky 		*chPtr = (Tcl_UniChar) byte;
30336db28c8SIlia Alshanetsky 		return 1;
3047a8cade3SRasmus Lerdorf 	}
3057a8cade3SRasmus Lerdorf #if TCL_UTF_MAX > 3
30636db28c8SIlia Alshanetsky 	else {
30736db28c8SIlia Alshanetsky 		int ch, total, trail;
30836db28c8SIlia Alshanetsky 
30936db28c8SIlia Alshanetsky 		total = totalBytes[byte];
31036db28c8SIlia Alshanetsky 		trail = total - 1;
31136db28c8SIlia Alshanetsky 
31236db28c8SIlia Alshanetsky 		if (trail > 0) {
31336db28c8SIlia Alshanetsky 			ch = byte & (0x3F >> trail);
31436db28c8SIlia Alshanetsky 			do {
31536db28c8SIlia Alshanetsky 				str++;
31636db28c8SIlia Alshanetsky 				if ((*str & 0xC0) != 0x80) {
31736db28c8SIlia Alshanetsky 					*chPtr = byte;
31836db28c8SIlia Alshanetsky 					return 1;
31936db28c8SIlia Alshanetsky 				}
32036db28c8SIlia Alshanetsky 				ch <<= 6;
32136db28c8SIlia Alshanetsky 				ch |= (*str & 0x3F);
32236db28c8SIlia Alshanetsky 				trail--;
32336db28c8SIlia Alshanetsky 			} while (trail > 0);
32436db28c8SIlia Alshanetsky 			*chPtr = ch;
32536db28c8SIlia Alshanetsky 			return total;
3267a8cade3SRasmus Lerdorf 		}
3277a8cade3SRasmus Lerdorf 	}
3287a8cade3SRasmus Lerdorf #endif
3297a8cade3SRasmus Lerdorf 
33036db28c8SIlia Alshanetsky 	*chPtr = (Tcl_UniChar) byte;
33136db28c8SIlia Alshanetsky 	return 1;
3327a8cade3SRasmus Lerdorf }
3337a8cade3SRasmus Lerdorf 
3347a8cade3SRasmus Lerdorf /********************************************************************/
3357a8cade3SRasmus Lerdorf /* font cache functions                                             */
3367a8cade3SRasmus Lerdorf 
fontTest(void * element,void * key)33736db28c8SIlia Alshanetsky static int fontTest (void *element, void *key)
3387a8cade3SRasmus Lerdorf {
33936db28c8SIlia Alshanetsky 	font_t *a = (font_t *) element;
34036db28c8SIlia Alshanetsky 	fontkey_t *b = (fontkey_t *) key;
341b7a7b1a6SStanislav Malyshev 
342796bfb0bSScott MacVicar 	if (strcmp (a->fontlist, b->fontlist) == 0) {
343796bfb0bSScott MacVicar 		switch (b->preferred_map) {
344796bfb0bSScott MacVicar 			case gdFTEX_Unicode:
345796bfb0bSScott MacVicar 				if (a->have_char_map_unicode) {
346796bfb0bSScott MacVicar 					return 1;
347796bfb0bSScott MacVicar 				}
348796bfb0bSScott MacVicar 				break;
349796bfb0bSScott MacVicar 			case gdFTEX_Shift_JIS:
350796bfb0bSScott MacVicar 				if (a->have_char_map_sjis) {
351796bfb0bSScott MacVicar 					return 1;
352796bfb0bSScott MacVicar 				}
353796bfb0bSScott MacVicar 				break;
354796bfb0bSScott MacVicar 			case gdFTEX_Big5:
355796bfb0bSScott MacVicar 				if (a->have_char_map_sjis) {
356796bfb0bSScott MacVicar 					return 1;
357796bfb0bSScott MacVicar 				}
358796bfb0bSScott MacVicar 				break;
359796bfb0bSScott MacVicar 		}
360796bfb0bSScott MacVicar 	}
361796bfb0bSScott MacVicar 	return 0;
3627a8cade3SRasmus Lerdorf }
3637a8cade3SRasmus Lerdorf 
fontFetch(char ** error,void * key)36445cd6275SIlia Alshanetsky static void *fontFetch (char **error, void *key)
3657a8cade3SRasmus Lerdorf {
366ef26e2d1SIlia Alshanetsky 	font_t *a;
367ef26e2d1SIlia Alshanetsky 	fontkey_t *b = (fontkey_t *) key;
368ef26e2d1SIlia Alshanetsky 	int n;
369ef26e2d1SIlia Alshanetsky 	int font_found = 0;
370ef26e2d1SIlia Alshanetsky 	unsigned short platform, encoding;
371ef26e2d1SIlia Alshanetsky 	char *fontsearchpath, *fontlist;
372e440d4fbSIlia Alshanetsky 	char fullname[MAXPATHLEN], cur_dir[MAXPATHLEN];
373ef26e2d1SIlia Alshanetsky 	char *name, *path=NULL, *dir;
374ef26e2d1SIlia Alshanetsky 	char *strtok_ptr;
375ef26e2d1SIlia Alshanetsky 	FT_Error err;
376ef26e2d1SIlia Alshanetsky 	FT_CharMap found = 0;
377ef26e2d1SIlia Alshanetsky 	FT_CharMap charmap;
378ef26e2d1SIlia Alshanetsky 
379ef26e2d1SIlia Alshanetsky 	a = (font_t *) gdPMalloc(sizeof(font_t));
380ef26e2d1SIlia Alshanetsky 	a->fontlist = gdPEstrdup(b->fontlist);
381ef26e2d1SIlia Alshanetsky 	a->library = b->library;
382ef26e2d1SIlia Alshanetsky 
383ef26e2d1SIlia Alshanetsky 	/*
384ef26e2d1SIlia Alshanetsky 	 * Search the pathlist for any of a list of font names.
385ef26e2d1SIlia Alshanetsky 	 */
386ef26e2d1SIlia Alshanetsky 	fontsearchpath = getenv ("GDFONTPATH");
387ef26e2d1SIlia Alshanetsky 	if (!fontsearchpath) {
388ef26e2d1SIlia Alshanetsky 		fontsearchpath = DEFAULT_FONTPATH;
38936db28c8SIlia Alshanetsky 	}
390ef26e2d1SIlia Alshanetsky 	fontlist = gdEstrdup(a->fontlist);
391ef26e2d1SIlia Alshanetsky 
392ef26e2d1SIlia Alshanetsky 	/*
393cbe1597bSSara Golemon 	 * Must use gd_strtok_r becasuse strtok() isn't thread safe
394ef26e2d1SIlia Alshanetsky 	 */
395ef26e2d1SIlia Alshanetsky 	for (name = gd_strtok_r (fontlist, LISTSEPARATOR, &strtok_ptr); name; name = gd_strtok_r (0, LISTSEPARATOR, &strtok_ptr)) {
396cbe1597bSSara Golemon 		char *strtok_ptr_path;
397ef26e2d1SIlia Alshanetsky 		/* make a fresh copy each time - strtok corrupts it. */
398ef26e2d1SIlia Alshanetsky 		path = gdEstrdup (fontsearchpath);
399e440d4fbSIlia Alshanetsky 
40036db28c8SIlia Alshanetsky 		/* if name is an absolute filename then test directly */
401fef929f0SScott MacVicar #ifdef NETWARE
402fef929f0SScott MacVicar 		if (*name == '/' || (name[0] != 0 && strstr(name, ":/"))) {
403fef929f0SScott MacVicar #else
404ef26e2d1SIlia Alshanetsky 		if (*name == '/' || (name[0] != 0 && name[1] == ':' && (name[2] == '/' || name[2] == '\\'))) {
405fef929f0SScott MacVicar #endif
406e440d4fbSIlia Alshanetsky 			snprintf(fullname, sizeof(fullname) - 1, "%s", name);
407ef26e2d1SIlia Alshanetsky 			if (access(fullname, R_OK) == 0) {
408ef26e2d1SIlia Alshanetsky 				font_found++;
409ef26e2d1SIlia Alshanetsky 				break;
410ef26e2d1SIlia Alshanetsky 			}
411ef26e2d1SIlia Alshanetsky 		}
412cbe1597bSSara Golemon 		for (dir = gd_strtok_r (path, PATHSEPARATOR, &strtok_ptr_path); dir;
413cbe1597bSSara Golemon 		     dir = gd_strtok_r (0, PATHSEPARATOR, &strtok_ptr_path)) {
414e440d4fbSIlia Alshanetsky 			if (!strcmp(dir, ".")) {
415bdeb220fSAnatol Belski 			#if HAVE_GETCWD
416e440d4fbSIlia Alshanetsky 				dir = VCWD_GETCWD(cur_dir, MAXPATHLEN);
417e440d4fbSIlia Alshanetsky #elif HAVE_GETWD
418e440d4fbSIlia Alshanetsky 				dir = VCWD_GETWD(cur_dir);
419e440d4fbSIlia Alshanetsky #endif
420e440d4fbSIlia Alshanetsky 				if (!dir) {
421e440d4fbSIlia Alshanetsky 					continue;
422e440d4fbSIlia Alshanetsky 				}
423e440d4fbSIlia Alshanetsky 			}
424e440d4fbSIlia Alshanetsky 
4258d6cfb79SIlia Alshanetsky #define GD_CHECK_FONT_PATH(ext)	\
4268d6cfb79SIlia Alshanetsky 	snprintf(fullname, sizeof(fullname) - 1, "%s/%s%s", dir, name, ext);	\
4278d6cfb79SIlia Alshanetsky 	if (access(fullname, R_OK) == 0) {	\
4288d6cfb79SIlia Alshanetsky 		font_found++;	\
4298d6cfb79SIlia Alshanetsky 		break;	\
4308d6cfb79SIlia Alshanetsky 	}	\
4318d6cfb79SIlia Alshanetsky 
4328d6cfb79SIlia Alshanetsky 			GD_CHECK_FONT_PATH("");
4338d6cfb79SIlia Alshanetsky 			GD_CHECK_FONT_PATH(".ttf");
4348d6cfb79SIlia Alshanetsky 			GD_CHECK_FONT_PATH(".pfa");
4358d6cfb79SIlia Alshanetsky 			GD_CHECK_FONT_PATH(".pfb");
4368d6cfb79SIlia Alshanetsky 			GD_CHECK_FONT_PATH(".dfont");
437ef26e2d1SIlia Alshanetsky 		}
438ef26e2d1SIlia Alshanetsky 		gdFree(path);
43992514865SIlia Alshanetsky 		path = NULL;
440ef26e2d1SIlia Alshanetsky 		if (font_found) {
441ef26e2d1SIlia Alshanetsky 			break;
44236db28c8SIlia Alshanetsky 		}
4437a8cade3SRasmus Lerdorf 	}
44436db28c8SIlia Alshanetsky 
445ef26e2d1SIlia Alshanetsky 	if (path) {
446ef26e2d1SIlia Alshanetsky 		gdFree(path);
4477a8cade3SRasmus Lerdorf 	}
44836db28c8SIlia Alshanetsky 
449ef26e2d1SIlia Alshanetsky 	gdFree(fontlist);
45036db28c8SIlia Alshanetsky 
451ef26e2d1SIlia Alshanetsky 	if (!font_found) {
452ef26e2d1SIlia Alshanetsky 		gdPFree(a->fontlist);
453ef26e2d1SIlia Alshanetsky 		gdPFree(a);
454ef26e2d1SIlia Alshanetsky 		*error = "Could not find/open font";
455ef26e2d1SIlia Alshanetsky 		return NULL;
456a24534a1SRasmus Lerdorf 	}
4577a8cade3SRasmus Lerdorf 
458ef26e2d1SIlia Alshanetsky 	err = FT_New_Face (*b->library, fullname, 0, &a->face);
459ef26e2d1SIlia Alshanetsky 	if (err) {
460ef26e2d1SIlia Alshanetsky 		gdPFree(a->fontlist);
461ef26e2d1SIlia Alshanetsky 		gdPFree(a);
462ef26e2d1SIlia Alshanetsky 		*error = "Could not read font";
463ef26e2d1SIlia Alshanetsky 		return NULL;
4647a8cade3SRasmus Lerdorf 	}
465ef26e2d1SIlia Alshanetsky 
46606fdf359SVeres Lajos 	/* FIXME - This mapping stuff is incomplete - where is the spec? */
4678d6cfb79SIlia Alshanetsky 	/* EAM   - It's worse than that. It's pointless to match character encodings here.
4688d6cfb79SIlia Alshanetsky 	 *         As currently written, the stored a->face->charmap only matches one of
4698d6cfb79SIlia Alshanetsky 	 *         the actual charmaps and we cannot know at this stage if it is the right
4708d6cfb79SIlia Alshanetsky 	 *         one. We should just skip all this stuff, and check in gdImageStringFTEx
4718d6cfb79SIlia Alshanetsky 	 *         if some particular charmap is preferred and if so whether it is held in
4728d6cfb79SIlia Alshanetsky 	 *         one of the a->face->charmaps[0..num_charmaps].
4738d6cfb79SIlia Alshanetsky 	 *         And why is it so bad not to find any recognized charmap?  The user may
4748d6cfb79SIlia Alshanetsky 	 *         still know what mapping to use, even if we do not.  In that case we can
4758d6cfb79SIlia Alshanetsky 	 *         just use the map in a->face->charmaps[num_charmaps] and be done with it.
4768d6cfb79SIlia Alshanetsky 	 */
477ef26e2d1SIlia Alshanetsky 
478ef26e2d1SIlia Alshanetsky 	for (n = 0; n < a->face->num_charmaps; n++) {
479ef26e2d1SIlia Alshanetsky 		charmap = a->face->charmaps[n];
480ef26e2d1SIlia Alshanetsky 		platform = charmap->platform_id;
481ef26e2d1SIlia Alshanetsky 		encoding = charmap->encoding_id;
4828d6cfb79SIlia Alshanetsky 
483796bfb0bSScott MacVicar 		/* Whatever is the last value is what should be set */
484796bfb0bSScott MacVicar 		a->have_char_map_unicode = 0;
485796bfb0bSScott MacVicar 		a->have_char_map_big5 = 0;
486796bfb0bSScott MacVicar 		a->have_char_map_sjis = 0;
487796bfb0bSScott MacVicar 		a->have_char_map_apple_roman = 0;
488796bfb0bSScott MacVicar 
4898d6cfb79SIlia Alshanetsky /* EAM DEBUG - Newer versions of libfree2 make it easier by defining encodings */
4906e9c4b31SIlia Alshanetsky #if (defined(FREETYPE_MAJOR) && ((FREETYPE_MAJOR == 2 && ((FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3) || FREETYPE_MINOR > 1) || FREETYPE_MAJOR > 2)))
4918d6cfb79SIlia Alshanetsky 	if (charmap->encoding == FT_ENCODING_MS_SYMBOL
4928d6cfb79SIlia Alshanetsky 		|| charmap->encoding == FT_ENCODING_ADOBE_CUSTOM
4938d6cfb79SIlia Alshanetsky 		|| charmap->encoding == FT_ENCODING_ADOBE_STANDARD) {
4948d6cfb79SIlia Alshanetsky 		a->have_char_map_unicode = 1;
4958d6cfb79SIlia Alshanetsky 		found = charmap;
4968d6cfb79SIlia Alshanetsky 		a->face->charmap = charmap;
4978d6cfb79SIlia Alshanetsky 		return (void *)a;
4988d6cfb79SIlia Alshanetsky 	}
4996e9c4b31SIlia Alshanetsky #endif /* Freetype 2.1.3 or better */
5008d6cfb79SIlia Alshanetsky /* EAM DEBUG */
5018d6cfb79SIlia Alshanetsky 
502ef26e2d1SIlia Alshanetsky 		if ((platform == 3 && encoding == 1)		/* Windows Unicode */
503ef26e2d1SIlia Alshanetsky 			|| (platform == 3 && encoding == 0)	/* Windows Symbol */
504ef26e2d1SIlia Alshanetsky 			|| (platform == 2 && encoding == 1)	/* ISO Unicode */
505ef26e2d1SIlia Alshanetsky 			|| (platform == 0))
506ef26e2d1SIlia Alshanetsky 		{						/* Apple Unicode */
507ef26e2d1SIlia Alshanetsky 			a->have_char_map_unicode = 1;
508ef26e2d1SIlia Alshanetsky 			found = charmap;
509796bfb0bSScott MacVicar 			if (b->preferred_map == gdFTEX_Unicode) {
510796bfb0bSScott MacVicar 				break;
511796bfb0bSScott MacVicar 			}
512ef26e2d1SIlia Alshanetsky 		} else if (platform == 3 && encoding == 4) {	/* Windows Big5 */
513ef26e2d1SIlia Alshanetsky 			a->have_char_map_big5 = 1;
514ef26e2d1SIlia Alshanetsky 			found = charmap;
515796bfb0bSScott MacVicar 			if (b->preferred_map == gdFTEX_Big5) {
516796bfb0bSScott MacVicar 				break;
517796bfb0bSScott MacVicar 			}
518ef26e2d1SIlia Alshanetsky 		} else if (platform == 3 && encoding == 2) {	/* Windows Sjis */
519ef26e2d1SIlia Alshanetsky 			a->have_char_map_sjis = 1;
520ef26e2d1SIlia Alshanetsky 			found = charmap;
521796bfb0bSScott MacVicar 			if (b->preferred_map == gdFTEX_Shift_JIS) {
522796bfb0bSScott MacVicar 				break;
523796bfb0bSScott MacVicar 			}
524ef26e2d1SIlia Alshanetsky 		} else if ((platform == 1 && encoding == 0)	/* Apple Roman */
525ef26e2d1SIlia Alshanetsky 			|| (platform == 2 && encoding == 0))
526ef26e2d1SIlia Alshanetsky 		{						/* ISO ASCII */
527ef26e2d1SIlia Alshanetsky 			a->have_char_map_apple_roman = 1;
528ef26e2d1SIlia Alshanetsky 			found = charmap;
529796bfb0bSScott MacVicar 			if (b->preferred_map == gdFTEX_MacRoman) {
530796bfb0bSScott MacVicar 				break;
531796bfb0bSScott MacVicar 			}
532ef26e2d1SIlia Alshanetsky 		}
5337a8cade3SRasmus Lerdorf 	}
534ef26e2d1SIlia Alshanetsky 	if (!found) {
535ef26e2d1SIlia Alshanetsky 		gdPFree(a->fontlist);
536ef26e2d1SIlia Alshanetsky 		gdPFree(a);
537ef26e2d1SIlia Alshanetsky 		*error = "Unable to find a CharMap that I can handle";
538ef26e2d1SIlia Alshanetsky 		return NULL;
5397a8cade3SRasmus Lerdorf 	}
5407a8cade3SRasmus Lerdorf 
54192514865SIlia Alshanetsky 	/* 2.0.5: we should actually return this */
54292514865SIlia Alshanetsky 	a->face->charmap = found;
543ef26e2d1SIlia Alshanetsky 	return (void *) a;
5447a8cade3SRasmus Lerdorf }
5457a8cade3SRasmus Lerdorf 
546ef26e2d1SIlia Alshanetsky static void fontRelease (void *element)
5477a8cade3SRasmus Lerdorf {
548ef26e2d1SIlia Alshanetsky 	font_t *a = (font_t *) element;
5497a8cade3SRasmus Lerdorf 
550ef26e2d1SIlia Alshanetsky 	FT_Done_Face (a->face);
551ef26e2d1SIlia Alshanetsky 	gdPFree(a->fontlist);
552ef26e2d1SIlia Alshanetsky 	gdPFree((char *) element);
5537a8cade3SRasmus Lerdorf }
5547a8cade3SRasmus Lerdorf 
5557a8cade3SRasmus Lerdorf /********************************************************************/
5567a8cade3SRasmus Lerdorf /* tweencolor cache functions                                            */
5577a8cade3SRasmus Lerdorf 
5588d6cfb79SIlia Alshanetsky static int tweenColorTest (void *element, void *key)
5597a8cade3SRasmus Lerdorf {
5608d6cfb79SIlia Alshanetsky 	tweencolor_t *a = (tweencolor_t *) element;
5618d6cfb79SIlia Alshanetsky 	tweencolorkey_t *b = (tweencolorkey_t *) key;
5627a8cade3SRasmus Lerdorf 
5638d6cfb79SIlia Alshanetsky 	return (a->pixel == b->pixel && a->bgcolor == b->bgcolor && a->fgcolor == b->fgcolor && a->im == b->im);
5647a8cade3SRasmus Lerdorf }
5657a8cade3SRasmus Lerdorf 
5667a8cade3SRasmus Lerdorf /*
5677a8cade3SRasmus Lerdorf  * Computes a color in im's color table that is part way between
5687a8cade3SRasmus Lerdorf  * the background and foreground colors proportional to the gray
5697a8cade3SRasmus Lerdorf  * pixel value in the range 0-NUMCOLORS. The fg and bg colors must already
570a24534a1SRasmus Lerdorf  * be in the color table for palette images. For truecolor images the
571a24534a1SRasmus Lerdorf  * returned value simply has an alpha component and gdImageAlphaBlend
572a24534a1SRasmus Lerdorf  * does the work so that text can be alpha blended across a complex
573a24534a1SRasmus Lerdorf  * background (TBB; and for real in 2.0.2).
5747a8cade3SRasmus Lerdorf  */
57536db28c8SIlia Alshanetsky static void * tweenColorFetch (char **error, void *key)
5767a8cade3SRasmus Lerdorf {
57736db28c8SIlia Alshanetsky 	tweencolor_t *a;
57836db28c8SIlia Alshanetsky 	tweencolorkey_t *b = (tweencolorkey_t *) key;
57936db28c8SIlia Alshanetsky 	int pixel, npixel, bg, fg;
58036db28c8SIlia Alshanetsky 	gdImagePtr im;
58136db28c8SIlia Alshanetsky 
58236db28c8SIlia Alshanetsky 	a = (tweencolor_t *) gdMalloc (sizeof (tweencolor_t));
58336db28c8SIlia Alshanetsky 	pixel = a->pixel = b->pixel;
58436db28c8SIlia Alshanetsky 	bg = a->bgcolor = b->bgcolor;
58536db28c8SIlia Alshanetsky 	fg = a->fgcolor = b->fgcolor;
586678b162fSNuno Lopes 	im = a->im = b->im;
58736db28c8SIlia Alshanetsky 
58836db28c8SIlia Alshanetsky 	/* if fg is specified by a negative color idx, then don't antialias */
58936db28c8SIlia Alshanetsky 	if (fg < 0) {
59036db28c8SIlia Alshanetsky 		if ((pixel + pixel) >= NUMCOLORS) {
59136db28c8SIlia Alshanetsky 			a->tweencolor = -fg;
59236db28c8SIlia Alshanetsky 		} else {
59336db28c8SIlia Alshanetsky 			a->tweencolor = bg;
59436db28c8SIlia Alshanetsky 		}
59536db28c8SIlia Alshanetsky 	} else {
59636db28c8SIlia Alshanetsky 		npixel = NUMCOLORS - pixel;
59736db28c8SIlia Alshanetsky 		if (im->trueColor) {
59836db28c8SIlia Alshanetsky 			/* 2.0.1: use gdImageSetPixel to do the alpha blending work,
59936db28c8SIlia Alshanetsky 			 * or to just store the alpha level. All we have to do here
60036db28c8SIlia Alshanetsky 			 * is incorporate our knowledge of the percentage of this
60136db28c8SIlia Alshanetsky 			 * pixel that is really "lit" by pushing the alpha value
60236db28c8SIlia Alshanetsky 			 * up toward transparency in edge regions.
60336db28c8SIlia Alshanetsky 			 */
60436db28c8SIlia Alshanetsky 			a->tweencolor = gdTrueColorAlpha(
60536db28c8SIlia Alshanetsky 						gdTrueColorGetRed(fg),
60636db28c8SIlia Alshanetsky 						gdTrueColorGetGreen(fg),
60736db28c8SIlia Alshanetsky                                         	gdTrueColorGetBlue(fg),
60836db28c8SIlia Alshanetsky 						gdAlphaMax - (gdTrueColorGetAlpha (fg) * pixel / NUMCOLORS));
60936db28c8SIlia Alshanetsky 		} else {
61036db28c8SIlia Alshanetsky 			a->tweencolor = gdImageColorResolve(im,
61136db28c8SIlia Alshanetsky 						(pixel * im->red[fg] + npixel * im->red[bg]) / NUMCOLORS,
61236db28c8SIlia Alshanetsky 						(pixel * im->green[fg] + npixel * im->green[bg]) / NUMCOLORS,
61336db28c8SIlia Alshanetsky 						(pixel * im->blue[fg] + npixel * im->blue[bg]) / NUMCOLORS);
61436db28c8SIlia Alshanetsky 		}
61536db28c8SIlia Alshanetsky 	}
61636db28c8SIlia Alshanetsky 	return (void *) a;
6177a8cade3SRasmus Lerdorf }
6187a8cade3SRasmus Lerdorf 
61936db28c8SIlia Alshanetsky static void tweenColorRelease (void *element)
6207a8cade3SRasmus Lerdorf {
62136db28c8SIlia Alshanetsky 	gdFree((char *) element);
6227a8cade3SRasmus Lerdorf }
6237a8cade3SRasmus Lerdorf 
6247a8cade3SRasmus Lerdorf /* draw_bitmap - transfers glyph bitmap to GD image */
62536db28c8SIlia Alshanetsky static char * gdft_draw_bitmap (gdCache_head_t *tc_cache, gdImage * im, int fg, FT_Bitmap bitmap, int pen_x, int pen_y)
6267a8cade3SRasmus Lerdorf {
62736db28c8SIlia Alshanetsky 	unsigned char *pixel = NULL;
62836db28c8SIlia Alshanetsky 	int *tpixel = NULL;
629*ba2b50d0SChristoph M. Becker 	int opixel;
63036db28c8SIlia Alshanetsky 	int x, y, row, col, pc, pcr;
63136db28c8SIlia Alshanetsky 
63236db28c8SIlia Alshanetsky 	tweencolor_t *tc_elem;
63336db28c8SIlia Alshanetsky 	tweencolorkey_t tc_key;
63436db28c8SIlia Alshanetsky 
63536db28c8SIlia Alshanetsky 	/* copy to image, mapping colors */
63636db28c8SIlia Alshanetsky 	tc_key.fgcolor = fg;
63736db28c8SIlia Alshanetsky 	tc_key.im = im;
63836db28c8SIlia Alshanetsky 	/* Truecolor version; does not require the cache */
63936db28c8SIlia Alshanetsky 	if (im->trueColor) {
64036db28c8SIlia Alshanetsky 		for (row = 0; row < bitmap.rows; row++) {
64136db28c8SIlia Alshanetsky 			pc = row * bitmap.pitch;
64236db28c8SIlia Alshanetsky 			pcr = pc;
64336db28c8SIlia Alshanetsky 			y = pen_y + row;
64436db28c8SIlia Alshanetsky 			/* clip if out of bounds */
64536db28c8SIlia Alshanetsky 			/* 2.0.16: clipping rectangle, not image bounds */
64636db28c8SIlia Alshanetsky 			if ((y > im->cy2) || (y < im->cy1)) {
64736db28c8SIlia Alshanetsky 				continue;
64836db28c8SIlia Alshanetsky 			}
64936db28c8SIlia Alshanetsky 			for (col = 0; col < bitmap.width; col++, pc++) {
65036db28c8SIlia Alshanetsky 				int level;
65136db28c8SIlia Alshanetsky 				if (bitmap.pixel_mode == ft_pixel_mode_grays) {
65236db28c8SIlia Alshanetsky 					/* Scale to 128 levels of alpha for gd use.
65336db28c8SIlia Alshanetsky 					 * alpha 0 is opacity, so be sure to invert at the end
65436db28c8SIlia Alshanetsky 					 */
65536db28c8SIlia Alshanetsky 					level = (bitmap.buffer[pc] * gdAlphaMax / (bitmap.num_grays - 1));
65636db28c8SIlia Alshanetsky 				} else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
65736db28c8SIlia Alshanetsky 					/* 2.0.5: mode_mono fix from Giuliano Pochini */
65836db28c8SIlia Alshanetsky 					level = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? gdAlphaTransparent : gdAlphaOpaque;
65936db28c8SIlia Alshanetsky 				} else {
66036db28c8SIlia Alshanetsky 					return "Unsupported ft_pixel_mode";
66136db28c8SIlia Alshanetsky 				}
66209eb6ed3SChristoph M. Becker 				if (level == 0)  /* if background */
66309eb6ed3SChristoph M. Becker 					continue;
66436db28c8SIlia Alshanetsky 				if ((fg >= 0) && (im->trueColor)) {
66536db28c8SIlia Alshanetsky 					/* Consider alpha in the foreground color itself to be an
66636db28c8SIlia Alshanetsky 					 * upper bound on how opaque things get, when truecolor is
66736db28c8SIlia Alshanetsky 					 * available. Without truecolor this results in far too many
66836db28c8SIlia Alshanetsky 					 * color indexes.
66936db28c8SIlia Alshanetsky 					 */
67036db28c8SIlia Alshanetsky 					level = level * (gdAlphaMax - gdTrueColorGetAlpha(fg)) / gdAlphaMax;
67136db28c8SIlia Alshanetsky 				}
67236db28c8SIlia Alshanetsky 				level = gdAlphaMax - level;
67336db28c8SIlia Alshanetsky 				x = pen_x + col;
67436db28c8SIlia Alshanetsky 				/* clip if out of bounds */
67536db28c8SIlia Alshanetsky 				/* 2.0.16: clip to clipping rectangle, Matt McNabb */
67636db28c8SIlia Alshanetsky 				if ((x > im->cx2) || (x < im->cx1)) {
67736db28c8SIlia Alshanetsky 					continue;
67836db28c8SIlia Alshanetsky 				}
67936db28c8SIlia Alshanetsky 				/* get pixel location in gd buffer */
68036db28c8SIlia Alshanetsky 				tpixel = &im->tpixels[y][x];
68136db28c8SIlia Alshanetsky 				if (fg < 0) {
68236db28c8SIlia Alshanetsky 					if (level < (gdAlphaMax / 2)) {
68336db28c8SIlia Alshanetsky 						*tpixel = -fg;
68436db28c8SIlia Alshanetsky 					}
68536db28c8SIlia Alshanetsky 				} else {
68636db28c8SIlia Alshanetsky 					if (im->alphaBlendingFlag) {
687*ba2b50d0SChristoph M. Becker 						opixel = *tpixel;
688*ba2b50d0SChristoph M. Becker 						if (gdTrueColorGetAlpha(opixel) != gdAlphaTransparent) {
689*ba2b50d0SChristoph M. Becker 							*tpixel = gdAlphaBlend (opixel,
690*ba2b50d0SChristoph M. Becker 							                        (level << 24) + (fg & 0xFFFFFF));
691*ba2b50d0SChristoph M. Becker 						} else {
692*ba2b50d0SChristoph M. Becker 							*tpixel = (level << 24) + (fg & 0xFFFFFF);
693*ba2b50d0SChristoph M. Becker 						}
69436db28c8SIlia Alshanetsky 					} else {
69536db28c8SIlia Alshanetsky 						*tpixel = (level << 24) + (fg & 0xFFFFFF);
69636db28c8SIlia Alshanetsky 					}
69736db28c8SIlia Alshanetsky 				}
69836db28c8SIlia Alshanetsky 			}
69936db28c8SIlia Alshanetsky 		}
70036db28c8SIlia Alshanetsky 		return (char *) NULL;
70136db28c8SIlia Alshanetsky 	}
70236db28c8SIlia Alshanetsky 	/* Non-truecolor case, restored to its more or less original form */
70336db28c8SIlia Alshanetsky 	for (row = 0; row < bitmap.rows; row++) {
70436db28c8SIlia Alshanetsky 		int pcr;
70536db28c8SIlia Alshanetsky 		pc = row * bitmap.pitch;
70636db28c8SIlia Alshanetsky 		pcr = pc;
70736db28c8SIlia Alshanetsky 		if (bitmap.pixel_mode==ft_pixel_mode_mono) {
70836db28c8SIlia Alshanetsky 			pc *= 8;    /* pc is measured in bits for monochrome images */
7097a8cade3SRasmus Lerdorf 		}
71036db28c8SIlia Alshanetsky 		y = pen_y + row;
71136db28c8SIlia Alshanetsky 
71236db28c8SIlia Alshanetsky 		/* clip if out of bounds */
71336db28c8SIlia Alshanetsky 		if (y >= im->sy || y < 0) {
71436db28c8SIlia Alshanetsky 			continue;
71536db28c8SIlia Alshanetsky 		}
71636db28c8SIlia Alshanetsky 
71736db28c8SIlia Alshanetsky 		for (col = 0; col < bitmap.width; col++, pc++) {
71836db28c8SIlia Alshanetsky 			if (bitmap.pixel_mode == ft_pixel_mode_grays) {
71936db28c8SIlia Alshanetsky 				/*
72036db28c8SIlia Alshanetsky 				 * Round to NUMCOLORS levels of antialiasing for
72136db28c8SIlia Alshanetsky 				 * index color images since only 256 colors are
72236db28c8SIlia Alshanetsky 				 * available.
72336db28c8SIlia Alshanetsky 				 */
72436db28c8SIlia Alshanetsky 				tc_key.pixel = ((bitmap.buffer[pc] * NUMCOLORS) + bitmap.num_grays / 2) / (bitmap.num_grays - 1);
72536db28c8SIlia Alshanetsky 			} else if (bitmap.pixel_mode == ft_pixel_mode_mono) {
72636db28c8SIlia Alshanetsky 				tc_key.pixel = ((bitmap.buffer[pc / 8] << (pc % 8)) & 128) ? NUMCOLORS : 0;
72736db28c8SIlia Alshanetsky 				/* 2.0.5: mode_mono fix from Giuliano Pochini */
72836db28c8SIlia Alshanetsky 				tc_key.pixel = ((bitmap.buffer[(col>>3)+pcr]) & (1<<(~col&0x07))) ? NUMCOLORS : 0;
72936db28c8SIlia Alshanetsky 			} else {
73036db28c8SIlia Alshanetsky 				return "Unsupported ft_pixel_mode";
73136db28c8SIlia Alshanetsky 			}
73236db28c8SIlia Alshanetsky 			if (tc_key.pixel > 0) { /* if not background */
73336db28c8SIlia Alshanetsky 				x = pen_x + col;
73436db28c8SIlia Alshanetsky 
73536db28c8SIlia Alshanetsky 				/* clip if out of bounds */
73636db28c8SIlia Alshanetsky 				if (x >= im->sx || x < 0) {
73736db28c8SIlia Alshanetsky 					continue;
73836db28c8SIlia Alshanetsky 				}
73936db28c8SIlia Alshanetsky 				/* get pixel location in gd buffer */
74036db28c8SIlia Alshanetsky 				pixel = &im->pixels[y][x];
74136db28c8SIlia Alshanetsky 				if (tc_key.pixel == NUMCOLORS) {
74236db28c8SIlia Alshanetsky 					/* use fg color directly. gd 2.0.2: watch out for
74336db28c8SIlia Alshanetsky 					 * negative indexes (thanks to David Marwood).
74436db28c8SIlia Alshanetsky 					 */
74536db28c8SIlia Alshanetsky 					*pixel = (fg < 0) ? -fg : fg;
74636db28c8SIlia Alshanetsky 				} else {
74736db28c8SIlia Alshanetsky 					/* find antialised color */
74836db28c8SIlia Alshanetsky 					tc_key.bgcolor = *pixel;
74936db28c8SIlia Alshanetsky 					tc_elem = (tweencolor_t *) gdCacheGet(tc_cache, &tc_key);
75036db28c8SIlia Alshanetsky 					*pixel = tc_elem->tweencolor;
75136db28c8SIlia Alshanetsky 				}
75236db28c8SIlia Alshanetsky 			}
7537a8cade3SRasmus Lerdorf 		}
7547a8cade3SRasmus Lerdorf 	}
75536db28c8SIlia Alshanetsky 	return (char *) NULL;
7567a8cade3SRasmus Lerdorf }
7577a8cade3SRasmus Lerdorf 
758a24534a1SRasmus Lerdorf static int
759a24534a1SRasmus Lerdorf gdroundupdown (FT_F26Dot6 v1, int roundup)
760a24534a1SRasmus Lerdorf {
76136db28c8SIlia Alshanetsky 	return (!roundup) ? v1 >> 6 : (v1 + 63) >> 6;
762a24534a1SRasmus Lerdorf }
763a24534a1SRasmus Lerdorf 
7648d6cfb79SIlia Alshanetsky void gdFontCacheShutdown()
765a24534a1SRasmus Lerdorf {
7665e50336eSNuno Lopes 	gdMutexLock(gdFontCacheMutex);
7675e50336eSNuno Lopes 
7688d6cfb79SIlia Alshanetsky 	if (fontCache) {
7698d6cfb79SIlia Alshanetsky 		gdCacheDelete(fontCache);
7708d6cfb79SIlia Alshanetsky 		fontCache = NULL;
7718d6cfb79SIlia Alshanetsky 		FT_Done_FreeType(library);
7728d6cfb79SIlia Alshanetsky 	}
7735e50336eSNuno Lopes 
7745e50336eSNuno Lopes 	gdMutexUnlock(gdFontCacheMutex);
7758d6cfb79SIlia Alshanetsky }
7768d6cfb79SIlia Alshanetsky 
7778b079070SIlia Alshanetsky void gdFreeFontCache()
7788b079070SIlia Alshanetsky {
7798b079070SIlia Alshanetsky 	gdFontCacheShutdown();
7808b079070SIlia Alshanetsky }
7818b079070SIlia Alshanetsky 
7821cd4938eSPierre Joye void gdFontCacheMutexSetup()
7831cd4938eSPierre Joye {
7841cd4938eSPierre Joye 	gdMutexSetup(gdFontCacheMutex);
7851cd4938eSPierre Joye }
7861cd4938eSPierre Joye 
7871cd4938eSPierre Joye void gdFontCacheMutexShutdown()
7881cd4938eSPierre Joye {
7891cd4938eSPierre Joye 	gdMutexShutdown(gdFontCacheMutex);
7901cd4938eSPierre Joye }
7911cd4938eSPierre Joye 
7928d6cfb79SIlia Alshanetsky int gdFontCacheSetup(void)
7938d6cfb79SIlia Alshanetsky {
7948d6cfb79SIlia Alshanetsky 	if (fontCache) {
7958d6cfb79SIlia Alshanetsky 		/* Already set up */
7968d6cfb79SIlia Alshanetsky 		return 0;
7978d6cfb79SIlia Alshanetsky 	}
7988d6cfb79SIlia Alshanetsky 	if (FT_Init_FreeType(&library)) {
7998d6cfb79SIlia Alshanetsky 		return -1;
8008d6cfb79SIlia Alshanetsky 	}
8018d6cfb79SIlia Alshanetsky 	fontCache = gdCacheCreate (FONTCACHESIZE, fontTest, fontFetch, fontRelease);
8028d6cfb79SIlia Alshanetsky 	return 0;
803a24534a1SRasmus Lerdorf }
804a24534a1SRasmus Lerdorf 
8058d6cfb79SIlia Alshanetsky 
8067a8cade3SRasmus Lerdorf /********************************************************************/
8077a8cade3SRasmus Lerdorf /* gdImageStringFT -  render a utf8 string onto a gd image          */
80892514865SIlia Alshanetsky 
8097a8cade3SRasmus Lerdorf char *
81036db28c8SIlia Alshanetsky gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
81192514865SIlia Alshanetsky 		 double ptsize, double angle, int x, int y, char *string)
8126d1dc814SWez Furlong {
8138d6cfb79SIlia Alshanetsky 	return gdImageStringFTEx(im, brect, fg, fontlist, ptsize, angle, x, y, string, 0);
81417238616SMarcus Boerger }
81517238616SMarcus Boerger 
8166d1dc814SWez Furlong char *
817822b53b9SIlia Alshanetsky gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsize, double angle, int x, int y, char *string, gdFTStringExtraPtr strex)
8187a8cade3SRasmus Lerdorf {
819822b53b9SIlia Alshanetsky 	FT_BBox bbox, glyph_bbox;
820822b53b9SIlia Alshanetsky 	FT_Matrix matrix;
821822b53b9SIlia Alshanetsky 	FT_Vector pen, delta, penf;
822822b53b9SIlia Alshanetsky 	FT_Face face;
823822b53b9SIlia Alshanetsky 	FT_Glyph image;
824822b53b9SIlia Alshanetsky 	FT_GlyphSlot slot;
825822b53b9SIlia Alshanetsky 	FT_Bool use_kerning;
826822b53b9SIlia Alshanetsky 	FT_UInt glyph_index, previous;
827822b53b9SIlia Alshanetsky 	double sin_a = sin (angle);
828822b53b9SIlia Alshanetsky 	double cos_a = cos (angle);
829822b53b9SIlia Alshanetsky 	int len, i = 0, ch;
830822b53b9SIlia Alshanetsky 	int x1 = 0, y1 = 0;
8318990a91fSTakeshi Abe 	font_t *font;
8322d2f18deSTakeshi Abe 	fontkey_t fontkey;
833822b53b9SIlia Alshanetsky 	char *next;
834822b53b9SIlia Alshanetsky 	char *tmpstr = NULL;
835822b53b9SIlia Alshanetsky 	int render = (im && (im->trueColor || (fg <= 255 && fg >= -255)));
836822b53b9SIlia Alshanetsky 	FT_BitmapGlyph bm;
837822b53b9SIlia Alshanetsky 	/* 2.0.13: Bob Ostermann: don't force autohint, that's just for testing freetype and doesn't look as good */
838822b53b9SIlia Alshanetsky 	int render_mode = FT_LOAD_DEFAULT;
839e1b8cad2SIlia Alshanetsky 	int m, mfound;
840e1b8cad2SIlia Alshanetsky 	/* Now tuneable thanks to Wez Furlong */
84161026e39SIlia Alshanetsky 	double linespace = LINESPACE;
842822b53b9SIlia Alshanetsky 	/* 2.0.6: put this declaration with the other declarations! */
843822b53b9SIlia Alshanetsky 	/*
844822b53b9SIlia Alshanetsky 	*   make a new tweenColorCache on every call
845822b53b9SIlia Alshanetsky 	*   because caching colormappings between calls
846822b53b9SIlia Alshanetsky 	*   is not safe. If the im-pointer points to a
847822b53b9SIlia Alshanetsky 	*   brand new image, the cache gives out bogus
848822b53b9SIlia Alshanetsky 	*   colorindexes.          -- 27.06.2001 <krisku@arrak.fi>
849822b53b9SIlia Alshanetsky 	*/
850822b53b9SIlia Alshanetsky 	gdCache_head_t  *tc_cache;
851