]> www.fi.muni.cz Git - evince.git/blob - dvi/mdvi-lib/fontmap.c
Do not include ev-poppler.h when pdf is disabled.
[evince.git] / dvi / mdvi-lib / fontmap.c
1 /* encoding.c - functions to manipulate encodings and fontmaps */
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 #include <string.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <sys/stat.h>
25
26 #include "mdvi.h"
27 #include "private.h"
28
29 #include <kpathsea/expand.h>
30 #include <kpathsea/pathsearch.h>
31
32 typedef struct _DviFontMap DviFontMap;
33
34 struct _DviFontMap {
35         ListHead entries;
36         DviHashTable fonts;
37 };
38
39 typedef struct _PSFontMap {
40         struct _PSFontMap *next;
41         struct _PSFontMap *prev;
42         char    *psname;
43         char    *mapname;
44         char    *fullname;
45 } PSFontMap;
46
47 /* these variables control PS font maps */
48 static char *pslibdir = NULL;   /* path where we look for PS font maps */
49 static char *psfontdir = NULL;  /* PS font search path */
50 static int psinitialized = 0;   /* did we expand the path already? */
51
52 static ListHead psfonts = MDVI_EMPTY_LIST_HEAD;
53 static DviHashTable pstable = MDVI_EMPTY_HASH_TABLE;
54
55 static ListHead fontmaps;
56 static DviHashTable maptable;
57 static int fontmaps_loaded = 0;
58
59 #define MAP_HASH_SIZE   57
60 #define ENC_HASH_SIZE   31
61 #define PSMAP_HASH_SIZE 57
62
63 /* this hash table should be big enough to 
64  * hold (ideally) one glyph name per bucket */
65 #define ENCNAME_HASH_SIZE       131 /* most TeX fonts have 128 glyphs */
66
67 static ListHead encodings = MDVI_EMPTY_LIST_HEAD;
68 static DviEncoding *tex_text_encoding = NULL;
69 static DviEncoding *default_encoding = NULL;
70
71 /* we keep two hash tables for encodings: one for their base files (e.g.
72  * "8r.enc"), and another one for their names (e.g. "TeXBase1Encoding") */ 
73 static DviHashTable enctable = MDVI_EMPTY_HASH_TABLE;
74 static DviHashTable enctable_file = MDVI_EMPTY_HASH_TABLE;
75
76 /* the TeX text encoding, from dvips */
77 static char *tex_text_vector[256] = {
78         "Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma", "Upsilon",
79         "Phi", "Psi", "Omega", "arrowup", "arrowdown", "quotesingle",
80         "exclamdown", "questiondown", "dotlessi", "dotlessj", "grave",
81         "acute", "caron", "breve", "macron", "ring", "cedilla",
82         "germandbls", "ae", "oe", "oslash", "AE", "OE", "Oslash", "space",
83         "exclam", "quotedbl", "numbersign", "dollar", "percent",
84         "ampersand", "quoteright", "parenleft", "parenright", "asterisk",
85         "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
86         "three", "four", "five", "six", "seven", "eight", "nine", "colon",
87         "semicolon", "less", "equal", "greater", "question", "at", "A", "B",
88         "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
89         "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
90         "bracketleft", "backslash", "bracketright", "circumflex",
91         "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h",
92         "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
93         "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "tilde",
94         "dieresis", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
95         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
96         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
97         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
98         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
100 };
101
102 static void ps_init_default_paths __PROTO((void));
103 static int      mdvi_set_default_encoding __PROTO((const char *name));
104 static int      mdvi_init_fontmaps __PROTO((void));
105
106 /* 
107  * What we do here is allocate one block large enough to hold the entire
108  * file (these files are small) minus the leading comments. This is much
109  * better than allocating up to 256 tiny strings per encoding vector. */
110 static int read_encoding(DviEncoding *enc)
111 {
112         FILE    *in;
113         int     curr;
114         char    *line;
115         char    *name;
116         char    *next;
117         struct stat st;
118         
119         ASSERT(enc->private == NULL);
120
121         in = fopen(enc->filename, "r");
122         if(in == NULL) {
123                 DEBUG((DBG_FMAP, "%s: could not read `%s' (%s)\n",
124                         enc->name, enc->filename, strerror(errno)));
125                 return -1;
126         }
127         if(fstat(fileno(in), &st) < 0) {
128                 /* should not happen */
129                 fclose(in);
130                 return -1;
131         }
132         st.st_size -= enc->offset;
133
134         /* this will be one big string */
135         enc->private = (char *)malloc(st.st_size + 1);
136         /* setup the hash table */
137         mdvi_hash_create(&enc->nametab, ENCNAME_HASH_SIZE);
138         /* setup the encoding vector */
139         enc->vector = (char **)mdvi_malloc(256 * sizeof(char *));
140
141         /* jump to the beginning of the interesting part */
142         fseek(in, enc->offset, SEEK_SET);
143         /* and read everything */
144         if(fread(enc->private, st.st_size, 1, in) != 1) {
145                 fclose(in);
146                 mdvi_free(enc->private);
147                 enc->private = NULL;
148                 return -1;
149         }
150         /* we don't need this anymore */
151         fclose(in);
152         curr = 0;
153
154         next = name = NULL;
155         DEBUG((DBG_FMAP, "%s: reading encoding vector\n", enc->name));  
156         for(line = enc->private; *line && curr < 256; line = next) {
157                 SKIPSP(line);
158                 if(*line == ']') {
159                         line++; SKIPSP(line);
160                         if(STRNEQ(line, "def", 3))
161                                 break;
162                 }
163                 name = getword(line, " \t\n", &next);
164                 if(name == NULL)
165                         break;
166                 /* next > line */
167                 if(*name < ' ')
168                         continue;
169                 if(*name == '%') {
170                         while(*next && *next != '\n')
171                                 next++;
172                         if(*next) next++; /* skip \n */
173                         continue;
174                 }
175
176                 /* got a name */
177                 if(*next) *next++ = 0;
178                 
179                 if(*name == '/')
180                         name++;
181                 enc->vector[curr] = name;
182                 /* add it to the hash table */
183                 if(!STREQ(name, ".notdef")) {
184                         mdvi_hash_add(&enc->nametab, MDVI_KEY(name), 
185                                 Int2Ptr(curr + 1), MDVI_HASH_REPLACE);
186                 }
187                 curr++;
188         }
189         if(curr == 0) {
190                 mdvi_hash_reset(&enc->nametab, 0);
191                 mdvi_free(enc->private);
192                 mdvi_free(enc);
193                 return -1;
194         }
195         while(curr < 256)
196                 enc->vector[curr++] = NULL;
197         return 0;
198 }
199
200 static DviEncoding *find_encoding(const char *name)
201 {
202         return (DviEncoding *)(encodings.count ? 
203                 mdvi_hash_lookup(&enctable, MDVI_KEY(name)) : NULL);
204 }
205
206 static void destroy_encoding(DviEncoding *enc)
207 {
208         if(enc == default_encoding) {
209                 default_encoding = tex_text_encoding;
210                 /* now we use reference counts again */
211                 mdvi_release_encoding(enc, 1);
212         }
213         if(enc != tex_text_encoding) {
214                 mdvi_hash_reset(&enc->nametab, 0);
215                 if(enc->private) {
216                         mdvi_free(enc->private);
217                         mdvi_free(enc->vector);
218                 }
219                 if(enc->name)
220                         mdvi_free(enc->name);
221                 if(enc->filename)
222                         mdvi_free(enc->filename);
223                 mdvi_free(enc);
224         }
225 }
226
227 /* this is used for the `enctable_file' hash table */
228 static void file_hash_free(DviHashKey key, void *data)
229 {
230         mdvi_free(key);
231 }
232
233 static DviEncoding *register_encoding(const char *basefile, int replace)
234 {
235         DviEncoding *enc;
236         FILE    *in;
237         char    *filename;
238         char    *name;
239         Dstring input;
240         char    *line;
241         long    offset;
242
243         DEBUG((DBG_FMAP, "register_encoding(%s)\n", basefile));
244
245         if(encodings.count) {
246                 enc = mdvi_hash_lookup(&enctable_file, MDVI_KEY(basefile));
247                 if(enc != NULL) {
248                         DEBUG((DBG_FMAP, "%s: already there\n", basefile));
249                         return enc; /* no error */
250                 }
251         } 
252
253         /* try our own files first */
254         filename = kpse_find_file(basefile, 
255                 kpse_program_text_format, 0);
256
257         /* then try the system-wide ones */
258         if(filename == NULL)
259                 filename = kpse_find_file(basefile, 
260                         kpse_tex_ps_header_format, 0);
261         if(filename == NULL)
262                 filename = kpse_find_file(basefile,
263                         kpse_dvips_config_format, 0);
264
265         /* finally try the given name */
266         if(filename == NULL)
267                 filename = mdvi_strdup(basefile);
268
269         in = fopen(filename, "r");
270         if(in == NULL) {
271                 mdvi_free(filename);
272                 return NULL;
273         }
274         
275         /* just lookup the name of the encoding */
276         name = NULL;
277         dstring_init(&input);
278         while((line = dgets(&input, in)) != NULL) {
279                 if(STRNEQ(line, "Encoding=", 9)) {
280                         name = getword(line + 9, " \t", &line);
281                         if(*line) *line++ = 0;
282                         break;
283                 } else if(*line == '/') {
284                         char    *label = getword(line + 1, " \t", &line);
285                         if(*line) {
286                                 *line++ = 0;
287                                 SKIPSP(line);
288                                 if(*line == '[') {
289                                         *line = 0;
290                                         name = label;
291                                         break;
292                                 }
293                         }
294                 }
295         }
296         offset = ftell(in);
297         fclose(in);
298         if(name == NULL || *name == 0) {
299                 DEBUG((DBG_FMAP, 
300                         "%s: could not determine name of encoding\n",
301                         basefile));
302                 mdvi_free(filename);
303                 return NULL;
304         }
305         
306         /* check if the encoding is already there */
307         enc = find_encoding(name);
308         if(enc == tex_text_encoding) {
309                 /* A special case: if the vector we found is the static one,
310                  * allow the user to override it with an external file */
311                 listh_remove(&encodings, LIST(enc));
312                 mdvi_hash_remove(&enctable, MDVI_KEY(enc->name));
313                 if(enc == default_encoding)
314                         default_encoding = NULL;
315         } else if(enc) {
316                 /* if the encoding is being used, refuse to remove it */
317                 if(enc->links) {
318                         mdvi_free(filename);
319                         dstring_reset(&input);
320                         return NULL;
321                 }
322                 if(replace) {
323                         mdvi_hash_remove(&enctable, MDVI_KEY(name));
324                         mdvi_hash_remove(&enctable_file, MDVI_KEY(basefile));
325                         listh_remove(&encodings, LIST(enc));
326                         if(enc == default_encoding) {
327                                 default_encoding = NULL;
328                                 mdvi_release_encoding(enc, 1);
329                         }
330                         DEBUG((DBG_FMAP, "%s: overriding encoding\n", name));
331                         destroy_encoding(enc);
332                 } else {
333                         mdvi_free(filename);
334                         dstring_reset(&input);
335                         return enc; /* no error */
336                 }
337         }
338         enc = xalloc(DviEncoding);
339         enc->name = mdvi_strdup(name);
340         enc->filename = filename;
341         enc->links = 0;
342         enc->offset = offset;
343         enc->private = NULL;
344         enc->vector = NULL;
345         mdvi_hash_init(&enc->nametab);
346         dstring_reset(&input);
347         if(default_encoding == NULL)
348                 default_encoding = enc;
349         mdvi_hash_add(&enctable, MDVI_KEY(enc->name), 
350                 enc, MDVI_HASH_UNCHECKED);
351         mdvi_hash_add(&enctable_file, MDVI_KEY(mdvi_strdup(basefile)), 
352                 enc, MDVI_HASH_REPLACE);
353         listh_prepend(&encodings, LIST(enc));
354         DEBUG((DBG_FMAP, "%s: encoding `%s' registered\n",
355                 basefile, enc->name));
356         return enc;
357 }
358
359 DviEncoding *mdvi_request_encoding(const char *name)
360 {
361         DviEncoding *enc = find_encoding(name);
362
363         if(enc == NULL) {
364                 DEBUG((DBG_FMAP, "%s: encoding not found, returning default `%s'\n",
365                         name, default_encoding->name));
366                 return default_encoding;
367         }
368         /* we don't keep reference counts for this */
369         if(enc == tex_text_encoding)
370                 return enc;
371         if(!enc->private && read_encoding(enc) < 0)
372                 return NULL;
373         enc->links++;
374
375         /* if the hash table is empty, rebuild it */
376         if(enc->nametab.nkeys == 0) {
377                 int     i;
378
379                 DEBUG((DBG_FMAP, "%s: rehashing\n", enc->name));                
380                 for(i = 0; i < 256; i++) {
381                         if(enc->vector[i] == NULL)
382                                 continue;
383                         mdvi_hash_add(&enc->nametab,
384                                 MDVI_KEY(enc->vector[i]),
385                                 (DviHashKey)Int2Ptr(i),
386                                 MDVI_HASH_REPLACE);
387                 }
388         }
389         return enc;
390 }
391
392 void    mdvi_release_encoding(DviEncoding *enc, int should_free)
393 {
394         /* ignore our static encoding */
395         if(enc == tex_text_encoding)
396                 return;
397         if(!enc->links || --enc->links > 0 || !should_free)
398                 return;
399         DEBUG((DBG_FMAP, "%s: resetting encoding vector\n", enc->name));
400         mdvi_hash_reset(&enc->nametab, 1); /* we'll reuse it */
401 }
402
403 int     mdvi_encode_glyph(DviEncoding *enc, const char *name)
404 {
405         void    *data;
406         
407         data = mdvi_hash_lookup(&enc->nametab, MDVI_KEY(name));
408         if(data == NULL)
409                 return -1;
410         /* we added +1 to the hashed index just to distinguish
411          * a failed lookup from a zero index. Adjust it now. */
412         return (Ptr2Int(data) - 1);
413 }
414
415 /****************
416  * Fontmaps     *
417  ****************/
418
419 static void parse_spec(DviFontMapEnt *ent, char *spec)
420 {
421         char    *arg, *command;
422         
423         /* this is a ridiculously simple parser, and recognizes only
424          * things of the form <argument> <command>. Of these, only
425          * command=SlantFont, ExtendFont and ReEncodeFont are handled */
426         while(*spec) {
427                 arg = getword(spec, " \t", &spec);
428                 if(*spec) *spec++ = 0;
429                 command = getword(spec, " \t", &spec);
430                 if(*spec) *spec++ = 0;
431                 if(!arg || !command)    
432                         continue;
433                 if(STREQ(command, "SlantFont")) {
434                         double  x = 10000 * strtod(arg, 0);
435                 
436                         /* SFROUND evaluates arguments twice */ 
437                         ent->slant = SFROUND(x);
438                 } else if(STREQ(command, "ExtendFont")) {
439                         double  x = 10000 * strtod(arg, 0);
440                         
441                         ent->extend = SFROUND(x);
442                 } else if(STREQ(command, "ReEncodeFont")) {
443                         if(ent->encoding)
444                                 mdvi_free(ent->encoding);
445                         ent->encoding = mdvi_strdup(arg);
446                 }
447         }
448 }
449
450 #if 0
451 static void print_ent(DviFontMapEnt *ent)
452 {
453         printf("Entry for `%s':\n", ent->fontname);
454         printf("  PS name: %s\n", ent->psname ? ent->psname : "(none)");
455         printf("  Encoding: %s\n", ent->encoding ? ent->encoding : "(default)");
456         printf("  EncFile: %s\n", ent->encfile ? ent->encfile : "(none)");
457         printf("  FontFile: %s\n", ent->fontfile ? ent->fontfile : "(same)");
458         printf("  Extend: %ld\n", ent->extend);
459         printf("  Slant: %ld\n", ent->slant);
460 }
461 #endif
462
463 DviFontMapEnt   *mdvi_load_fontmap(const char *file)
464 {
465         char    *ptr;
466         FILE    *in;
467         int     lineno = 1;
468         Dstring input;
469         ListHead list;
470         DviFontMapEnt *ent;
471         DviEncoding     *last_encoding;
472         char    *last_encfile;
473
474         ptr = kpse_find_file(file, kpse_program_text_format, 0);
475         if(ptr == NULL)
476                 ptr = kpse_find_file(file, kpse_tex_ps_header_format, 0);
477         if(ptr == NULL)
478                 ptr = kpse_find_file(file, kpse_dvips_config_format, 0);
479         if(ptr == NULL)
480                 in = fopen(file, "r");                  
481         else {
482                 in = fopen(ptr, "r");
483                 mdvi_free(ptr);
484         }
485         if(in == NULL)
486                 return NULL;
487                 
488         ent = NULL;
489         listh_init(&list);
490         dstring_init(&input);
491         last_encoding = NULL;
492         last_encfile  = NULL;
493                 
494         while((ptr = dgets(&input, in)) != NULL) {
495                 char    *font_file;
496                 char    *tex_name;
497                 char    *ps_name;
498                 char    *vec_name;
499                 int     is_encoding;
500                 DviEncoding *enc;
501
502                 lineno++;
503                 SKIPSP(ptr);
504                 
505                 /* we skip what dvips does */
506                 if(*ptr <= ' ' || *ptr == '*' || *ptr == '#' || 
507                    *ptr == ';' || *ptr == '%')
508                         continue;
509                         
510                 font_file   = NULL;
511                 tex_name    = NULL;
512                 ps_name     = NULL;
513                 vec_name    = NULL;
514                 is_encoding = 0;
515
516                 if(ent == NULL) {
517                         ent = xalloc(DviFontMapEnt);
518                         ent->encoding = NULL;
519                         ent->slant = 0;
520                         ent->extend = 0;
521                 }
522                 while(*ptr) {
523                         char    *hdr_name = NULL;
524                         
525                         while(*ptr && *ptr <= ' ')
526                                 ptr++;
527                         if(*ptr == 0)
528                                 break;
529                         if(*ptr == '"') {
530                                 char    *str;
531                                 
532                                 str = getstring(ptr, " \t", &ptr);
533                                 if(*ptr) *ptr++ = 0;
534                                 parse_spec(ent, str);
535                                 continue;
536                         } else if(*ptr == '<') {
537                                 ptr++;
538                                 if(*ptr == '<')
539                                         ptr++;
540                                 else if(*ptr == '[') {
541                                         is_encoding = 1;
542                                         ptr++;
543                                 }
544                                 SKIPSP(ptr);
545                                 hdr_name = ptr;
546                         } else if(!tex_name)
547                                 tex_name = ptr;
548                         else if(!ps_name)
549                                 ps_name = ptr;
550                         else
551                                 hdr_name = ptr;
552
553                         /* get next word */
554                         getword(ptr, " \t", &ptr);
555                         if(*ptr) *ptr++ = 0;
556
557                         if(hdr_name) {
558                                 const char *ext = file_extension(hdr_name);
559                                 
560                                 if(is_encoding || (ext && STRCEQ(ext, "enc")))
561                                         vec_name = hdr_name;
562                                 else
563                                         font_file = hdr_name;
564                         }
565                 }
566
567                 if(tex_name == NULL)
568                         continue;
569                 ent->fontname = mdvi_strdup(tex_name);
570                 ent->psname   = ps_name   ? mdvi_strdup(ps_name)   : NULL;
571                 ent->fontfile = font_file ? mdvi_strdup(font_file) : NULL;
572                 ent->encfile  = vec_name  ? mdvi_strdup(vec_name)  : NULL;
573                 ent->fullfile = NULL;
574                 enc = NULL; /* we don't have this yet */
575
576                 /* if we have an encoding file, register it */
577                 if(ent->encfile) {
578                         /* register_encoding is smart enough not to load the 
579                          * same file twice */
580                         if(!last_encfile || !STREQ(last_encfile, ent->encfile)) {
581                                 last_encfile  = ent->encfile;
582                                 last_encoding = register_encoding(ent->encfile, 1);
583                         }
584                         enc = last_encoding;
585                 }
586                 if(ent->encfile && enc){                
587                         if(ent->encoding && !STREQ(ent->encoding, enc->name)) {
588                                 warning(
589         _("%s: %d: [%s] requested encoding `%s' does not match vector `%s'\n"),
590                                         file, lineno);
591                         } else if(!ent->encoding)
592                                 ent->encoding = mdvi_strdup(enc->name);
593                 }
594                 
595                 /* add it to the list */
596                 /*print_ent(ent);*/
597                 listh_append(&list, LIST(ent));
598                 ent = NULL;
599         }
600         dstring_reset(&input);  
601         fclose(in);
602         
603         return (DviFontMapEnt *)list.head;
604 }
605
606 static void free_ent(DviFontMapEnt *ent)
607 {
608         ASSERT(ent->fontname != NULL);
609         mdvi_free(ent->fontname);
610         if(ent->psname)
611                 mdvi_free(ent->psname);
612         if(ent->fontfile)
613                 mdvi_free(ent->fontfile);
614         if(ent->encoding)
615                 mdvi_free(ent->encoding);
616         if(ent->encfile)
617                 mdvi_free(ent->encfile);
618         if(ent->fullfile)
619                 mdvi_free(ent->fullfile);
620         mdvi_free(ent);
621 }
622
623 void    mdvi_install_fontmap(DviFontMapEnt *head)
624 {
625         DviFontMapEnt *ent, *next;
626
627         for(ent = head; ent; ent = next) {
628                 /* add all the entries, overriding old ones */
629                 DviFontMapEnt *old;
630                 
631                 old = (DviFontMapEnt *)
632                         mdvi_hash_remove(&maptable, MDVI_KEY(ent->fontname));
633                 if(old != NULL) {
634                         DEBUG((DBG_FMAP, "%s: overriding fontmap entry\n",
635                                 old->fontname));
636                         listh_remove(&fontmaps, LIST(old));
637                         free_ent(old);
638                 }
639                 next = ent->next;
640                 mdvi_hash_add(&maptable, MDVI_KEY(ent->fontname), 
641                         ent, MDVI_HASH_UNCHECKED);
642                 listh_append(&fontmaps, LIST(ent));
643         }
644 }
645
646 static void init_static_encoding()
647 {
648         DviEncoding     *encoding;
649         int     i;
650
651         DEBUG((DBG_FMAP, "installing static TeX text encoding\n"));     
652         encoding = xalloc(DviEncoding);
653         encoding->private  = "";
654         encoding->filename = "";
655         encoding->name     = "TeXTextEncoding";
656         encoding->vector   = tex_text_vector;
657         encoding->links    = 1;
658         encoding->offset   = 0;
659         mdvi_hash_create(&encoding->nametab, ENCNAME_HASH_SIZE);
660         for(i = 0; i < 256; i++) {
661                 if(encoding->vector[i]) {
662                         mdvi_hash_add(&encoding->nametab,
663                                 MDVI_KEY(encoding->vector[i]),
664                                 (DviHashKey)Int2Ptr(i),
665                                 MDVI_HASH_UNCHECKED);
666                 }
667         }
668         ASSERT_VALUE(encodings.count, 0);
669         mdvi_hash_create(&enctable, ENC_HASH_SIZE);
670         mdvi_hash_create(&enctable_file, ENC_HASH_SIZE);
671         enctable_file.hash_free = file_hash_free;
672         mdvi_hash_add(&enctable, MDVI_KEY(encoding->name), 
673                 encoding, MDVI_HASH_UNCHECKED);
674         listh_prepend(&encodings, LIST(encoding));
675         tex_text_encoding = encoding;
676         default_encoding = tex_text_encoding;
677 }
678
679 static int      mdvi_set_default_encoding(const char *name)
680 {
681         DviEncoding *enc, *old;
682
683         enc = find_encoding(name);
684         if(enc == NULL)
685                 return -1;
686         if(enc == default_encoding)
687                 return 0;
688         /* this will read it from file if necessary,
689          * but it can fail if the file is corrupted */
690         enc = mdvi_request_encoding(name);
691         if(enc == NULL)
692                 return -1;
693         old = default_encoding;
694         default_encoding = enc;
695         if(old != tex_text_encoding)
696                 mdvi_release_encoding(old, 1);
697         return 0;
698 }
699
700 static int      mdvi_init_fontmaps(void)
701 {
702         char    *file;
703         char    *line;
704         FILE    *in;
705         Dstring input;
706         int     count = 0;
707         char    *config;
708
709         if(fontmaps_loaded)
710                 return 0;
711         /* we will only try this once */
712         fontmaps_loaded = 1;
713
714         DEBUG((DBG_FMAP, "reading fontmaps\n"));
715
716         /* make sure the static encoding is there */
717         init_static_encoding();
718
719         /* create the fontmap hash table */
720         mdvi_hash_create(&maptable, MAP_HASH_SIZE);
721                         
722         /* get the name of our configuration file */
723         config = kpse_cnf_get("mdvi-config");
724         if(config == NULL)
725                 config = MDVI_DEFAULT_CONFIG;
726         /* let's ask kpathsea for the file first */
727         file = kpse_find_file(config, kpse_program_text_format, 0);
728         if(file == NULL)
729                 in = fopen(config, "r");
730         else {
731                 in = fopen(file, "r");
732                 mdvi_free(file);
733         }
734         if(in == NULL)
735                 return -1;
736         dstring_init(&input);
737         while((line = dgets(&input, in)) != NULL) {
738                 char    *arg;
739                 
740                 SKIPSP(line);
741                 if(*line < ' ' || *line == '#' || *line == '%')
742                         continue;
743                 if(STRNEQ(line, "fontmap", 7)) {
744                         DviFontMapEnt *ent;
745                         
746                         arg = getstring(line + 7, " \t", &line); *line = 0;
747                         DEBUG((DBG_FMAP, "%s: loading fontmap\n", arg));
748                         ent = mdvi_load_fontmap(arg);
749                         if(ent == NULL)
750                                 warning(_("%s: could not load fontmap\n"), arg);
751                         else {
752                                 DEBUG((DBG_FMAP, 
753                                         "%s: installing fontmap\n", arg));
754                                 mdvi_install_fontmap(ent);
755                                 count++;
756                         }
757                 } else if(STRNEQ(line, "encoding", 8)) {
758                         arg = getstring(line + 8, " \t", &line); *line = 0;
759                         if(arg && *arg)
760                                 register_encoding(arg, 1);
761                 } else if(STRNEQ(line, "default-encoding", 16)) {
762                         arg = getstring(line + 16, " \t", &line); *line = 0;
763                         if(mdvi_set_default_encoding(arg) < 0)
764                                 warning(_("%s: could not set as default encoding\n"),
765                                         arg);
766                 } else if(STRNEQ(line, "psfontpath", 10)) {
767                         arg = getstring(line + 11, " \t", &line); *line = 0;
768                         if(!psinitialized)
769                                 ps_init_default_paths();
770                         if(psfontdir)
771                                 mdvi_free(psfontdir);
772                         psfontdir = kpse_path_expand(arg);
773                 } else if(STRNEQ(line, "pslibpath", 9)) {
774                         arg = getstring(line + 10, " \t", &line); *line = 0;
775                         if(!psinitialized)
776                                 ps_init_default_paths();
777                         if(pslibdir)
778                                 mdvi_free(pslibdir);
779                         pslibdir = kpse_path_expand(arg);
780                 } else if(STRNEQ(line, "psfontmap", 9)) {
781                         arg = getstring(line + 9, " \t", &line); *line = 0;
782                         if(mdvi_ps_read_fontmap(arg) < 0)
783                                 warning("%s: %s: could not read PS fontmap\n",
784                                         config, arg);
785                 }
786         }
787         fclose(in);
788         dstring_reset(&input);
789         fontmaps_loaded = 1;
790         DEBUG((DBG_FMAP, "%d files installed, %d fontmaps\n",
791                 count, fontmaps.count));
792         return count;
793 }
794
795 int     mdvi_query_fontmap(DviFontMapInfo *info, const char *fontname)
796 {
797         DviFontMapEnt *ent;
798
799         if(!fontmaps_loaded && mdvi_init_fontmaps() < 0)
800                 return -1;              
801         ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(fontname));
802
803         if(ent == NULL)
804                 return -1;
805         info->psname   = ent->psname;
806         info->encoding = ent->encoding;
807         info->fontfile = ent->fontfile;
808         info->extend   = ent->extend;
809         info->slant    = ent->slant;
810         info->fullfile = ent->fullfile;
811         
812         return 0;       
813 }
814
815 int     mdvi_add_fontmap_file(const char *name, const char *fullpath)
816 {
817         DviFontMapEnt *ent;
818         
819         if(!fontmaps_loaded && mdvi_init_fontmaps() < 0)
820                 return -1;
821         ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(name));
822         if(ent == NULL)
823                 return -1;
824         if(ent->fullfile)
825                 mdvi_free(ent->fullfile);
826         ent->fullfile = mdvi_strdup(fullpath);
827         return 0;
828 }
829
830
831 void    mdvi_flush_encodings(void)
832 {
833         DviEncoding *enc;
834
835         if(enctable.nbucks == 0)
836                 return;
837
838         DEBUG((DBG_FMAP, "flushing %d encodings\n", encodings.count));
839         /* asked to remove all encodings */
840         for(; (enc = (DviEncoding *)encodings.head); ) {
841                 encodings.head = LIST(enc->next);
842                 if((enc != tex_text_encoding && enc->links) || enc->links > 1) {
843                         warning(_("encoding vector `%s' is in use\n"),
844                                 enc->name);
845                 }
846                 destroy_encoding(enc);
847         }
848         /* destroy the static encoding */
849         if(tex_text_encoding->nametab.buckets)
850                 mdvi_hash_reset(&tex_text_encoding->nametab, 0);
851         mdvi_hash_reset(&enctable, 0);
852         mdvi_hash_reset(&enctable_file, 0);     
853 }
854
855 void    mdvi_flush_fontmaps(void)
856 {
857         DviFontMapEnt *ent;
858         
859         if(!fontmaps_loaded)
860                 return;
861
862         DEBUG((DBG_FMAP, "flushing %d fontmaps\n", fontmaps.count));
863         for(; (ent = (DviFontMapEnt *)fontmaps.head); ) {
864                 fontmaps.head = LIST(ent->next);
865                 free_ent(ent);
866         }
867         mdvi_hash_reset(&maptable, 0);
868         fontmaps_loaded = 0;
869 }
870
871 /* reading of PS fontmaps */
872
873 void    ps_init_default_paths(void)
874 {
875         char    *kppath;
876         char    *kfpath;
877
878         ASSERT(psinitialized == 0);
879
880         kppath = getenv("GS_LIB");
881         kfpath = getenv("GS_FONTPATH"); 
882
883         if(kppath != NULL)
884                 pslibdir = kpse_path_expand(kppath);
885         if(kfpath != NULL)
886                 psfontdir = kpse_path_expand(kfpath);
887
888         listh_init(&psfonts);
889         mdvi_hash_create(&pstable, PSMAP_HASH_SIZE);
890         psinitialized = 1;
891 }
892
893 int     mdvi_ps_read_fontmap(const char *name)
894 {
895         char    *fullname;
896         FILE    *in;
897         Dstring dstr;
898         char    *line;
899         int     count = 0;
900         
901         if(!psinitialized)
902                 ps_init_default_paths();
903         if(pslibdir)
904                 fullname = kpse_path_search(pslibdir, name, 1);
905         else
906                 fullname = (char *)name;
907         in = fopen(fullname, "r");
908         if(in == NULL) {
909                 if(fullname != name)
910                         mdvi_free(fullname);
911                 return -1;
912         }
913         dstring_init(&dstr);
914         
915         while((line = dgets(&dstr, in)) != NULL) {
916                 char    *name;
917                 char    *mapname;
918                 const char *ext;
919                 PSFontMap *ps;
920                 
921                 SKIPSP(line);
922                 /* we're looking for lines of the form
923                  *  /FONT-NAME    (fontfile)
924                  *  /FONT-NAME    /FONT-ALIAS 
925                  */
926                 if(*line != '/')
927                         continue;
928                 name = getword(line + 1, " \t", &line);
929                 if(*line) *line++ = 0;
930                 mapname = getword(line, " \t", &line);
931                 if(*line) *line++ = 0;
932                 
933                 if(!name || !mapname || !*name)
934                         continue;
935                 if(*mapname == '(') {
936                         char    *end;
937                         
938                         mapname++;
939                         for(end = mapname; *end && *end != ')'; end++);
940                         *end = 0;
941                 }
942                 if(!*mapname)
943                         continue;
944                 /* dont add `.gsf' fonts, which require a full blown
945                  * PostScript interpreter */
946                 ext = file_extension(mapname);
947                 if(ext && STREQ(ext, "gsf")) {
948                         DEBUG((DBG_FMAP, "(ps) %s: font `%s' ignored\n",
949                                 name, mapname));
950                         continue;
951                 }
952                 ps = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(name));
953                 if(ps != NULL) {
954                         if(STREQ(ps->mapname, mapname))
955                                 continue;
956                         DEBUG((DBG_FMAP, 
957                                 "(ps) replacing font `%s' (%s) by `%s'\n",
958                                 name, ps->mapname, mapname));
959                         mdvi_free(ps->mapname);
960                         ps->mapname = mdvi_strdup(mapname);
961                         if(ps->fullname) {
962                                 mdvi_free(ps->fullname);
963                                 ps->fullname = NULL;
964                         }
965                 } else {
966                         DEBUG((DBG_FMAP, "(ps) adding font `%s' as `%s'\n",
967                                 name, mapname));
968                         ps = xalloc(PSFontMap);
969                         ps->psname   = mdvi_strdup(name);
970                         ps->mapname  = mdvi_strdup(mapname);
971                         ps->fullname = NULL;
972                         listh_append(&psfonts, LIST(ps));
973                         mdvi_hash_add(&pstable, MDVI_KEY(ps->psname),
974                                 ps, MDVI_HASH_UNCHECKED);
975                         count++;
976                 }
977         }
978         fclose(in);
979         dstring_reset(&dstr);
980         
981         DEBUG((DBG_FMAP, "(ps) %s: %d PostScript fonts registered\n",
982                 fullname, count));
983         return 0;
984 }
985
986 void    mdvi_ps_flush_fonts(void)
987 {
988         PSFontMap *map;
989         
990         if(!psinitialized)
991                 return;
992         DEBUG((DBG_FMAP, "(ps) flushing PS font map (%d) entries\n",
993                 psfonts.count));
994         mdvi_hash_reset(&pstable, 0);
995         for(; (map = (PSFontMap *)psfonts.head); ) {
996                 psfonts.head = LIST(map->next);
997                 mdvi_free(map->psname);
998                 mdvi_free(map->mapname);
999                 if(map->fullname)
1000                         mdvi_free(map->fullname);
1001                 mdvi_free(map);
1002         }
1003         listh_init(&psfonts);
1004         if(pslibdir) {
1005                 mdvi_free(pslibdir);
1006                 pslibdir = NULL;
1007         }
1008         if(psfontdir) {
1009                 mdvi_free(psfontdir);
1010                 psfontdir = NULL;
1011         }
1012         psinitialized = 0;
1013 }
1014
1015 char    *mdvi_ps_find_font(const char *psname)
1016 {
1017         PSFontMap *map, *smap;
1018         char    *filename;
1019         int     recursion_limit = 32;
1020
1021         DEBUG((DBG_FMAP, "(ps) resolving PS font `%s'\n", psname));
1022         if(!psinitialized)
1023                 return NULL;
1024         map = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(psname));
1025         if(map == NULL)
1026                 return NULL;
1027         if(map->fullname)
1028                 return mdvi_strdup(map->fullname);
1029
1030         /* is it an alias? */
1031         smap = map;
1032         while(recursion_limit-- > 0 && smap && *smap->mapname == '/')
1033                 smap = (PSFontMap *)mdvi_hash_lookup(&pstable, 
1034                         MDVI_KEY(smap->mapname + 1));
1035         if(smap == NULL) {
1036                 if(recursion_limit == 0)
1037                         DEBUG((DBG_FMAP, 
1038                                 "(ps) %s: possible loop in PS font map\n",
1039                                 psname));
1040                 return NULL;
1041         }
1042
1043         if(psfontdir)
1044                 filename = kpse_path_search(psfontdir, smap->mapname, 1);
1045         else if(file_exists(map->mapname))
1046                 filename = mdvi_strdup(map->mapname);
1047         else
1048                 filename = NULL;
1049         if(filename)
1050                 map->fullname = mdvi_strdup(filename);
1051                 
1052         return filename;
1053 }
1054
1055 /*
1056  * To get metric info for a font, we proceed as follows:
1057  *  - We try to find NAME.<tfm,ofm,afm>.
1058  *  - We query the fontmap for NAME.
1059  *  - We get back a PSNAME, and use to find the file in the PS font map.
1060  *  - We get the PSFONT file name, replace its extension by "afm" and
1061  *  lookup the file in GS's font search path.
1062  *  - We finally read the data, transform it as specified in our font map,
1063  *  and return it to the caller. The new data is left in the font metrics
1064  *  cache, so the next time it will be found at the first step (when we look
1065  *  up NAME.afm).
1066  *
1067  * The name `_ps_' in this function is not meant to imply that it can be 
1068  * used for Type1 fonts only. It should be usable for TrueType fonts as well.
1069  *
1070  * The returned metric info is subjected to the same caching mechanism as
1071  * all the other metric data, as returned by get_font_metrics(). One should
1072  * not modify the returned data at all, and it should be disposed with
1073  * free_font_metrics().
1074  */
1075 TFMInfo *mdvi_ps_get_metrics(const char *fontname)
1076 {
1077         TFMInfo *info;
1078         DviFontMapInfo map;
1079         char    buffer[64]; /* to avoid mallocs */
1080         char    *psfont;
1081         char    *basefile;
1082         char    *afmfile;
1083         char    *ext;
1084         int     baselen;
1085         int     nc;
1086         TFMChar *ch;
1087         double  efactor;
1088         double  sfactor;
1089
1090         DEBUG((DBG_FMAP, "(ps) %s: looking for metric data\n", fontname));      
1091         info = get_font_metrics(fontname, DviFontAny, NULL);
1092         if(info != NULL)
1093                 return info;
1094
1095         /* query the fontmap */
1096         if(mdvi_query_fontmap(&map, fontname) < 0 || !map.psname)
1097                 return NULL;
1098         
1099         /* get the PS font */
1100         psfont = mdvi_ps_find_font(map.psname);
1101         if(psfont == NULL)
1102                 return NULL;
1103         DEBUG((DBG_FMAP, "(ps) %s: found as PS font `%s'\n",
1104                 fontname, psfont));
1105         /* replace its extension */
1106         basefile = strrchr(psfont, '/');
1107         if(basefile == NULL)
1108                 basefile = psfont;
1109         baselen = strlen(basefile);
1110         ext = strrchr(basefile, '.');
1111         if(ext != NULL)
1112                 *ext = 0;
1113         if(baselen + 4 < 64)
1114                 afmfile = &buffer[0];
1115         else
1116                 afmfile = mdvi_malloc(baselen + 5);
1117         strcpy(afmfile, basefile);
1118         strcpy(afmfile + baselen, ".afm");
1119         /* we don't need this anymore */
1120         mdvi_free(psfont);
1121         DEBUG((DBG_FMAP, "(ps) %s: looking for `%s'\n",
1122                 fontname, afmfile));
1123         /* lookup the file */
1124         psfont = kpse_path_search(psfontdir, afmfile, 1);
1125         /* don't need this anymore */
1126         if(afmfile != &buffer[0])
1127                 mdvi_free(afmfile);
1128         if(psfont != NULL) {
1129                 info = get_font_metrics(fontname, DviFontAFM, psfont);
1130                 mdvi_free(psfont);
1131         } else
1132                 info = NULL;
1133         if(info == NULL || (!map.extend && !map.slant))
1134                 return info;
1135
1136         /* 
1137          * transform the data as prescribed -- keep in mind that `info' 
1138          * points to CACHED data, so we're modifying the metric cache 
1139          * in place.
1140          */
1141
1142 #define DROUND(x)       ((x) >= 0 ? floor((x) + 0.5) : ceil((x) - 0.5))
1143 #define TRANSFORM(x,y)  DROUND(efactor * (x) + sfactor * (y))
1144
1145         efactor = (double)map.extend / 10000.0;
1146         sfactor = (double)map.slant / 10000.0;
1147         DEBUG((DBG_FMAP, "(ps) %s: applying extend=%f, slant=%f\n",
1148                 efactor, sfactor));
1149
1150         nc = info->hic - info->loc + 1;
1151         for(ch = info->chars; ch < info->chars + nc; ch++) {
1152                 /* the AFM bounding box is:
1153                  *    wx = ch->advance
1154                  *   llx = ch->left
1155                  *   lly = -ch->depth
1156                  *   urx = ch->right
1157                  *   ury = ch->height
1158                  * what we do here is transform wx, llx, and urx by
1159                  *         newX = efactor * oldX + sfactor * oldY
1160                  * where for `wx' oldY = 0. Also, these numbers are all in
1161                  * TFM units (i.e. TFM's fix-words, which is just the actual
1162                  * number times 2^20, no need to do anything to it).
1163                  */
1164                 if(ch->present) {
1165                         ch->advance = TRANSFORM(ch->advance, 0);
1166                         ch->left    = TRANSFORM(ch->left, -ch->depth);
1167                         ch->right   = TRANSFORM(ch->right, ch->height);
1168                 }
1169         }
1170         
1171         return info;
1172 }