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