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