Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
fontmap.c
Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 {
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 
55 
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 
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") */
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, "rb");
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 */
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, "rb");
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 
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),
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 
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, "rb");
482  else {
483  in = fopen(ptr, "rb");
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 
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;
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),
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 */
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, "rb");
732  else {
733  in = fopen(file, "rb");
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, *map_file;
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  map_file = kpse_find_file(arg, kpse_fontmap_format, 0);
753  if (map_file)
754  ent = mdvi_load_fontmap(map_file);
755  }
756  if(ent == NULL)
757  mdvi_warning(_("%s: could not load fontmap\n"), arg);
758  else {
759  DEBUG((DBG_FMAP,
760  "%s: installing fontmap\n", arg));
762  count++;
763  }
764  } else if(STRNEQ(line, "encoding", 8)) {
765  arg = getstring(line + 8, " \t", &line); *line = 0;
766  if(arg && *arg)
767  register_encoding(arg, 1);
768  } else if(STRNEQ(line, "default-encoding", 16)) {
769  arg = getstring(line + 16, " \t", &line); *line = 0;
770  if(mdvi_set_default_encoding(arg) < 0)
771  mdvi_warning(_("%s: could not set as default encoding\n"),
772  arg);
773  } else if(STRNEQ(line, "psfontpath", 10)) {
774  arg = getstring(line + 11, " \t", &line); *line = 0;
775  if(!psinitialized)
777  if(psfontdir)
779  psfontdir = kpse_path_expand(arg);
780  } else if(STRNEQ(line, "pslibpath", 9)) {
781  arg = getstring(line + 10, " \t", &line); *line = 0;
782  if(!psinitialized)
784  if(pslibdir)
786  pslibdir = kpse_path_expand(arg);
787  } else if(STRNEQ(line, "psfontmap", 9)) {
788  arg = getstring(line + 9, " \t", &line); *line = 0;
789  if(mdvi_ps_read_fontmap(arg) < 0)
790  mdvi_warning("%s: %s: could not read PS fontmap\n",
791  config, arg);
792  }
793  }
794  fclose(in);
795  dstring_reset(&input);
796  fontmaps_loaded = 1;
797  DEBUG((DBG_FMAP, "%d files installed, %d fontmaps\n",
798  count, fontmaps.count));
799  return count;
800 }
801 
802 int mdvi_query_fontmap(DviFontMapInfo *info, const char *fontname)
803 {
804  DviFontMapEnt *ent;
805 
806  if(!fontmaps_loaded && mdvi_init_fontmaps() < 0)
807  return -1;
808  ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(fontname));
809 
810  if(ent == NULL)
811  return -1;
812  info->psname = ent->psname;
813  info->encoding = ent->encoding;
814  info->fontfile = ent->fontfile;
815  info->extend = ent->extend;
816  info->slant = ent->slant;
817  info->fullfile = ent->fullfile;
818 
819  return 0;
820 }
821 
822 int mdvi_add_fontmap_file(const char *name, const char *fullpath)
823 {
824  DviFontMapEnt *ent;
825 
826  if(!fontmaps_loaded && mdvi_init_fontmaps() < 0)
827  return -1;
828  ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(name));
829  if(ent == NULL)
830  return -1;
831  if(ent->fullfile)
832  mdvi_free(ent->fullfile);
833  ent->fullfile = mdvi_strdup(fullpath);
834  return 0;
835 }
836 
837 
839 {
840  DviEncoding *enc;
841 
842  if(enctable.nbucks == 0)
843  return;
844 
845  DEBUG((DBG_FMAP, "flushing %d encodings\n", encodings.count));
846  /* asked to remove all encodings */
847  for(; (enc = (DviEncoding *)encodings.head); ) {
848  encodings.head = LIST(enc->next);
849  if((enc != tex_text_encoding && enc->links) || enc->links > 1) {
850  mdvi_warning(_("encoding vector `%s' is in use\n"),
851  enc->name);
852  }
853  destroy_encoding(enc);
854  }
855  /* destroy the static encoding */
856  if(tex_text_encoding->nametab.buckets)
857  mdvi_hash_reset(&tex_text_encoding->nametab, 0);
858  mdvi_hash_reset(&enctable, 0);
859  mdvi_hash_reset(&enctable_file, 0);
860 }
861 
863 {
864  DviFontMapEnt *ent;
865 
866  if(!fontmaps_loaded)
867  return;
868 
869  DEBUG((DBG_FMAP, "flushing %d fontmaps\n", fontmaps.count));
870  for(; (ent = (DviFontMapEnt *)fontmaps.head); ) {
871  fontmaps.head = LIST(ent->next);
872  free_ent(ent);
873  }
874  mdvi_hash_reset(&maptable, 0);
875  fontmaps_loaded = 0;
876 }
877 
878 /* reading of PS fontmaps */
879 
881 {
882  char *kppath;
883  char *kfpath;
884 
885  ASSERT(psinitialized == 0);
886 
887  kppath = getenv("GS_LIB");
888  kfpath = getenv("GS_FONTPATH");
889 
890  if(kppath != NULL)
891  pslibdir = kpse_path_expand(kppath);
892  if(kfpath != NULL)
893  psfontdir = kpse_path_expand(kfpath);
894 
895  listh_init(&psfonts);
897  psinitialized = 1;
898 }
899 
900 int mdvi_ps_read_fontmap(const char *name)
901 {
902  char *fullname;
903  FILE *in;
904  Dstring dstr;
905  char *line;
906  int count = 0;
907 
908  if(!psinitialized)
910  if(pslibdir)
911  fullname = kpse_path_search(pslibdir, name, 1);
912  else
913  fullname = (char *)name;
914  in = fopen(fullname, "rb");
915  if(in == NULL) {
916  if(fullname != name)
917  mdvi_free(fullname);
918  return -1;
919  }
920  dstring_init(&dstr);
921 
922  while((line = dgets(&dstr, in)) != NULL) {
923  char *name;
924  char *mapname;
925  const char *ext;
926  PSFontMap *ps;
927 
928  SKIPSP(line);
929  /* we're looking for lines of the form
930  * /FONT-NAME (fontfile)
931  * /FONT-NAME /FONT-ALIAS
932  */
933  if(*line != '/')
934  continue;
935  name = getword(line + 1, " \t", &line);
936  if(*line) *line++ = 0;
937  mapname = getword(line, " \t", &line);
938  if(*line) *line++ = 0;
939 
940  if(!name || !mapname || !*name)
941  continue;
942  if(*mapname == '(') {
943  char *end;
944 
945  mapname++;
946  for(end = mapname; *end && *end != ')'; end++);
947  *end = 0;
948  }
949  if(!*mapname)
950  continue;
951  /* dont add `.gsf' fonts, which require a full blown
952  * PostScript interpreter */
953  ext = file_extension(mapname);
954  if(ext && STREQ(ext, "gsf")) {
955  DEBUG((DBG_FMAP, "(ps) %s: font `%s' ignored\n",
956  name, mapname));
957  continue;
958  }
959  ps = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(name));
960  if(ps != NULL) {
961  if(STREQ(ps->mapname, mapname))
962  continue;
963  DEBUG((DBG_FMAP,
964  "(ps) replacing font `%s' (%s) by `%s'\n",
965  name, ps->mapname, mapname));
966  mdvi_free(ps->mapname);
967  ps->mapname = mdvi_strdup(mapname);
968  if(ps->fullname) {
969  mdvi_free(ps->fullname);
970  ps->fullname = NULL;
971  }
972  } else {
973  DEBUG((DBG_FMAP, "(ps) adding font `%s' as `%s'\n",
974  name, mapname));
975  ps = xalloc(PSFontMap);
976  ps->psname = mdvi_strdup(name);
977  ps->mapname = mdvi_strdup(mapname);
978  ps->fullname = NULL;
979  listh_append(&psfonts, LIST(ps));
980  mdvi_hash_add(&pstable, MDVI_KEY(ps->psname),
981  ps, MDVI_HASH_UNCHECKED);
982  count++;
983  }
984  }
985  fclose(in);
986  dstring_reset(&dstr);
987 
988  DEBUG((DBG_FMAP, "(ps) %s: %d PostScript fonts registered\n",
989  fullname, count));
990  return 0;
991 }
992 
994 {
995  PSFontMap *map;
996 
997  if(!psinitialized)
998  return;
999  DEBUG((DBG_FMAP, "(ps) flushing PS font map (%d) entries\n",
1000  psfonts.count));
1001  mdvi_hash_reset(&pstable, 0);
1002  for(; (map = (PSFontMap *)psfonts.head); ) {
1003  psfonts.head = LIST(map->next);
1004  mdvi_free(map->psname);
1005  mdvi_free(map->mapname);
1006  if(map->fullname)
1007  mdvi_free(map->fullname);
1008  mdvi_free(map);
1009  }
1010  listh_init(&psfonts);
1011  if(pslibdir) {
1013  pslibdir = NULL;
1014  }
1015  if(psfontdir) {
1017  psfontdir = NULL;
1018  }
1019  psinitialized = 0;
1020 }
1021 
1022 char *mdvi_ps_find_font(const char *psname)
1023 {
1024  PSFontMap *map, *smap;
1025  char *filename;
1026  int recursion_limit = 32;
1027 
1028  DEBUG((DBG_FMAP, "(ps) resolving PS font `%s'\n", psname));
1029  if(!psinitialized)
1030  return NULL;
1031  map = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(psname));
1032  if(map == NULL)
1033  return NULL;
1034  if(map->fullname)
1035  return mdvi_strdup(map->fullname);
1036 
1037  /* is it an alias? */
1038  smap = map;
1039  while(recursion_limit-- > 0 && smap && *smap->mapname == '/')
1040  smap = (PSFontMap *)mdvi_hash_lookup(&pstable,
1041  MDVI_KEY(smap->mapname + 1));
1042  if(smap == NULL) {
1043  if(recursion_limit == 0)
1044  DEBUG((DBG_FMAP,
1045  "(ps) %s: possible loop in PS font map\n",
1046  psname));
1047  return NULL;
1048  }
1049 
1050  if(psfontdir)
1051  filename = kpse_path_search(psfontdir, smap->mapname, 1);
1052  else if(file_exists(map->mapname))
1053  filename = mdvi_strdup(map->mapname);
1054  else
1055  filename = NULL;
1056  if(filename)
1057  map->fullname = mdvi_strdup(filename);
1058 
1059  return filename;
1060 }
1061 
1062 /*
1063  * To get metric info for a font, we proceed as follows:
1064  * - We try to find NAME.<tfm,ofm,afm>.
1065  * - We query the fontmap for NAME.
1066  * - We get back a PSNAME, and use to find the file in the PS font map.
1067  * - We get the PSFONT file name, replace its extension by "afm" and
1068  * lookup the file in GS's font search path.
1069  * - We finally read the data, transform it as specified in our font map,
1070  * and return it to the caller. The new data is left in the font metrics
1071  * cache, so the next time it will be found at the first step (when we look
1072  * up NAME.afm).
1073  *
1074  * The name `_ps_' in this function is not meant to imply that it can be
1075  * used for Type1 fonts only. It should be usable for TrueType fonts as well.
1076  *
1077  * The returned metric info is subjected to the same caching mechanism as
1078  * all the other metric data, as returned by get_font_metrics(). One should
1079  * not modify the returned data at all, and it should be disposed with
1080  * free_font_metrics().
1081  */
1082 TFMInfo *mdvi_ps_get_metrics(const char *fontname)
1083 {
1084  TFMInfo *info;
1085  DviFontMapInfo map;
1086  char buffer[64]; /* to avoid mallocs */
1087  char *psfont;
1088  char *basefile;
1089  char *afmfile;
1090  char *ext;
1091  int baselen;
1092  int nc;
1093  TFMChar *ch;
1094  double efactor;
1095  double sfactor;
1096 
1097  DEBUG((DBG_FMAP, "(ps) %s: looking for metric data\n", fontname));
1098  info = get_font_metrics(fontname, DviFontAny, NULL);
1099  if(info != NULL)
1100  return info;
1101 
1102  /* query the fontmap */
1103  if(mdvi_query_fontmap(&map, fontname) < 0 || !map.psname)
1104  return NULL;
1105 
1106  /* get the PS font */
1107  psfont = mdvi_ps_find_font(map.psname);
1108  if(psfont == NULL)
1109  return NULL;
1110  DEBUG((DBG_FMAP, "(ps) %s: found as PS font `%s'\n",
1111  fontname, psfont));
1112  /* replace its extension */
1113  basefile = strrchr(psfont, '/');
1114  if(basefile == NULL)
1115  basefile = psfont;
1116  baselen = strlen(basefile);
1117  ext = strrchr(basefile, '.');
1118  if(ext != NULL)
1119  *ext = 0;
1120  if(baselen + 4 < 64)
1121  afmfile = &buffer[0];
1122  else
1123  afmfile = mdvi_malloc(baselen + 5);
1124  strcpy(afmfile, basefile);
1125  strcpy(afmfile + baselen, ".afm");
1126  /* we don't need this anymore */
1127  mdvi_free(psfont);
1128  DEBUG((DBG_FMAP, "(ps) %s: looking for `%s'\n",
1129  fontname, afmfile));
1130  /* lookup the file */
1131  psfont = kpse_path_search(psfontdir, afmfile, 1);
1132  /* don't need this anymore */
1133  if(afmfile != &buffer[0])
1134  mdvi_free(afmfile);
1135  if(psfont != NULL) {
1136  info = get_font_metrics(fontname, DviFontAFM, psfont);
1137  mdvi_free(psfont);
1138  } else
1139  info = NULL;
1140  if(info == NULL || (!map.extend && !map.slant))
1141  return info;
1142 
1143  /*
1144  * transform the data as prescribed -- keep in mind that `info'
1145  * points to CACHED data, so we're modifying the metric cache
1146  * in place.
1147  */
1148 
1149 #define DROUND(x) ((x) >= 0 ? floor((x) + 0.5) : ceil((x) - 0.5))
1150 #define TRANSFORM(x,y) DROUND(efactor * (x) + sfactor * (y))
1151 
1152  efactor = (double)map.extend / 10000.0;
1153  sfactor = (double)map.slant / 10000.0;
1154  DEBUG((DBG_FMAP, "(ps) %s: applying extend=%f, slant=%f\n",
1155  efactor, sfactor));
1156 
1157  nc = info->hic - info->loc + 1;
1158  for(ch = info->chars; ch < info->chars + nc; ch++) {
1159  /* the AFM bounding box is:
1160  * wx = ch->advance
1161  * llx = ch->left
1162  * lly = -ch->depth
1163  * urx = ch->right
1164  * ury = ch->height
1165  * what we do here is transform wx, llx, and urx by
1166  * newX = efactor * oldX + sfactor * oldY
1167  * where for `wx' oldY = 0. Also, these numbers are all in
1168  * TFM units (i.e. TFM's fix-words, which is just the actual
1169  * number times 2^20, no need to do anything to it).
1170  */
1171  if(ch->present) {
1172  ch->advance = TRANSFORM(ch->advance, 0);
1173  ch->left = TRANSFORM(ch->left, -ch->depth);
1174  ch->right = TRANSFORM(ch->right, ch->height);
1175  }
1176  }
1177 
1178  return info;
1179 }