]> www.fi.muni.cz Git - evince.git/blob - backend/dvi/mdvi-lib/fontsrch.c
Update FSF address everywhere.
[evince.git] / backend / dvi / mdvi-lib / fontsrch.c
1 /* fontsearch.c -- implements the font lookup mechanism in MDVI */
2 /*
3  * Copyright (C) 2000, Matias Atria
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 /*
21  * How this works:
22  *   Fonts are divided into MAX_CLASS priority classes. The first
23  * MAX_CLASS-1 ones correspond to `real' fonts (pk, gf, vf, type1, truetype,
24  * etc). The last one corresponds to `metric' fonts that are used as a last
25  * resort (tfm, afm, ofm, ...). When a font is looked up, it is tried in a
26  * `high' priority class (0 being the highest priority). The priority is
27  * lowered until it reaches MAX_CLASS-1. Then the whole thing is repeated
28  * for the fallback font. When the search reaches MAX_CLASS-1, we lookup the
29  * original font, and then the fallback font. The search can be done
30  * incrementally, with several calls to mdvi_lookup_font(). If this function
31  * is called again to continue a search, the function assumes the previous
32  * font it returned was not valid, and it goes on to the next step.
33  *
34  * Reason for this:
35  *   Some font types are quite expensive to load (e.g. Type1),  so loading
36  * them is deferred until the last possible moment. This means that a font that
37  * was supposed to exist may have to be discarded. Until now, MDVI had no ability to
38  * "resume" a search, so in this case it would have produced an error, regardless
39  * of whether the offending font existed in other formats.
40  *   Also, given the large number of font types supported by MDVI, some mechanism
41  * was necessary to bring some order into the chaos.
42  *
43  * This mechanism fixes these two problems. For the first one, a search can
44  * be "resumed" and all the font formats tried for the missing font, and
45  * again for the fallback font (see above). As for the second, the
46  * hierarchical division in classes gives a lot of flexibility in how the
47  * fonts are configured.
48  */
49
50 #include <config.h>
51 #include "mdvi.h"
52
53 #define HAVE_PROTOTYPES 1
54 #include <kpathsea/tex-file.h>
55 #include <kpathsea/tex-glyph.h>
56
57 struct _DviFontClass {
58         DviFontClass *next;
59         DviFontClass *prev;
60         DviFontInfo  info;
61         int     links;
62         int     id;
63 };
64
65 char *_mdvi_fallback_font = MDVI_FALLBACK_FONT;
66
67 /* this leaves classes 0 and 1 for `real' fonts */
68 #define MAX_CLASS       3
69 static ListHead font_classes[MAX_CLASS];
70 static int initialized = 0;
71
72 static void init_font_classes(void)
73 {
74         int     i;
75         
76         for(i = 0; i < MAX_CLASS; i++)
77                 listh_init(&font_classes[i]);
78         initialized = 1;
79 }
80
81 int     mdvi_get_font_classes(void)
82 {
83         return (MAX_CLASS - 2);
84 }
85
86 char    **mdvi_list_font_class(int klass)
87 {
88         char    **list;
89         int     i, n;
90         DviFontClass *fc;
91         
92         if(klass == -1)
93                 klass = MAX_CLASS-1;
94         if(klass < 0 || klass >= MAX_CLASS)
95                 return NULL;
96         n = font_classes[klass].count;
97         list = xnalloc(char *, n + 1);
98         fc = (DviFontClass *)font_classes[klass].head;
99         for(i = 0; i < n; fc = fc->next, i++) {
100                 list[i] = mdvi_strdup(fc->info.name);
101         }
102         list[i] = NULL;
103         return list;
104 }
105
106 int     mdvi_register_font_type(DviFontInfo *info, int klass)
107 {
108         DviFontClass *fc;
109
110         if(klass == -1)
111                 klass = MAX_CLASS-1;
112         if(klass < 0 || klass >= MAX_CLASS)
113                 return -1;
114         if(!initialized)
115                 init_font_classes();
116         fc = xalloc(struct _DviFontClass);
117         fc->links = 0;
118         fc->id = klass;
119         fc->info.name = mdvi_strdup(info->name);
120         fc->info.scalable = info->scalable;
121         fc->info.load = info->load;
122         fc->info.getglyph = info->getglyph;
123         fc->info.shrink0 = info->shrink0;
124         fc->info.shrink1 = info->shrink1;
125         fc->info.freedata = info->freedata;
126         fc->info.reset = info->reset;
127         fc->info.lookup = info->lookup;
128         fc->info.kpse_type = info->kpse_type;
129         listh_append(&font_classes[klass], LIST(fc));
130         return 0;
131 }
132
133 int     mdvi_unregister_font_type(const char *name, int klass)
134 {
135         DviFontClass *fc;
136         int     k;
137
138         if(klass == -1)
139                 klass = MAX_CLASS - 1;
140         
141         if(klass >= 0 && klass < MAX_CLASS) {
142                 k = klass;
143                 LIST_FOREACH(fc, DviFontClass, &font_classes[k]) {
144                         if(STREQ(fc->info.name, name))
145                                 break;
146                 }
147         } else if(klass < 0) {
148                 for(k = 0; k < MAX_CLASS; k++) {
149                         LIST_FOREACH(fc, DviFontClass, &font_classes[k]) {
150                                 if(STREQ(fc->info.name, name))
151                                         break;
152                         }
153                         if(fc) break;
154                 }
155         } else
156                 return -1;
157         
158         if(fc == NULL || fc->links)
159                 return -1;
160         /* remove it */
161         listh_remove(&font_classes[k], LIST(fc));
162         
163         /* and destroy it */
164         mdvi_free(fc->info.name);
165         mdvi_free(fc);
166         return 0;
167 }
168
169 static char *lookup_font(DviFontClass *ptr, const char *name, Ushort *h, Ushort *v)
170 {
171         char    *filename;
172
173         /*
174          * If the font type registered a function to do the lookup, use that. 
175          * Otherwise we use kpathsea.
176          */     
177         if(ptr->info.lookup)
178                 filename = ptr->info.lookup(name, h, v);
179         else if(ptr->info.kpse_type <= kpse_any_glyph_format) {
180                 kpse_glyph_file_type type;
181
182                 filename = kpse_find_glyph(name, Max(*h, *v),
183                         ptr->info.kpse_type, &type);
184                 /* if kpathsea returned a fallback font, reject it */
185                 if(filename && type.source == kpse_glyph_source_fallback) {
186                         mdvi_free(filename);
187                         filename = NULL;
188                 } else if(filename)
189                         *h = *v = type.dpi;
190         } else
191                 filename = kpse_find_file(name, ptr->info.kpse_type, 1);
192         return filename;
193 }
194
195 /*
196  * Class MAX_CLASS-1 is special: it consists of `metric' fonts that should
197  * be tried as a last resort
198  */
199 char    *mdvi_lookup_font(DviFontSearch *search)
200 {
201         int kid;
202         int k;
203         DviFontClass *ptr;
204         DviFontClass *last;
205         char    *filename = NULL;
206         const char *name;
207         Ushort  hdpi, vdpi;
208
209         if(search->id < 0)
210                 return NULL;
211         
212         if(search->curr == NULL) {
213                 /* this is the initial search */
214                 name = search->wanted_name;
215                 hdpi = search->hdpi;
216                 vdpi = search->vdpi;
217                 kid = 0;
218                 last = NULL;
219         } else {
220                 name = search->actual_name;
221                 hdpi = search->actual_hdpi;
222                 vdpi = search->actual_vdpi;
223                 kid = search->id;
224                 last = search->curr;
225         }
226
227         ptr = NULL;
228 again:
229         /* try all classes except MAX_CLASS-1 */
230         for(k = kid; !filename && k < MAX_CLASS-1; k++) {
231                 if(last == NULL)
232                         ptr = (DviFontClass *)font_classes[k].head;
233                 else
234                         ptr = last->next;
235                 while(ptr) {
236                         DEBUG((DBG_FONTS, "%d: trying `%s' at (%d,%d)dpi as `%s'\n",
237                                 k, name, hdpi, vdpi, ptr->info.name));
238                         /* lookup the font in this class */
239                         filename = lookup_font(ptr, name, &hdpi, &vdpi);
240                         if(filename)
241                                 break;
242                         ptr = ptr->next;
243                 }
244                 last = NULL;
245         }
246         if(filename != NULL) {
247                 search->id = k-1;
248                 search->curr = ptr;
249                 search->actual_name = name;
250                 search->actual_hdpi = hdpi;
251                 search->actual_vdpi = vdpi;
252                 search->info = &ptr->info;
253                 ptr->links++;
254                 return filename;
255         }
256
257         if(kid < MAX_CLASS - 1 && !STREQ(name, _mdvi_fallback_font)) {
258                 mdvi_warning("font `%s' at %dx%d not found, trying `%s' instead\n",
259                              name, hdpi, vdpi, _mdvi_fallback_font);
260                 name = _mdvi_fallback_font;
261                 kid = 0;
262                 goto again;
263         }
264
265         /* we tried the fallback font, and all the `real' classes. Let's
266          * try the `metric' class now */
267         name = search->wanted_name;
268         hdpi = search->hdpi;
269         vdpi = search->vdpi;
270         if(kid == MAX_CLASS-1) {
271                 /* we were looking into this class from the beginning */
272                 if(last == NULL) {
273                         /* no more fonts to try */
274                         return NULL;
275                 }
276                 ptr = last->next;
277         } else {
278                 mdvi_warning("font `%s' not found, trying metric files instead\n",
279                              name);
280                 ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head;
281         }
282
283 metrics:
284         while(ptr) {
285                 DEBUG((DBG_FONTS, "metric: trying `%s' at (%d,%d)dpi as `%s'\n",
286                         name, hdpi, vdpi, ptr->info.name));
287                 filename = lookup_font(ptr, name, &hdpi, &vdpi);
288                 if(filename)
289                         break;
290                 ptr = ptr->next;
291         }
292         if(filename != NULL) {
293                 if(STREQ(name, _mdvi_fallback_font))
294                         search->id = MAX_CLASS;
295                 else
296                         search->id = MAX_CLASS - 1;
297                 search->curr = ptr;
298                 search->actual_name = name;
299                 search->actual_hdpi = hdpi;
300                 search->actual_vdpi = vdpi;
301                 search->info = &ptr->info;
302                 ptr->links++;
303                 return filename;
304         }
305         if(!STREQ(name, _mdvi_fallback_font)) {
306                 mdvi_warning("metric file for `%s' not found, trying `%s' instead\n",
307                              name, _mdvi_fallback_font);
308                 name = _mdvi_fallback_font;
309                 ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head;
310                 goto metrics;
311         }
312
313         search->id = -1;
314         search->actual_name = NULL;
315         
316         /* tough luck, nothing found */
317         return NULL;
318 }
319
320 /* called by `font_reference' to do the initial lookup */
321 DviFont *mdvi_add_font(const char *name, Int32 sum,
322         int hdpi, int vdpi, Int32 scale)
323 {
324         DviFont *font;
325         
326         font = xalloc(DviFont);
327         font->fontname = mdvi_strdup(name);
328         SEARCH_INIT(font->search, font->fontname, hdpi, vdpi);
329         font->filename = mdvi_lookup_font(&font->search);
330         if(font->filename == NULL) {
331                 /* this answer is final */
332                 mdvi_free(font->fontname);
333                 mdvi_free(font);
334                 return NULL;
335         }
336         font->hdpi = font->search.actual_hdpi;
337         font->vdpi = font->search.actual_vdpi;
338         font->scale = scale;
339         font->design = 0;
340         font->checksum = sum;
341         font->type = 0;
342         font->links = 0;
343         font->loc = 0;
344         font->hic = 0;
345         font->in = NULL;
346         font->chars = NULL;
347         font->subfonts = NULL;
348
349         return font;
350 }
351
352 int     mdvi_font_retry(DviParams *params, DviFont *font)
353 {
354         /* try the search again */
355         char    *filename;
356
357         ASSERT(font->search.curr != NULL);      
358         /* we won't be using this class anymore */
359         font->search.curr->links--;
360
361         filename = mdvi_lookup_font(&font->search);
362         if(filename == NULL)
363                 return -1;
364         mdvi_free(font->filename);
365         font->filename = filename;
366         /* copy the new information */
367         font->hdpi = font->search.actual_hdpi;
368         font->vdpi = font->search.actual_vdpi;
369
370         return 0;
371 }