1 /****************************************************************************
2 * RRDtool 1.2.30 Copyright by Tobi Oetiker, 1997-2009
3 ****************************************************************************
4 * rrd_afm.h Parsing afm tables to find width of strings.
5 ****************************************************************************
6 * $Id$
7 */
9 #include "../rrd_config.h"
11 #include "rrd_afm.h"
12 #include "rrd_afm_data.h"
14 #include <stdio.h>
16 #ifdef HAVE_STRING_H
17 #include <string.h>
18 #endif
20 #include "unused.h"
22 #if 0
23 # define DEBUG 1
24 # define DLOG(x) fprintf x
25 #else
26 # define DEBUG 0
27 # define DLOG(x)
28 #endif
30 /* Adobe SVG View and Batik 1.1.1 can't handle ligatures.
31 So disable it as we just waste speed.
32 Besides, it doesn't matter much in normal text.
33 */
34 #define ENABLE_LIGATURES 0
36 static const afm_fontinfo *afm_last_used_font = NULL;
37 static const char *last_unknown_font = NULL;
39 #define is_font(p, name) \
40 (!strcmp(p->postscript_name, name) || !strcmp(p->fullname, name))
42 static const afm_fontinfo *afm_searchfont(const char *name)
43 {
44 int i;
45 const afm_fontinfo *p = afm_last_used_font;
46 if (p && is_font(p, name))
47 return p;
48 p = afm_fontinfolist;
49 for (i = 0; i < afm_fontinfo_count; i++, p++) {
50 if (is_font(p, name)) {
51 afm_last_used_font = p;
52 return p;
53 }
54 }
55 return NULL;
56 }
59 /* returns always a font, never NULL.
60 The rest of the code depends on the result never being NULL.
61 See rrd_afm.h */
62 static const afm_fontinfo *afm_findfont(const char *name)
63 {
64 const afm_fontinfo *p = afm_searchfont(name);
65 if (p)
66 return p;
67 if (!last_unknown_font || strcmp(name, last_unknown_font)) {
68 fprintf(stderr, "Can't find font '%s'\n", name);
69 last_unknown_font = name;
70 }
71 p = afm_searchfont(RRD_AFM_DEFAULT_FONT);
72 if (p)
73 return p;
74 return afm_fontinfolist; /* anything, just anything. */
75 }
77 const char *afm_get_font_postscript_name(const char* font)
78 {
79 const afm_fontinfo *p = afm_findfont(font);
80 return p->postscript_name;
81 }
83 const char *afm_get_font_name(const char* font)
84 {
85 const afm_fontinfo *p = afm_findfont(font);
86 return p->fullname;
87 }
89 double afm_get_ascender(const char* font, double size)
90 {
91 const afm_fontinfo *p = afm_findfont(font);
92 return size * p->ascender / 1000.0;
93 }
95 double afm_get_descender(const char* font, double size)
96 {
97 const afm_fontinfo *p = afm_findfont(font);
98 return size * p->descender / 1000.0;
99 }
101 static int afm_find_char_index(const afm_fontinfo *fontinfo,
102 afm_cunicode ch1)
103 {
104 int idx = ch1 - 32;
105 afm_cuint16 *indexP;
106 int numIndexChars, i;
107 if (idx <= 0)
108 return 0;
109 if (idx <= 126 - 32)
110 return idx;
111 indexP = fontinfo->highchars_index;
112 if (indexP == 0)
113 return 0;
114 numIndexChars = fontinfo->highchars_count;
115 DLOG((stderr, " find highbit, num = %d\n", numIndexChars));
116 if (ch1 >= 161 && ch1 <= 255) {
117 idx = ch1 - 161;
118 DLOG((stderr, " 161, idx = %d -> %d\n", idx, indexP[idx]));
119 if (idx < numIndexChars && indexP[idx] == ch1) {
120 idx += 127 - 32;
121 DLOG((stderr, " 161-guessed ok to %d\n", idx));
122 return idx;
123 }
124 }
125 for (i = 0; i < numIndexChars; i++) {
126 DLOG((stderr, " compares to %d -> %d\n", indexP[i], i));
127 if (indexP[i] == ch1)
128 return i + 127 - 32;
129 }
130 DLOG((stderr, "Did not find %d in highchars_index ??\n", ch1));
131 return 0;
132 }
134 #if ENABLE_LIGATURES
135 static afm_cunicode afm_find_combined_ligature(const afm_fontinfo *fontinfo,
136 afm_cunicode ch1, afm_cunicode ch2)
137 {
138 afm_cunicode *p = fontinfo->ligatures;
139 int num = fontinfo->ligatures_count;
140 int i;
141 if (!num)
142 return 0;
143 DLOG((stderr, " find-lig, num = %d\n", num));
144 for (i = 0; i < num; i++, p += 3) {
145 DLOG((stderr, " lig: %d + %d -> %d (%c %c %c)\n",
146 p[0], p[1], p[2], p[0], p[1], p[2]));
147 if (ch1 == *p && ch2 == p[1]) {
148 DLOG((stderr, " matches.\n"));
149 return p[2];
150 }
151 }
152 return 0;
153 }
154 #endif
156 #define READ_ESCAPED(p, val) \
157 if ((val = *p++) == 0) { \
158 val = 254 + *p++; \
159 } else if (!--val) { \
160 val = *p++ << 8; \
161 val |= *p++; \
162 }
165 static long afm_find_kern(const afm_fontinfo *fontinfo,
166 int kern_idx, afm_cunicode ch2)
167 {
168 afm_cuint8 *p8 = fontinfo->kerning_data + kern_idx;
169 int num;
170 READ_ESCAPED(p8, num);
171 DLOG((stderr, " find kern, num pairs = %d\n", num));
172 while (num > 0) {
173 afm_unicode ch;
174 READ_ESCAPED(p8, ch);
175 DLOG((stderr, " pair-char = %d\n", ch));
176 if (ch == ch2) {
177 DLOG((stderr, " got kern = %d\n", *(afm_csint8*)p8));
178 return *(afm_csint8*)p8;
179 }
180 p8++;
181 num--;
182 }
183 return 0;
184 }
186 /* measure width of a text string */
187 double afm_get_text_width( double start, const char* font, double size,
188 double tabwidth, const char* text)
189 {
190 #ifdef HAVE_MBSTOWCS
191 size_t clen = strlen(text) + 1;
192 wchar_t *cstr = malloc(sizeof(wchar_t) * clen); /* yes we are allocating probably too much here, I know */
193 int text_count = mbstowcs(cstr, text, clen);
194 double w;
195 if (text_count == -1)
196 text_count = mbstowcs(cstr, "Enc-Err", 6);
197 #ifdef __APPLE__
198 while (text_count > 0) {
199 text_count--;
200 cstr[text_count] = afm_fix_osx_charset(cstr[text_count]); /* unsafe macro */
201 }
202 #endif
203 w = afm_get_text_width_wide(start, font, size, tabwidth, cstr);
204 free(cstr);
205 return w;
206 #else
207 return afm_get_text_width_wide(start, font, size, tabwidth, text);
208 #endif
209 }
211 double afm_get_text_width_wide( double UNUSED(start), const char* font, double size,
212 double UNUSED(tabwidth), const afm_char* text)
213 {
214 const afm_fontinfo *fontinfo = afm_findfont(font);
215 long width = 0;
216 double widthf;
217 const afm_char *up = text;
218 DLOG((stderr, "================= %s\n", text));
219 if (fontinfo == NULL) {
220 while (*up)
221 up++;
222 return size * (up - text);
223 }
224 while (1) {
225 afm_unicode ch1, ch2;
226 int idx1, kern_idx;
227 if ((ch1 = *up) == 0)
228 break;
229 ch2 = *++up;
230 DLOG((stderr, "------------- Loop: %d + %d (%c%c) at %d\n",
231 ch1, ch2, ch1, ch2 ? ch2 : ' ',
232 (up - (const unsigned char*)text) - 1));
233 idx1 = afm_find_char_index(fontinfo, ch1);
234 DLOG((stderr, " idx1 = %d\n", idx1));
235 #if ENABLE_LIGATURES
236 if (ch2) {
237 int ch1_new = afm_find_combined_ligature(fontinfo, ch1, ch2);
238 DLOG((stderr, " lig-ch = %d\n", ch1_new));
239 if (ch1_new) {
240 ch1 = ch1_new;
241 idx1 = afm_find_char_index(fontinfo, ch1);
242 ch2 = *++up;
243 DLOG((stderr, " -> idx1 = %d, ch2 = %d (%c)\n",
244 idx1, ch2, ch2 ? ch2 : ' '));
245 }
246 }
247 #endif
248 width += fontinfo->widths[idx1];
249 DLOG((stderr, "Plain width of %d = %d\n", ch1, fontinfo->widths[idx1]));
250 if (fontinfo->kerning_index && ch2) {
251 kern_idx = fontinfo->kerning_index[idx1];
252 DLOG((stderr, " kern_idx = %d\n", kern_idx));
253 if (kern_idx > 0)
254 width += afm_find_kern(fontinfo, kern_idx, ch2);
255 }
256 }
257 widthf = (width * 6 / 1000.0) * size;
258 DLOG((stderr, "Returns %ld (%ld) -> %f\n", width, width * 6, widthf));
259 return widthf;
260 }
262 #ifdef __APPLE__
263 const unsigned char afm_mac2iso[128] = {
264 '\xC4', '\xC5', '\xC7', '\xC9', '\xD1', '\xD6', '\xDC', '\xE1', /* 80 */
265 '\xE0', '\xE2', '\xE4', '\xE3', '\xE5', '\xE7', '\xE9', '\xE8', /* 88 */
266 '\xEA', '\xEB', '\xED', '\xEC', '\xEE', '\xEF', '\xF1', '\xF3', /* 90 */
267 '\xF2', '\xF4', '\xF6', '\xF5', '\xFA', '\xF9', '\xFB', '\xFC', /* 98 */
268 '\xDD', '\xB0', '\xA2', '\xA3', '\xA7', ' ', '\xB6', '\xDF', /* A0 */
269 '\xAE', '\xA9', ' ', '\xB4', '\xA8', ' ', '\xC6', '\xD8', /* A8 */
270 ' ', '\xB1', '\xBE', ' ', '\xA5', '\xB5', ' ', ' ', /* B0 */
271 '\xBD', '\xBC', ' ', '\xAA', '\xBA', ' ', '\xE6', '\xF8', /* B8 */
272 '\xBF', '\xA1', '\xAC', ' ', ' ', ' ', ' ', '\xAB', /* C0 */
273 '\xBB', ' ', '\xA0', '\xC0', '\xC3', '\xD5', ' ', '\xA6', /* C8 */
274 '\xAD', ' ', '"', '"', '\'', '\'', '\xF7', '\xD7', /* D0 */
275 '\xFF', ' ', ' ', '\xA4', '\xD0', '\xF0', '\xDE', '\xFE', /* D8 */
276 '\xFD', '\xB7', ' ', ' ', ' ', '\xC2', '\xCA', '\xC1', /* E0 */
277 '\xCB', '\xC8', '\xCD', '\xCE', '\xCF', '\xCC', '\xD3', '\xD4', /* E8 */
278 ' ', '\xD2', '\xDA', '\xDB', '\xD9', ' ', ' ', ' ', /* F0 */
279 '\xAF', ' ', ' ', ' ', '\xB8', ' ', ' ', ' ', /* F8 */
280 };
281 #endif