]> www.fi.muni.cz Git - evince.git/blob - backend/dvi/mdvi-lib/fontmap.c
6aa87e038fa1654795abe84a62886038e76735cb
[evince.git] / backend / 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, ent->encfile,
591                                         ent->encoding, enc->name);
592                         } else if(!ent->encoding)
593                                 ent->encoding = mdvi_strdup(enc->name);
594                 }
595                 
596                 /* add it to the list */
597                 /*print_ent(ent);*/
598                 listh_append(&list, LIST(ent));
599                 ent = NULL;
600         }
601         dstring_reset(&input);  
602         fclose(in);
603         
604         return (DviFontMapEnt *)list.head;
605 }
606
607 static void free_ent(DviFontMapEnt *ent)
608 {
609         ASSERT(ent->fontname != NULL);
610         mdvi_free(ent->fontname);
611         if(ent->psname)
612                 mdvi_free(ent->psname);
613         if(ent->fontfile)
614                 mdvi_free(ent->fontfile);
615         if(ent->encoding)
616                 mdvi_free(ent->encoding);
617         if(ent->encfile)
618                 mdvi_free(ent->encfile);
619         if(ent->fullfile)
620                 mdvi_free(ent->fullfile);
621         mdvi_free(ent);
622 }
623
624 void    mdvi_install_fontmap(DviFontMapEnt *head)
625 {
626         DviFontMapEnt *ent, *next;
627
628         for(ent = head; ent; ent = next) {
629                 /* add all the entries, overriding old ones */
630                 DviFontMapEnt *old;
631                 
632                 old = (DviFontMapEnt *)
633                         mdvi_hash_remove(&maptable, MDVI_KEY(ent->fontname));
634                 if(old != NULL) {
635                         DEBUG((DBG_FMAP, "%s: overriding fontmap entry\n",
636                                 old->fontname));
637                         listh_remove(&fontmaps, LIST(old));
638                         free_ent(old);
639                 }
640                 next = ent->next;
641                 mdvi_hash_add(&maptable, MDVI_KEY(ent->fontname), 
642                         ent, MDVI_HASH_UNCHECKED);
643                 listh_append(&fontmaps, LIST(ent));
644         }
645 }
646
647 static void init_static_encoding()
648 {
649         DviEncoding     *encoding;
650         int     i;
651
652         DEBUG((DBG_FMAP, "installing static TeX text encoding\n"));     
653         encoding = xalloc(DviEncoding);
654         encoding->private  = "";
655         encoding->filename = "";
656         encoding->name     = "TeXTextEncoding";
657         encoding->vector   = tex_text_vector;
658         encoding->links    = 1;
659         encoding->offset   = 0;
660         mdvi_hash_create(&encoding->nametab, ENCNAME_HASH_SIZE);
661         for(i = 0; i < 256; i++) {
662                 if(encoding->vector[i]) {
663                         mdvi_hash_add(&encoding->nametab,
664                                 MDVI_KEY(encoding->vector[i]),
665                                 (DviHashKey)Int2Ptr(i),
666                                 MDVI_HASH_UNCHECKED);
667                 }
668         }
669         ASSERT_VALUE(encodings.count, 0);
670         mdvi_hash_create(&enctable, ENC_HASH_SIZE);
671         mdvi_hash_create(&enctable_file, ENC_HASH_SIZE);
672         enctable_file.hash_free = file_hash_free;
673         mdvi_hash_add(&enctable, MDVI_KEY(encoding->name), 
674                 encoding, MDVI_HASH_UNCHECKED);
675         listh_prepend(&encodings, LIST(encoding));
676         tex_text_encoding = encoding;
677         default_encoding = tex_text_encoding;
678 }
679
680 static int      mdvi_set_default_encoding(const char *name)
681 {
682         DviEncoding *enc, *old;
683
684         enc = find_encoding(name);
685         if(enc == NULL)
686                 return -1;
687         if(enc == default_encoding)
688                 return 0;
689         /* this will read it from file if necessary,
690          * but it can fail if the file is corrupted */
691         enc = mdvi_request_encoding(name);
692         if(enc == NULL)
693                 return -1;
694         old = default_encoding;
695         default_encoding = enc;
696         if(old != tex_text_encoding)
697                 mdvi_release_encoding(old, 1);
698         return 0;
699 }
700
701 static int      mdvi_init_fontmaps(void)
702 {
703         char    *file;
704         char    *line;
705         FILE    *in;
706         Dstring input;
707         int     count = 0;
708         char    *config;
709
710         if(fontmaps_loaded)
711                 return 0;
712         /* we will only try this once */
713         fontmaps_loaded = 1;
714
715         DEBUG((DBG_FMAP, "reading fontmaps\n"));
716
717         /* make sure the static encoding is there */
718         init_static_encoding();
719
720         /* create the fontmap hash table */
721         mdvi_hash_create(&maptable, MAP_HASH_SIZE);
722                         
723         /* get the name of our configuration file */
724         config = kpse_cnf_get("mdvi-config");
725         if(config == NULL)
726                 config = MDVI_DEFAULT_CONFIG;
727         /* let's ask kpathsea for the file first */
728         file = kpse_find_file(config, kpse_program_text_format, 0);
729         if(file == NULL)
730                 in = fopen(config, "r");
731         else {
732                 in = fopen(file, "r");
733                 mdvi_free(file);
734         }
735         if(in == NULL)
736                 return -1;
737         dstring_init(&input);
738         while((line = dgets(&input, in)) != NULL) {
739                 char    *arg;
740                 
741                 SKIPSP(line);
742                 if(*line < ' ' || *line == '#' || *line == '%')
743                         continue;
744                 if(STRNEQ(line, "fontmap", 7)) {
745                         DviFontMapEnt *ent;
746                         
747                         arg = getstring(line + 7, " \t", &line); *line = 0;
748                         DEBUG((DBG_FMAP, "%s: loading fontmap\n", arg));
749                         ent = mdvi_load_fontmap(arg);
750                         if(ent == NULL)
751                                 warning(_("%s: could not load fontmap\n"), arg);
752                         else {
753                                 DEBUG((DBG_FMAP, 
754                                         "%s: installing fontmap\n", arg));
755                                 mdvi_install_fontmap(ent);
756                                 count++;
757                         }
758                 } else if(STRNEQ(line, "encoding", 8)) {
759                         arg = getstring(line + 8, " \t", &line); *line = 0;
760                         if(arg && *arg)
761                                 register_encoding(arg, 1);
762                 } else if(STRNEQ(line, "default-encoding", 16)) {
763                         arg = getstring(line + 16, " \t", &line); *line = 0;
764                         if(mdvi_set_default_encoding(arg) < 0)
765                                 warning(_("%s: could not set as default encoding\n"),
766                                         arg);
767                 } else if(STRNEQ(line, "psfontpath", 10)) {
768                         arg = getstring(line + 11, " \t", &line); *line = 0;
769                         if(!psinitialized)
770                                 ps_init_default_paths();
771                         if(psfontdir)
772                                 mdvi_free(psfontdir);
773                         psfontdir = kpse_path_expand(arg);
774                 } else if(STRNEQ(line, "pslibpath", 9)) {
775                         arg = getstring(line + 10, " \t", &line); *line = 0;
776                         if(!psinitialized)
777                                 ps_init_default_paths();
778                         if(pslibdir)
779                                 mdvi_free(pslibdir);
780                         pslibdir = kpse_path_expand(arg);
781                 } else if(STRNEQ(line, "psfontmap", 9)) {
782                         arg = getstring(line + 9, " \t", &line); *line = 0;
783                         if(mdvi_ps_read_fontmap(arg) < 0)
784                                 warning("%s: %s: could not read PS fontmap\n",
785                                         config, arg);
786                 }
787         }
788         fclose(in);
789         dstring_reset(&input);
790         fontmaps_loaded = 1;
791         DEBUG((DBG_FMAP, "%d files installed, %d fontmaps\n",
792                 count, fontmaps.count));
793         return count;
794 }
795
796 int     mdvi_query_fontmap(DviFontMapInfo *info, const char *fontname)
797 {
798         DviFontMapEnt *ent;
799
800         if(!fontmaps_loaded && mdvi_init_fontmaps() < 0)
801                 return -1;              
802         ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(fontname));
803
804         if(ent == NULL)
805                 return -1;
806         info->psname   = ent->psname;
807         info->encoding = ent->encoding;
808         info->fontfile = ent->fontfile;
809         info->extend   = ent->extend;
810         info->slant    = ent->slant;
811         info->fullfile = ent->fullfile;
812         
813         return 0;       
814 }
815
816 int     mdvi_add_fontmap_file(const char *name, const char *fullpath)
817 {
818         DviFontMapEnt *ent;
819         
820         if(!fontmaps_loaded && mdvi_init_fontmaps() < 0)
821                 return -1;
822         ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(name));
823         if(ent == NULL)
824                 return -1;
825         if(ent->fullfile)
826                 mdvi_free(ent->fullfile);
827         ent->fullfile = mdvi_strdup(fullpath);
828         return 0;
829 }
830
831
832 void    mdvi_flush_encodings(void)
833 {
834         DviEncoding *enc;
835
836         if(enctable.nbucks == 0)
837                 return;
838
839         DEBUG((DBG_FMAP, "flushing %d encodings\n", encodings.count));
840         /* asked to remove all encodings */
841         for(; (enc = (DviEncoding *)encodings.head); ) {
842                 encodings.head = LIST(enc->next);
843                 if((enc != tex_text_encoding && enc->links) || enc->links > 1) {
844                         warning(_("encoding vector `%s' is in use\n"),
845                                 enc->name);
846                 }
847                 destroy_encoding(enc);
848         }
849         /* destroy the static encoding */
850         if(tex_text_encoding->nametab.buckets)
851                 mdvi_hash_reset(&tex_text_encoding->nametab, 0);
852         mdvi_hash_reset(&enctable, 0);
853         mdvi_hash_reset(&enctable_file, 0);     
854 }
855
856 void    mdvi_flush_fontmaps(void)
857 {
858         DviFontMapEnt *ent;
859         
860         if(!fontmaps_loaded)
861                 return;
862
863         DEBUG((DBG_FMAP, "flushing %d fontmaps\n", fontmaps.count));
864         for(; (ent = (DviFontMapEnt *)fontmaps.head); ) {
865                 fontmaps.head = LIST(ent->next);
866                 free_ent(ent);
867         }
868         mdvi_hash_reset(&maptable, 0);
869         fontmaps_loaded = 0;
870 }
871
872 /* reading of PS fontmaps */
873
874 void    ps_init_default_paths(void)
875 {
876         char    *kppath;
877         char    *kfpath;
878
879         ASSERT(psinitialized == 0);
880
881         kppath = getenv("GS_LIB");
882         kfpath = getenv("GS_FONTPATH"); 
883
884         if(kppath != NULL)
885                 pslibdir = kpse_path_expand(kppath);
886         if(kfpath != NULL)
887                 psfontdir = kpse_path_expand(kfpath);
888
889         listh_init(&psfonts);
890         mdvi_hash_create(&pstable, PSMAP_HASH_SIZE);
891         psinitialized = 1;
892 }
893
894 int     mdvi_ps_read_fontmap(const char *name)
895 {
896         char    *fullname;
897         FILE    *in;
898         Dstring dstr;
899         char    *line;
900         int     count = 0;
901         
902         if(!psinitialized)
903                 ps_init_default_paths();
904         if(pslibdir)
905                 fullname = kpse_path_search(pslibdir, name, 1);
906         else
907                 fullname = (char *)name;
908         in = fopen(fullname, "r");
909         if(in == NULL) {
910                 if(fullname != name)
911                         mdvi_free(fullname);
912                 return -1;
913         }
914         dstring_init(&dstr);
915         
916         while((line = dgets(&dstr, in)) != NULL) {
917                 char    *name;
918                 char    *mapname;
919                 const char *ext;
920                 PSFontMap *ps;
921                 
922                 SKIPSP(line);
923                 /* we're looking for lines of the form
924                  *  /FONT-NAME    (fontfile)
925                  *  /FONT-NAME    /FONT-ALIAS 
926                  */
927                 if(*line != '/')
928                         continue;
929                 name = getword(line + 1, " \t", &line);
930                 if(*line) *line++ = 0;
931                 mapname = getword(line, " \t", &line);
932                 if(*line) *line++ = 0;
933                 
934                 if(!name || !mapname || !*name)
935                         continue;
936                 if(*mapname == '(') {
937                         char    *end;
938                         
939                         mapname++;
940                         for(end = mapname; *end && *end != ')'; end++);
941                         *end = 0;
942                 }
943                 if(!*mapname)
944                         continue;
945                 /* dont add `.gsf' fonts, which require a full blown
946                  * PostScript interpreter */
947                 ext = file_extension(mapname);
948                 if(ext && STREQ(ext, "gsf")) {
949                         DEBUG((DBG_FMAP, "(ps) %s: font `%s' ignored\n",
950                                 name, mapname));
951                         continue;
952                 }
953                 ps = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(name));
954                 if(ps != NULL) {
955                         if(STREQ(ps->mapname, mapname))
956                                 continue;
957                         DEBUG((DBG_FMAP, 
958                                 "(ps) replacing font `%s' (%s) by `%s'\n",
959                                 name, ps->mapname, mapname));
960                         mdvi_free(ps->mapname);
961                         ps->mapname = mdvi_strdup(mapname);
962                         if(ps->fullname) {
963                                 mdvi_free(ps->fullname);
964                                 ps->fullname = NULL;
965                         }
966                 } else {
967                         DEBUG((DBG_FMAP, "(ps) adding font `%s' as `%s'\n",
968                                 name, mapname));
969                         ps = xalloc(PSFontMap);
970                         ps->psname   = mdvi_strdup(name);
971                         ps->mapname  = mdvi_strdup(mapname);
972                         ps->fullname = NULL;
973                         listh_append(&psfonts, LIST(ps));
974                         mdvi_hash_add(&pstable, MDVI_KEY(ps->psname),
975                                 ps, MDVI_HASH_UNCHECKED);
976                         count++;
977                 }
978         }
979         fclose(in);
980         dstring_reset(&dstr);
981         
982         DEBUG((DBG_FMAP, "(ps) %s: %d PostScript fonts registered\n",
983                 fullname, count));
984         return 0;
985 }
986
987 void    mdvi_ps_flush_fonts(void)
988 {
989         PSFontMap *map;
990         
991         if(!psinitialized)
992                 return;
993         DEBUG((DBG_FMAP, "(ps) flushing PS font map (%d) entries\n",
994                 psfonts.count));
995         mdvi_hash_reset(&pstable, 0);
996         for(; (map = (PSFontMap *)psfonts.head); ) {
997                 psfonts.head = LIST(map->next);
998                 mdvi_free(map->psname);
999                 mdvi_free(map->mapname);
1000                 if(map->fullname)
1001                         mdvi_free(map->fullname);
1002                 mdvi_free(map);
1003         }
1004         listh_init(&psfonts);
1005         if(pslibdir) {
1006                 mdvi_free(pslibdir);
1007                 pslibdir = NULL;
1008         }
1009         if(psfontdir) {
1010                 mdvi_free(psfontdir);
1011                 psfontdir = NULL;
1012         }
1013         psinitialized = 0;
1014 }
1015
1016 char    *mdvi_ps_find_font(const char *psname)
1017 {
1018         PSFontMap *map, *smap;
1019         char    *filename;
1020         int     recursion_limit = 32;
1021
1022         DEBUG((DBG_FMAP, "(ps) resolving PS font `%s'\n", psname));
1023         if(!psinitialized)
1024                 return NULL;
1025         map = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(psname));
1026         if(map == NULL)
1027                 return NULL;
1028         if(map->fullname)
1029                 return mdvi_strdup(map->fullname);
1030
1031         /* is it an alias? */
1032         smap = map;
1033         while(recursion_limit-- > 0 && smap && *smap->mapname == '/')
1034                 smap = (PSFontMap *)mdvi_hash_lookup(&pstable, 
1035                         MDVI_KEY(smap->mapname + 1));
1036         if(smap == NULL) {
1037                 if(recursion_limit == 0)
1038                         DEBUG((DBG_FMAP, 
1039                                 "(ps) %s: possible loop in PS font map\n",
1040                                 psname));
1041                 return NULL;
1042         }
1043
1044         if(psfontdir)
1045                 filename = kpse_path_search(psfontdir, smap->mapname, 1);
1046         else if(file_exists(map->mapname))
1047                 filename = mdvi_strdup(map->mapname);
1048         else
1049                 filename = NULL;
1050         if(filename)
1051                 map->fullname = mdvi_strdup(filename);
1052                 
1053         return filename;
1054 }
1055
1056 /*
1057  * To get metric info for a font, we proceed as follows:
1058  *  - We try to find NAME.<tfm,ofm,afm>.
1059  *  - We query the fontmap for NAME.
1060  *  - We get back a PSNAME, and use to find the file in the PS font map.
1061  *  - We get the PSFONT file name, replace its extension by "afm" and
1062  *  lookup the file in GS's font search path.
1063  *  - We finally read the data, transform it as specified in our font map,
1064  *  and return it to the caller. The new data is left in the font metrics
1065  *  cache, so the next time it will be found at the first step (when we look
1066  *  up NAME.afm).
1067  *
1068  * The name `_ps_' in this function is not meant to imply that it can be 
1069  * used for Type1 fonts only. It should be usable for TrueType fonts as well.
1070  *
1071  * The returned metric info is subjected to the same caching mechanism as
1072  * all the other metric data, as returned by get_font_metrics(). One should
1073  * not modify the returned data at all, and it should be disposed with
1074  * free_font_metrics().
1075  */
1076 TFMInfo *mdvi_ps_get_metrics(const char *fontname)
1077 {
1078         TFMInfo *info;
1079         DviFontMapInfo map;
1080         char    buffer[64]; /* to avoid mallocs */
1081         char    *psfont;
1082         char    *basefile;
1083         char    *afmfile;
1084         char    *ext;
1085         int     baselen;
1086         int     nc;
1087         TFMChar *ch;
1088         double  efactor;
1089         double  sfactor;
1090
1091         DEBUG((DBG_FMAP, "(ps) %s: looking for metric data\n", fontname));      
1092         info = get_font_metrics(fontname, DviFontAny, NULL);
1093         if(info != NULL)
1094                 return info;
1095
1096         /* query the fontmap */
1097         if(mdvi_query_fontmap(&map, fontname) < 0 || !map.psname)
1098                 return NULL;
1099         
1100         /* get the PS font */
1101         psfont = mdvi_ps_find_font(map.psname);
1102         if(psfont == NULL)
1103                 return NULL;
1104         DEBUG((DBG_FMAP, "(ps) %s: found as PS font `%s'\n",
1105                 fontname, psfont));
1106         /* replace its extension */
1107         basefile = strrchr(psfont, '/');
1108         if(basefile == NULL)
1109                 basefile = psfont;
1110         baselen = strlen(basefile);
1111         ext = strrchr(basefile, '.');
1112         if(ext != NULL)
1113                 *ext = 0;
1114         if(baselen + 4 < 64)
1115                 afmfile = &buffer[0];
1116         else
1117                 afmfile = mdvi_malloc(baselen + 5);
1118         strcpy(afmfile, basefile);
1119         strcpy(afmfile + baselen, ".afm");
1120         /* we don't need this anymore */
1121         mdvi_free(psfont);
1122         DEBUG((DBG_FMAP, "(ps) %s: looking for `%s'\n",
1123                 fontname, afmfile));
1124         /* lookup the file */
1125         psfont = kpse_path_search(psfontdir, afmfile, 1);
1126         /* don't need this anymore */
1127         if(afmfile != &buffer[0])
1128                 mdvi_free(afmfile);
1129         if(psfont != NULL) {
1130                 info = get_font_metrics(fontname, DviFontAFM, psfont);
1131                 mdvi_free(psfont);
1132         } else
1133                 info = NULL;
1134         if(info == NULL || (!map.extend && !map.slant))
1135                 return info;
1136
1137         /* 
1138          * transform the data as prescribed -- keep in mind that `info' 
1139          * points to CACHED data, so we're modifying the metric cache 
1140          * in place.
1141          */
1142
1143 #define DROUND(x)       ((x) >= 0 ? floor((x) + 0.5) : ceil((x) - 0.5))
1144 #define TRANSFORM(x,y)  DROUND(efactor * (x) + sfactor * (y))
1145
1146         efactor = (double)map.extend / 10000.0;
1147         sfactor = (double)map.slant / 10000.0;
1148         DEBUG((DBG_FMAP, "(ps) %s: applying extend=%f, slant=%f\n",
1149                 efactor, sfactor));
1150
1151         nc = info->hic - info->loc + 1;
1152         for(ch = info->chars; ch < info->chars + nc; ch++) {
1153                 /* the AFM bounding box is:
1154                  *    wx = ch->advance
1155                  *   llx = ch->left
1156                  *   lly = -ch->depth
1157                  *   urx = ch->right
1158                  *   ury = ch->height
1159                  * what we do here is transform wx, llx, and urx by
1160                  *         newX = efactor * oldX + sfactor * oldY
1161                  * where for `wx' oldY = 0. Also, these numbers are all in
1162                  * TFM units (i.e. TFM's fix-words, which is just the actual
1163                  * number times 2^20, no need to do anything to it).
1164                  */
1165                 if(ch->present) {
1166                         ch->advance = TRANSFORM(ch->advance, 0);
1167                         ch->left    = TRANSFORM(ch->left, -ch->depth);
1168                         ch->right   = TRANSFORM(ch->right, ch->height);
1169                 }
1170         }
1171         
1172         return info;
1173 }