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