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 */
35char *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
105char* 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
118static 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
194static gvtextlayout_engine_t gd_textlayout_engine = {
195 gd_textlayout,
196};
197#endif
198
199gvplugin_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