]> www.fi.muni.cz Git - evince.git/blob - backend/dvi/mdvi-lib/fontsrch.c
Reorganize source tree.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "mdvi.h"
51
52 #define HAVE_PROTOTYPES 1
53 #include <kpathsea/tex-file.h>
54 #include <kpathsea/tex-glyph.h>
55
56 struct _DviFontClass {
57         DviFontClass *next;
58         DviFontClass *prev;
59         DviFontInfo  info;
60         int     links;
61         int     id;
62 };
63
64 char *_mdvi_fallback_font = MDVI_FALLBACK_FONT;
65
66 /* this leaves classes 0 and 1 for `real' fonts */
67 #define MAX_CLASS       3
68 static ListHead font_classes[MAX_CLASS];
69 static int initialized = 0;
70
71 static void init_font_classes(void)
72 {
73         int     i;
74         
75         for(i = 0; i < MAX_CLASS; i++)
76                 listh_init(&font_classes[i]);
77         initialized = 1;
78 }
79
80 int     mdvi_get_font_classes(void)
81 {
82         return (MAX_CLASS - 2);
83 }
84
85 char    **mdvi_list_font_class(int klass)
86 {
87         char    **list;
88         int     i, n;
89         DviFontClass *fc;
90         
91         if(klass == -1)
92                 klass = MAX_CLASS-1;
93         if(klass < 0 || klass >= MAX_CLASS)
94                 return NULL;
95         n = font_classes[klass].count;
96         list = xnalloc(char *, n + 1);
97         fc = (DviFontClass *)font_classes[klass].head;
98         for(i = 0; i < n; fc = fc->next, i++) {
99                 list[i] = mdvi_strdup(fc->info.name);
100         }
101         list[i] = NULL;
102         return list;
103 }
104
105 int     mdvi_register_font_type(DviFontInfo *info, int klass)
106 {
107         DviFontClass *fc;
108
109         if(klass == -1)
110                 klass = MAX_CLASS-1;
111         if(klass < 0 || klass >= MAX_CLASS)
112                 return -1;
113         if(!initialized)
114                 init_font_classes();
115         fc = xalloc(struct _DviFontClass);
116         fc->links = 0;
117         fc->id = klass;
118         fc->info.name = mdvi_strdup(info->name);
119         fc->info.scalable = info->scalable;
120         fc->info.load = info->load;
121         fc->info.getglyph = info->getglyph;
122         fc->info.shrink0 = info->shrink0;
123         fc->info.shrink1 = info->shrink1;
124         fc->info.freedata = info->freedata;
125         fc->info.reset = info->reset;
126         fc->info.lookup = info->lookup;
127         fc->info.kpse_type = info->kpse_type;
128         listh_append(&font_classes[klass], LIST(fc));
129         return 0;
130 }
131
132 int     mdvi_unregister_font_type(const char *name, int klass)
133 {
134         DviFontClass *fc;
135         int     k;
136
137         if(klass == -1)
138                 klass = MAX_CLASS - 1;
139         
140         if(klass >= 0 && klass < MAX_CLASS) {
141                 k = klass;
142                 LIST_FOREACH(fc, DviFontClass, &font_classes[k]) {
143                         if(STREQ(fc->info.name, name))
144                                 break;
145                 }
146         } else if(klass < 0) {
147                 for(k = 0; k < MAX_CLASS; k++) {
148                         LIST_FOREACH(fc, DviFontClass, &font_classes[k]) {
149                                 if(STREQ(fc->info.name, name))
150                                         break;
151                         }
152                         if(fc) break;
153                 }
154         } else
155                 return -1;
156         
157         if(fc == NULL || fc->links)
158                 return -1;
159         /* remove it */
160         listh_remove(&font_classes[k], LIST(fc));
161         
162         /* and destroy it */
163         mdvi_free(fc->info.name);
164         mdvi_free(fc);
165         return 0;
166 }
167
168 static char *lookup_font(DviFontClass *ptr, const char *name, Ushort *h, Ushort *v)
169 {
170         char    *filename;
171
172         /*
173          * If the font type registered a function to do the lookup, use that. 
174          * Otherwise we use kpathsea.
175          */     
176         if(ptr->info.lookup)
177                 filename = ptr->info.lookup(name, h, v);
178         else if(ptr->info.kpse_type <= kpse_any_glyph_format) {
179                 kpse_glyph_file_type type;
180
181                 filename = kpse_find_glyph(name, Max(*h, *v),
182                         ptr->info.kpse_type, &type);
183                 /* if kpathsea returned a fallback font, reject it */
184                 if(filename && type.source == kpse_glyph_source_fallback) {
185                         mdvi_free(filename);
186                         filename = NULL;
187                 } else if(filename)
188                         *h = *v = type.dpi;
189         } else
190                 filename = kpse_find_file(name, ptr->info.kpse_type, 1);
191         return filename;
192 }
193
194 /*
195  * Class MAX_CLASS-1 is special: it consists of `metric' fonts that should
196  * be tried as a last resort
197  */
198 char    *mdvi_lookup_font(DviFontSearch *search)
199 {
200         int kid;
201         int k;
202         DviFontClass *ptr;
203         DviFontClass *last;
204         char    *filename = NULL;
205         const char *name;
206         Ushort  hdpi, vdpi;
207
208         if(search->id < 0)
209                 return NULL;
210         
211         if(search->curr == NULL) {
212                 /* this is the initial search */
213                 name = search->wanted_name;
214                 hdpi = search->hdpi;
215                 vdpi = search->vdpi;
216                 kid = 0;
217                 last = NULL;
218         } else {
219                 name = search->actual_name;
220                 hdpi = search->actual_hdpi;
221                 vdpi = search->actual_vdpi;
222                 kid = search->id;
223                 last = search->curr;
224         }
225
226         ptr = NULL;
227 again:
228         /* try all classes except MAX_CLASS-1 */
229         for(k = kid; !filename && k < MAX_CLASS-1; k++) {
230                 if(last == NULL)
231                         ptr = (DviFontClass *)font_classes[k].head;
232                 else
233                         ptr = last->next;
234                 while(ptr) {
235                         DEBUG((DBG_FONTS, "%d: trying `%s' at (%d,%d)dpi as `%s'\n",
236                                 k, name, hdpi, vdpi, ptr->info.name));
237                         /* lookup the font in this class */
238                         filename = lookup_font(ptr, name, &hdpi, &vdpi);
239                         if(filename)
240                                 break;
241                         ptr = ptr->next;
242                 }
243                 last = NULL;
244         }
245         if(filename != NULL) {
246                 search->id = k-1;
247                 search->curr = ptr;
248                 search->actual_name = name;
249                 search->actual_hdpi = hdpi;
250                 search->actual_vdpi = vdpi;
251                 search->info = &ptr->info;
252                 ptr->links++;
253                 return filename;
254         }
255
256         if(kid < MAX_CLASS - 1 && !STREQ(name, _mdvi_fallback_font)) {
257                 warning("font `%s' at %dx%d not found, trying `%s' instead\n",
258                         name, hdpi, vdpi, _mdvi_fallback_font);
259                 name = _mdvi_fallback_font;
260                 kid = 0;
261                 goto again;
262         }
263
264         /* we tried the fallback font, and all the `real' classes. Let's
265          * try the `metric' class now */
266         name = search->wanted_name;
267         hdpi = search->hdpi;
268         vdpi = search->vdpi;
269         if(kid == MAX_CLASS-1) {
270                 /* we were looking into this class from the beginning */
271                 if(last == NULL) {
272                         /* no more fonts to try */
273                         return NULL;
274                 }
275                 ptr = last->next;
276         } else {
277                 warning("font `%s' not found, trying metric files instead\n",
278                         name);
279                 ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head;
280         }
281
282 metrics:
283         while(ptr) {
284                 DEBUG((DBG_FONTS, "metric: trying `%s' at (%d,%d)dpi as `%s'\n",
285                         name, hdpi, vdpi, ptr->info.name));
286                 filename = lookup_font(ptr, name, &hdpi, &vdpi);
287                 if(filename)
288                         break;
289                 ptr = ptr->next;
290         }
291         if(filename != NULL) {
292                 if(STREQ(name, _mdvi_fallback_font))
293                         search->id = MAX_CLASS;
294                 else
295                         search->id = MAX_CLASS - 1;
296                 search->curr = ptr;
297                 search->actual_name = name;
298                 search->actual_hdpi = hdpi;
299                 search->actual_vdpi = vdpi;
300                 search->info = &ptr->info;
301                 ptr->links++;
302                 return filename;
303         }
304         if(!STREQ(name, _mdvi_fallback_font)) {
305                 warning("metric file for `%s' not found, trying `%s' instead\n",
306                         name, _mdvi_fallback_font);
307                 name = _mdvi_fallback_font;
308                 ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head;
309                 goto metrics;
310         }
311
312         search->id = -1;
313         search->actual_name = NULL;
314         
315         /* tough luck, nothing found */
316         return NULL;
317 }
318
319 /* called by `font_reference' to do the initial lookup */
320 DviFont *mdvi_add_font(const char *name, Int32 sum,
321         int hdpi, int vdpi, Int32 scale)
322 {
323         DviFont *font;
324         
325         font = xalloc(DviFont);
326         font->fontname = mdvi_strdup(name);
327         SEARCH_INIT(font->search, font->fontname, hdpi, vdpi);
328         font->filename = mdvi_lookup_font(&font->search);
329         if(font->filename == NULL) {
330                 /* this answer is final */
331                 mdvi_free(font->fontname);
332                 mdvi_free(font);
333                 return NULL;
334         }
335         font->hdpi = font->search.actual_hdpi;
336         font->vdpi = font->search.actual_vdpi;
337         font->scale = scale;
338         font->design = 0;
339         font->checksum = sum;
340         font->type = 0;
341         font->links = 0;
342         font->loc = 0;
343         font->hic = 0;
344         font->in = NULL;
345         font->chars = NULL;
346         font->subfonts = NULL;
347
348         return font;
349 }
350
351 int     mdvi_font_retry(DviParams *params, DviFont *font)
352 {
353         /* try the search again */
354         char    *filename;
355
356         ASSERT(font->search.curr != NULL);      
357         /* we won't be using this class anymore */
358         font->search.curr->links--;
359
360         filename = mdvi_lookup_font(&font->search);
361         if(filename == NULL)
362                 return -1;
363         mdvi_free(font->filename);
364         font->filename = filename;
365         /* copy the new information */
366         font->hdpi = font->search.actual_hdpi;
367         font->vdpi = font->search.actual_vdpi;
368
369         return 0;
370 }