1 | /* $Id$ $Revision$ */ |
2 | /* vim:set shiftwidth=4 ts=8: */ |
3 | |
4 | /************************************************************************* |
5 | * Copyright (c) 2011 AT&T Intellectual Property |
6 | * All rights reserved. This program and the accompanying materials |
7 | * are made available under the terms of the Eclipse Public License v1.0 |
8 | * which accompanies this distribution, and is available at |
9 | * http://www.eclipse.org/legal/epl-v10.html |
10 | * |
11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
12 | *************************************************************************/ |
13 | |
14 | #include "config.h" |
15 | |
16 | #include <stdio.h> |
17 | #include <stdlib.h> |
18 | #include <string.h> |
19 | #include "gvplugin_textlayout.h" |
20 | #include "gd.h" |
21 | |
22 | #ifdef HAVE_GD_FREETYPE |
23 | |
24 | /* fontsize at which text is omitted entirely */ |
25 | #define FONTSIZE_MUCH_TOO_SMALL 0.15 |
26 | /* fontsize at which text is rendered by a simple line */ |
27 | #define FONTSIZE_TOO_SMALL 1.5 |
28 | |
29 | #ifndef HAVE_GD_FONTCONFIG |
30 | /* gd_alternate_fontlist; |
31 | * Sometimes fonts are stored under a different name, |
32 | * especially on Windows. Without fontconfig, we provide |
33 | * here some rudimentary name mapping. |
34 | */ |
35 | char *gd_alternate_fontlist(char *font) |
36 | { |
37 | static char *fontbuf; |
38 | static int fontbufsz; |
39 | char *p, *fontlist; |
40 | int len; |
41 | |
42 | len = strlen(font) + 1; |
43 | if (len > fontbufsz) { |
44 | fontbufsz = 2 * len; |
45 | if (fontbuf == NULL) |
46 | fontbuf = malloc(fontbufsz); |
47 | else |
48 | fontbuf = realloc(fontbuf, fontbufsz); |
49 | } |
50 | |
51 | /* fontbuf to contain font without style descriptions like -Roman or -Italic */ |
52 | strcpy(fontbuf, font); |
53 | if ((p = strchr(fontbuf, '-')) || (p = strchr(fontbuf, '_'))) |
54 | *p = 0; |
55 | |
56 | fontlist = fontbuf; |
57 | if ((strcasecmp(font, "times-bold" ) == 0) |
58 | || (strcasecmp(fontbuf, "timesbd" ) == 0) |
59 | || (strcasecmp(fontbuf, "timesb" ) == 0)) |
60 | fontlist = "timesbd;Timesbd;TIMESBD;timesb;Timesb;TIMESB" ; |
61 | |
62 | else if ((strcasecmp(font, "times-italic" ) == 0) |
63 | || (strcasecmp(fontbuf, "timesi" ) == 0)) |
64 | fontlist = "timesi;Timesi;TIMESI" ; |
65 | |
66 | else if ((strcasecmp(font, "timesnewroman" ) == 0) |
67 | || (strcasecmp(font, "timesnew" ) == 0) |
68 | || (strcasecmp(font, "timesroman" ) == 0) |
69 | || (strcasecmp(fontbuf, "times" ) == 0)) |
70 | fontlist = "times;Times;TIMES" ; |
71 | |
72 | else if ((strcasecmp(font, "arial-bold" ) == 0) |
73 | || (strcasecmp(fontbuf, "arialb" ) == 0)) |
74 | fontlist = "arialb;Arialb;ARIALB" ; |
75 | |
76 | else if ((strcasecmp(font, "arial-italic" ) == 0) |
77 | || (strcasecmp(fontbuf, "ariali" ) == 0)) |
78 | fontlist = "ariali;Ariali;ARIALI" ; |
79 | |
80 | else if (strcasecmp(fontbuf, "helvetica" ) == 0) |
81 | fontlist = "helvetica;Helvetica;HELVETICA;arial;Arial;ARIAL" ; |
82 | |
83 | else if (strcasecmp(fontbuf, "arial" ) == 0) |
84 | fontlist = "arial;Arial;ARIAL" ; |
85 | |
86 | else if (strcasecmp(fontbuf, "courier" ) == 0) |
87 | fontlist = "courier;Courier;COURIER;cour" ; |
88 | |
89 | return fontlist; |
90 | } |
91 | #endif /* HAVE_GD_FONTCONFIG */ |
92 | |
93 | /* gd_psfontResolve: |
94 | * * Construct alias for postscript fontname. |
95 | * * NB. Uses a static array - non-reentrant. |
96 | * */ |
97 | |
98 | #define ADD_ATTR(a) \ |
99 | if (a) { \ |
100 | strcat(buf, comma ? " " : ", "); \ |
101 | comma = 1; \ |
102 | strcat(buf, a); \ |
103 | } |
104 | |
105 | char* gd_psfontResolve (PostscriptAlias* pa) |
106 | { |
107 | static char buf[1024]; |
108 | int comma=0; |
109 | strcpy(buf, pa->family); |
110 | |
111 | ADD_ATTR(pa->weight); |
112 | ADD_ATTR(pa->stretch); |
113 | ADD_ATTR(pa->style); |
114 | |
115 | return buf; |
116 | } |
117 | |
118 | static boolean gd_textlayout(textspan_t * span, char **fontpath) |
119 | { |
120 | char *err, *fontlist, *fontname; |
121 | double fontsize; |
122 | int brect[8]; |
123 | gdFTStringExtra strex; |
124 | #ifdef HAVE_GD_FONTCONFIG |
125 | PostscriptAlias *pA; |
126 | #endif |
127 | |
128 | fontname = span->font->name; |
129 | fontsize = span->font->size; |
130 | |
131 | strex.fontpath = NULL; |
132 | strex.flags = gdFTEX_RETURNFONTPATHNAME | gdFTEX_RESOLUTION; |
133 | strex.hdpi = strex.vdpi = POINTS_PER_INCH; |
134 | |
135 | if (strstr(fontname, "/" )) |
136 | strex.flags |= gdFTEX_FONTPATHNAME; |
137 | else |
138 | strex.flags |= gdFTEX_FONTCONFIG; |
139 | |
140 | span->size.x = 0.0; |
141 | span->size.y = 0.0; |
142 | span->yoffset_layout = 0.0; |
143 | |
144 | span->layout = NULL; |
145 | span->free_layout = NULL; |
146 | |
147 | span->yoffset_centerline = 0.1 * fontsize; |
148 | |
149 | if (fontname) { |
150 | if (fontsize <= FONTSIZE_MUCH_TOO_SMALL) { |
151 | return TRUE; /* OK, but ignore text entirely */ |
152 | } else if (fontsize <= FONTSIZE_TOO_SMALL) { |
153 | /* draw line in place of text */ |
154 | /* fake a finite fontsize so that line length is calculated */ |
155 | fontsize = FONTSIZE_TOO_SMALL; |
156 | } |
157 | /* call gdImageStringFT with null *im to get brect and to set font cache */ |
158 | #ifdef HAVE_GD_FONTCONFIG |
159 | gdFTUseFontConfig(1); /* tell gd that we really want to use fontconfig, 'cos it s not the default */ |
160 | pA = span->font->postscript_alias; |
161 | if (pA) |
162 | fontlist = gd_psfontResolve (pA); |
163 | else |
164 | fontlist = fontname; |
165 | #else |
166 | fontlist = gd_alternate_fontlist(fontname); |
167 | #endif |
168 | |
169 | err = gdImageStringFTEx(NULL, brect, -1, fontlist, |
170 | fontsize, 0, 0, 0, span->str, &strex); |
171 | |
172 | if (err) { |
173 | agerr(AGERR,"%s\n" , err); |
174 | return FALSE; /* indicate error */ |
175 | } |
176 | |
177 | if (fontpath) |
178 | *fontpath = strex.fontpath; |
179 | else |
180 | free (strex.fontpath); /* strup'ed in libgd */ |
181 | |
182 | if (span->str && span->str[0]) { |
183 | /* can't use brect on some archtectures if strlen 0 */ |
184 | span->size.x = (double) (brect[4] - brect[0]); |
185 | /* 1.2 specifies how much extra space to leave between lines; |
186 | * see LINESPACING in const.h. |
187 | */ |
188 | span->size.y = (int)(fontsize * 1.2); |
189 | } |
190 | } |
191 | return TRUE; |
192 | } |
193 | |
194 | static gvtextlayout_engine_t gd_textlayout_engine = { |
195 | gd_textlayout, |
196 | }; |
197 | #endif |
198 | |
199 | gvplugin_installed_t gvtextlayout_gd_types[] = { |
200 | #ifdef HAVE_GD_FREETYPE |
201 | {0, "textlayout" , 2, &gd_textlayout_engine, NULL}, |
202 | #endif |
203 | {0, NULL, 0, NULL, NULL} |
204 | }; |
205 | |