]> www.fi.muni.cz Git - evince.git/blob - dvi/mdvi-lib/fontmap.c
Recent files support.
[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 **)xmalloc(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                 xfree(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                 xfree(enc->private);
192                 xfree(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                         xfree(enc->private);
217                         xfree(enc->vector);
218                 }
219                 if(enc->name)
220                         xfree(enc->name);
221                 if(enc->filename)
222                         xfree(enc->filename);
223                 xfree(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         xfree(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 = xstrdup(basefile);
268
269         in = fopen(filename, "r");
270         if(in == NULL) {
271                 xfree(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                 xfree(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                         xfree(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                         xfree(filename);
334                         dstring_reset(&input);
335                         return enc; /* no error */
336                 }
337         }
338         enc = xalloc(DviEncoding);
339         enc->name = xstrdup(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(xstrdup(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                                 xfree(ent->encoding);
445                         ent->encoding = xstrdup(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                 xfree(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 = xstrdup(tex_name);
570                 ent->psname   = ps_name   ? xstrdup(ps_name)   : NULL;
571                 ent->fontfile = font_file ? xstrdup(font_file) : NULL;
572                 ent->encfile  = vec_name  ? xstrdup(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 = xstrdup(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         xfree(ent->fontname);
610         if(ent->psname)
611                 xfree(ent->psname);
612         if(ent->fontfile)
613                 xfree(ent->fontfile);
614         if(ent->encoding)
615                 xfree(ent->encoding);
616         if(ent->encfile)
617                 xfree(ent->encfile);
618         if(ent->fullfile)
619                 xfree(ent->fullfile);
620         xfree(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                 xfree(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                                 xfree(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                                 xfree(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                 xfree(ent->fullfile);
826         ent->fullfile = xstrdup(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 = mdvi_getenv("MDVI_PS_LIBPATH");
876         char    *kfpath = mdvi_getenv("MDVI_PS_FONTPATH");
877
878         ASSERT(psinitialized == 0);
879         if(kppath == NULL)
880                 kppath = getenv("GS_LIB");
881         if(kfpath == NULL)
882                 kfpath = getenv("GS_FONTPATH"); 
883         if(kppath != NULL)
884                 pslibdir = kpse_path_expand(kppath);
885         if(kfpath != NULL)
886                 psfontdir = kpse_path_expand(kfpath);
887         listh_init(&psfonts);
888         mdvi_hash_create(&pstable, PSMAP_HASH_SIZE);
889         psinitialized = 1;
890 }
891
892 int     mdvi_ps_read_fontmap(const char *name)
893 {
894         char    *fullname;
895         FILE    *in;
896         Dstring dstr;
897         char    *line;
898         int     count = 0;
899         
900         if(!psinitialized)
901                 ps_init_default_paths();
902         if(pslibdir)
903                 fullname = kpse_path_search(pslibdir, name, 1);
904         else
905                 fullname = (char *)name;
906         in = fopen(fullname, "r");
907         if(in == NULL) {
908                 if(fullname != name)
909                         xfree(fullname);
910                 return -1;
911         }
912         dstring_init(&dstr);
913         
914         while((line = dgets(&dstr, in)) != NULL) {
915                 char    *name;
916                 char    *mapname;
917                 const char *ext;
918                 PSFontMap *ps;
919                 
920                 SKIPSP(line);
921                 /* we're looking for lines of the form
922                  *  /FONT-NAME    (fontfile)
923                  *  /FONT-NAME    /FONT-ALIAS 
924                  */
925                 if(*line != '/')
926                         continue;
927                 name = getword(line + 1, " \t", &line);
928                 if(*line) *line++ = 0;
929                 mapname = getword(line, " \t", &line);
930                 if(*line) *line++ = 0;
931                 
932                 if(!name || !mapname || !*name)
933                         continue;
934                 if(*mapname == '(') {
935                         char    *end;
936                         
937                         mapname++;
938                         for(end = mapname; *end && *end != ')'; end++);
939                         *end = 0;
940                 }
941                 if(!*mapname)
942                         continue;
943                 /* dont add `.gsf' fonts, which require a full blown
944                  * PostScript interpreter */
945                 ext = file_extension(mapname);
946                 if(ext && STREQ(ext, "gsf")) {
947                         DEBUG((DBG_FMAP, "(ps) %s: font `%s' ignored\n",
948                                 name, mapname));
949                         continue;
950                 }
951                 ps = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(name));
952                 if(ps != NULL) {
953                         if(STREQ(ps->mapname, mapname))
954                                 continue;
955                         DEBUG((DBG_FMAP, 
956                                 "(ps) replacing font `%s' (%s) by `%s'\n",
957                                 name, ps->mapname, mapname));
958                         xfree(ps->mapname);
959                         ps->mapname = xstrdup(mapname);
960                         if(ps->fullname) {
961                                 xfree(ps->fullname);
962                                 ps->fullname = NULL;
963                         }
964                 } else {
965                         DEBUG((DBG_FMAP, "(ps) adding font `%s' as `%s'\n",
966                                 name, mapname));
967                         ps = xalloc(PSFontMap);
968                         ps->psname   = xstrdup(name);
969                         ps->mapname  = xstrdup(mapname);
970                         ps->fullname = NULL;
971                         listh_append(&psfonts, LIST(ps));
972                         mdvi_hash_add(&pstable, MDVI_KEY(ps->psname),
973                                 ps, MDVI_HASH_UNCHECKED);
974                         count++;
975                 }
976         }
977         fclose(in);
978         dstring_reset(&dstr);
979         
980         DEBUG((DBG_FMAP, "(ps) %s: %d PostScript fonts registered\n",
981                 fullname, count));
982         return 0;
983 }
984
985 void    mdvi_ps_flush_fonts(void)
986 {
987         PSFontMap *map;
988         
989         if(!psinitialized)
990                 return;
991         DEBUG((DBG_FMAP, "(ps) flushing PS font map (%d) entries\n",
992                 psfonts.count));
993         mdvi_hash_reset(&pstable, 0);
994         for(; (map = (PSFontMap *)psfonts.head); ) {
995                 psfonts.head = LIST(map->next);
996                 xfree(map->psname);
997                 xfree(map->mapname);
998                 if(map->fullname)
999                         xfree(map->fullname);
1000                 xfree(map);
1001         }
1002         listh_init(&psfonts);
1003         if(pslibdir) {
1004                 xfree(pslibdir);
1005                 pslibdir = NULL;
1006         }
1007         if(psfontdir) {
1008                 xfree(psfontdir);
1009                 psfontdir = NULL;
1010         }
1011         psinitialized = 0;
1012 }
1013
1014 char    *mdvi_ps_find_font(const char *psname)
1015 {
1016         PSFontMap *map, *smap;
1017         char    *filename;
1018         int     recursion_limit = 32;
1019
1020         DEBUG((DBG_FMAP, "(ps) resolving PS font `%s'\n", psname));
1021         if(!psinitialized)
1022                 return NULL;
1023         map = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(psname));
1024         if(map == NULL)
1025                 return NULL;
1026         if(map->fullname)
1027                 return xstrdup(map->fullname);
1028
1029         /* is it an alias? */
1030         smap = map;
1031         while(recursion_limit-- > 0 && smap && *smap->mapname == '/')
1032                 smap = (PSFontMap *)mdvi_hash_lookup(&pstable, 
1033                         MDVI_KEY(smap->mapname + 1));
1034         if(smap == NULL) {
1035                 if(recursion_limit == 0)
1036                         DEBUG((DBG_FMAP, 
1037                                 "(ps) %s: possible loop in PS font map\n",
1038                                 psname));
1039                 return NULL;
1040         }
1041
1042         if(psfontdir)
1043                 filename = kpse_path_search(psfontdir, smap->mapname, 1);
1044         else if(file_exists(map->mapname))
1045                 filename = xstrdup(map->mapname);
1046         else
1047                 filename = NULL;
1048         if(filename)
1049                 map->fullname = xstrdup(filename);
1050                 
1051         return filename;
1052 }
1053
1054 /*
1055  * To get metric info for a font, we proceed as follows:
1056  *  - We try to find NAME.<tfm,ofm,afm>.
1057  *  - We query the fontmap for NAME.
1058  *  - We get back a PSNAME, and use to find the file in the PS font map.
1059  *  - We get the PSFONT file name, replace its extension by "afm" and
1060  *  lookup the file in GS's font search path.
1061  *  - We finally read the data, transform it as specified in our font map,
1062  *  and return it to the caller. The new data is left in the font metrics
1063  *  cache, so the next time it will be found at the first step (when we look
1064  *  up NAME.afm).
1065  *
1066  * The name `_ps_' in this function is not meant to imply that it can be 
1067  * used for Type1 fonts only. It should be usable for TrueType fonts as well.
1068  *
1069  * The returned metric info is subjected to the same caching mechanism as
1070  * all the other metric data, as returned by get_font_metrics(). One should
1071  * not modify the returned data at all, and it should be disposed with
1072  * free_font_metrics().
1073  */
1074 TFMInfo *mdvi_ps_get_metrics(const char *fontname)
1075 {
1076         TFMInfo *info;
1077         DviFontMapInfo map;
1078         char    buffer[64]; /* to avoid mallocs */
1079         char    *psfont;
1080         char    *basefile;
1081         char    *afmfile;
1082         char    *ext;
1083         int     baselen;
1084         int     nc;
1085         TFMChar *ch;
1086         double  efactor;
1087         double  sfactor;
1088
1089         DEBUG((DBG_FMAP, "(ps) %s: looking for metric data\n", fontname));      
1090         info = get_font_metrics(fontname, DviFontAny, NULL);
1091         if(info != NULL)
1092                 return info;
1093
1094         /* query the fontmap */
1095         if(mdvi_query_fontmap(&map, fontname) < 0 || !map.psname)
1096                 return NULL;
1097         
1098         /* get the PS font */
1099         psfont = mdvi_ps_find_font(map.psname);
1100         if(psfont == NULL)
1101                 return NULL;
1102         DEBUG((DBG_FMAP, "(ps) %s: found as PS font `%s'\n",
1103                 fontname, psfont));
1104         /* replace its extension */
1105         basefile = strrchr(psfont, '/');
1106         if(basefile == NULL)
1107                 basefile = psfont;
1108         baselen = strlen(basefile);
1109         ext = strrchr(basefile, '.');
1110         if(ext != NULL)
1111                 *ext = 0;
1112         if(baselen + 4 < 64)
1113                 afmfile = &buffer[0];
1114         else
1115                 afmfile = xmalloc(baselen + 5);
1116         strcpy(afmfile, basefile);
1117         strcpy(afmfile + baselen, ".afm");
1118         /* we don't need this anymore */
1119         xfree(psfont);
1120         DEBUG((DBG_FMAP, "(ps) %s: looking for `%s'\n",
1121                 fontname, afmfile));
1122         /* lookup the file */
1123         psfont = kpse_path_search(psfontdir, afmfile, 1);
1124         /* don't need this anymore */
1125         if(afmfile != &buffer[0])
1126                 xfree(afmfile);
1127         if(psfont != NULL) {
1128                 info = get_font_metrics(fontname, DviFontAFM, psfont);
1129                 xfree(psfont);
1130         } else
1131                 info = NULL;
1132         if(info == NULL || (!map.extend && !map.slant))
1133                 return info;
1134
1135         /* 
1136          * transform the data as prescribed -- keep in mind that `info' 
1137          * points to CACHED data, so we're modifying the metric cache 
1138          * in place.
1139          */
1140
1141 #define DROUND(x)       ((x) >= 0 ? floor((x) + 0.5) : ceil((x) - 0.5))
1142 #define TRANSFORM(x,y)  DROUND(efactor * (x) + sfactor * (y))
1143
1144         efactor = (double)map.extend / 10000.0;
1145         sfactor = (double)map.slant / 10000.0;
1146         DEBUG((DBG_FMAP, "(ps) %s: applying extend=%f, slant=%f\n",
1147                 efactor, sfactor));
1148
1149         nc = info->hic - info->loc + 1;
1150         for(ch = info->chars; ch < info->chars + nc; ch++) {
1151                 /* the AFM bounding box is:
1152                  *    wx = ch->advance
1153                  *   llx = ch->left
1154                  *   lly = -ch->depth
1155                  *   urx = ch->right
1156                  *   ury = ch->height
1157                  * what we do here is transform wx, llx, and urx by
1158                  *         newX = efactor * oldX + sfactor * oldY
1159                  * where for `wx' oldY = 0. Also, these numbers are all in
1160                  * TFM units (i.e. TFM's fix-words, which is just the actual
1161                  * number times 2^20, no need to do anything to it).
1162                  */
1163                 if(ch->present) {
1164                         ch->advance = TRANSFORM(ch->advance, 0);
1165                         ch->left    = TRANSFORM(ch->left, -ch->depth);
1166                         ch->right   = TRANSFORM(ch->right, ch->height);
1167                 }
1168         }
1169         
1170         return info;
1171 }