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