1 /* mapfile.c: handling of map files/lines
2 Copyright 1996-2014 Han The Thanh, <thanh@pdftex.org>
3 
4 This file is part of pdfTeX.
5 
6 pdfTeX is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 pdfTeX is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "ptexlib.h"
21 #include <kpathsea/c-auto.h>
22 #include <kpathsea/c-memstr.h>
23 #include <math.h>
24 #include <string.h>
25 
26 #define FM_BUF_SIZE     1024
27 
28 static FILE *fm_file;
29 
30 #define fm_open()       \
31     open_input (&fm_file, kpse_fontmap_format, FOPEN_RBIN_MODE)
32 #define fm_close()      xfclose(fm_file, cur_file_name)
33 #define fm_getchar()    xgetc(fm_file)
34 #define fm_eof()        feof(fm_file)
35 
36 typedef enum { FM_DUPIGNORE, FM_REPLACE, FM_DELETE } updatemode;
37 typedef enum { MAPFILE, MAPLINE } maptype;
38 
39 typedef struct mitem {
40     updatemode mode;            /* FM_DUPIGNORE or FM_REPLACE or FM_DELETE */
41     maptype type;               /* map file or map line */
42     char *line;                 /* pointer to map file name or map line */
43     int lineno;                 /* line number in map file */
44 } mapitem;
45 mapitem *mitem = NULL;
46 
47 static const char nontfm[] = "<nontfm>";
48 
49 #define read_field(r, q, buf) do {  \
50     q = buf;                        \
51     while (*r != ' ' && *r != '<' && *r != '"' && *r != '\0') \
52         *q++ = *r++;                \
53     *q = '\0';                      \
54     skip (r, ' ');                  \
55 } while (0)
56 
57 #define set_field(F) do {           \
58     if (q > buf)                    \
59         fm->F = xstrdup(buf);       \
60     if (*r == '\0')                 \
61         goto done;                  \
62 } while (0)
63 
new_fm_entry(void)64 fm_entry *new_fm_entry(void)
65 {
66     fm_entry *fm;
67     fm = xtalloc(1, fm_entry);
68     fm->tfm_name = NULL;
69     fm->sfd_name = NULL;
70     fm->ps_name = NULL;
71     fm->fd_flags = FD_FLAGS_NOT_SET_IN_MAPLINE;
72     fm->ff_name = NULL;
73     fm->encname = NULL;
74     fm->type = 0;
75     fm->slant = 0;
76     fm->extend = 0;
77     fm->links = 0;
78     fm->pid = -1;
79     fm->eid = -1;
80     fm->subfont = NULL;
81     fm->in_use = false;
82     return fm;
83 }
84 
delete_fm_entry(fm_entry * fm)85 void delete_fm_entry(fm_entry * fm)
86 {
87     xfree(fm->tfm_name);
88     xfree(fm->sfd_name);
89     xfree(fm->ps_name);
90     xfree(fm->ff_name);
91     xfree(fm);
92 }
93 
new_ff_entry(void)94 static ff_entry *new_ff_entry(void)
95 {
96     ff_entry *ff;
97     ff = xtalloc(1, ff_entry);
98     ff->ff_name = NULL;
99     ff->ff_path = NULL;
100     return ff;
101 }
102 
delete_ff_entry(ff_entry * ff)103 static void delete_ff_entry(ff_entry * ff)
104 {
105     xfree(ff->ff_name);
106     xfree(ff->ff_path);
107     xfree(ff);
108 }
109 
dummy_fm_entry(void)110 static fm_entry *dummy_fm_entry(void)
111 {
112     static fm_entry const_fm_entry;
113     return & const_fm_entry;
114 }
115 
116 /**********************************************************************/
117 
118 struct avl_table *tfm_tree = NULL;
119 struct avl_table *ps_tree = NULL;
120 struct avl_table *ff_tree = NULL;
121 struct avl_table *encname_tree = NULL;
122 
123 /* AVL sort fm_entry into tfm_tree by tfm_name */
124 
comp_fm_entry_tfm(const void * pa,const void * pb,void * p)125 static int comp_fm_entry_tfm(const void *pa, const void *pb, void *p)
126 {
127     return strcmp(((const fm_entry *) pa)->tfm_name,
128                   ((const fm_entry *) pb)->tfm_name);
129 }
130 
131 /* AVL sort fm_entry into ps_tree by ps_name, slant, and extend */
132 
comp_fm_entry_ps(const void * pa,const void * pb,void * p)133 static int comp_fm_entry_ps(const void *pa, const void *pb, void *p)
134 {
135     int i;
136     const fm_entry *p1 = (const fm_entry *) pa, *p2 = (const fm_entry *) pb;
137     assert(p1->ps_name != NULL && p2->ps_name != NULL);
138     if ((i = strcmp(p1->ps_name, p2->ps_name)) != 0)
139         return i;
140     cmp_return(p1->slant, p2->slant);
141     cmp_return(p1->extend, p2->extend);
142     return 0;
143 }
144 
145 /* AVL sort ff_entry into ff_tree by ff_name */
146 
comp_ff_entry(const void * pa,const void * pb,void * p)147 static int comp_ff_entry(const void *pa, const void *pb, void *p)
148 {
149     return strcmp(((const ff_entry *) pa)->ff_name,
150                   ((const ff_entry *) pb)->ff_name);
151 }
152 
create_avl_trees(void)153 static void create_avl_trees(void)
154 {
155     assert(tfm_tree == NULL);
156     tfm_tree = avl_create(comp_fm_entry_tfm, NULL, &avl_xallocator);
157     assert(tfm_tree != NULL);
158     assert(ps_tree == NULL);
159     ps_tree = avl_create(comp_fm_entry_ps, NULL, &avl_xallocator);
160     assert(ps_tree != NULL);
161     assert(ff_tree == NULL);
162     ff_tree = avl_create(comp_ff_entry, NULL, &avl_xallocator);
163     assert(ff_tree != NULL);
164     assert(encname_tree == NULL);
165     encname_tree = avl_create(comp_string_entry, NULL, &avl_xallocator);
166     assert(encname_tree != NULL);
167 }
168 
169 /**********************************************************************/
170 /*
171 The function avl_do_entry() is not completely symmetrical with regards
172 to tfm_name and ps_name handling, e. g. a duplicate tfm_name gives a
173 "goto exit", and no ps_name link is tried. This is to keep it compatible
174 with the original version.
175 */
176 
avl_do_entry(fm_entry * fm,int mode)177 int avl_do_entry(fm_entry * fm, int mode)
178 {
179     fm_entry *p;
180     void *a;
181     void **aa;
182     boolean suppress_warn = (getpdfsuppresswarningdupmap() > 0);
183 
184     /* handle tfm_name link */
185 
186     if (strcmp(fm->tfm_name, nontfm) != 0) {
187         p = (fm_entry *) avl_find(tfm_tree, fm);
188         if (p != NULL) {
189             switch (mode) {
190             case FM_DUPIGNORE:
191                 if (!suppress_warn)
192                     pdftex_warn
193                         ("fontmap entry for `%s' already exists, duplicates ignored",
194                          fm->tfm_name);
195                 goto exit;
196                 break;
197             case FM_REPLACE:
198             case FM_DELETE:
199                 if (p->in_use) {
200                     pdftex_warn
201                         ("fontmap entry for `%s' has been used, replace/delete not allowed",
202                          fm->tfm_name);
203                     goto exit;
204                 }
205                 a = avl_delete(tfm_tree, p);
206                 assert(a != NULL);
207                 unset_tfmlink(p);
208                 if (!has_pslink(p))
209                     delete_fm_entry(p);
210                 break;
211             default:
212                 assert(0);
213             }
214         }
215         if (mode != FM_DELETE) {
216             aa = avl_probe(tfm_tree, fm);
217             assert(aa != NULL);
218             set_tfmlink(fm);
219         }
220     }
221 
222     /* handle ps_name link */
223 
224     if (fm->ps_name != NULL) {
225         p = (fm_entry *) avl_find(ps_tree, fm);
226         if (p != NULL) {
227             switch (mode) {
228             case FM_DUPIGNORE:
229                 goto exit;
230                 break;
231             case FM_REPLACE:
232             case FM_DELETE:
233                 if (p->in_use)
234                     goto exit;
235                 a = avl_delete(ps_tree, p);
236                 assert(a != NULL);
237                 unset_pslink(p);
238                 if (!has_tfmlink(p))
239                     delete_fm_entry(p);
240                 break;
241             default:
242                 assert(0);
243             }
244         }
245         if (mode != FM_DELETE && is_t1fontfile(fm) && is_included(fm)) {
246             aa = avl_probe(ps_tree, fm);
247             assert(aa != NULL);
248             set_pslink(fm);
249         }
250     }
251   exit:
252     if (!has_tfmlink(fm) && !has_pslink(fm))    /* e. g. after FM_DELETE */
253         return 1;               /* deallocation of fm_entry structure required */
254     else
255         return 0;
256 }
257 
258 /* add the encoding name to an AVL tree. this has nothing to do with writeenc.c */
259 
add_encname(char * s)260 static char *add_encname(char *s)
261 {
262     char *p;
263     void **aa;
264     assert(s != NULL);
265     assert(encname_tree != NULL);
266     if ((p = (char *) avl_find(encname_tree, s)) == NULL) {     /* encoding name not yet registered */
267         p = xstrdup(s);
268         aa = avl_probe(encname_tree, p);
269         assert(aa != NULL);
270     }
271     return p;
272 }
273 
274 /**********************************************************************/
275 /* consistency check for map entry, with warn flag */
276 
check_fm_entry(fm_entry * fm,boolean warn)277 static int check_fm_entry(fm_entry * fm, boolean warn)
278 {
279     int a = 0;
280     assert(fm != NULL);
281 
282     if (is_fontfile(fm) && !is_included(fm)) {
283         if (warn)
284             pdftex_warn
285                 ("ambiguous entry for `%s': font file present but not included, "
286                  "will be treated as font file not present", fm->tfm_name);
287         xfree(fm->ff_name);
288         /* do not set variable |a| as this entry will be still accepted */
289     }
290 
291     /* if both ps_name and font file are missing, drop this entry */
292     if (fm->ps_name == NULL && !is_fontfile(fm)) {
293         if (warn)
294             pdftex_warn
295                 ("invalid entry for `%s': both ps_name and font file missing",
296                  fm->tfm_name);
297         a += 1;
298     }
299 
300     /* TrueType fonts cannot be reencoded without subsetting */
301     if (is_truetype(fm) && is_reencoded(fm) && !is_subsetted(fm)) {
302         if (warn)
303             pdftex_warn
304                 ("invalid entry for `%s': only subsetted TrueType font can be reencoded",
305                  fm->tfm_name);
306         a += 2;
307     }
308 
309     /* SlantFont and ExtendFont can be used only with Type1 fonts */
310     if ((fm->slant != 0 || fm->extend != 0)
311         && !(is_t1fontfile(fm) && is_included(fm))) {
312         if (warn)
313             pdftex_warn
314                 ("invalid entry for `%s': SlantFont/ExtendFont can be used only with embedded Type1 fonts",
315                  fm->tfm_name);
316         a += 4;
317     }
318 
319     /* the value of SlantFont and ExtendFont must be reasonable */
320     if (abs(fm->slant) > 1000) {
321         if (warn)
322             pdftex_warn
323                 ("invalid entry for `%s': too big value of SlantFont (%g)",
324                  fm->tfm_name, fm->slant / 1000.0);
325         a += 8;
326     }
327     if (abs(fm->extend) > 2000) {
328         if (warn)
329             pdftex_warn
330                 ("invalid entry for `%s': too big value of ExtendFont (%g)",
331                  fm->tfm_name, fm->extend / 1000.0);
332         a += 16;
333     }
334 
335     /* subfonts must be used with subsetted non-reencoded TrueType fonts */
336     if (fm->pid != -1 &&
337         !(is_truetype(fm) && is_subsetted(fm) && !is_reencoded(fm))) {
338         if (warn)
339             pdftex_warn
340                 ("invalid entry for `%s': PidEid can be used only with subsetted non-reencoded TrueType fonts",
341                  fm->tfm_name);
342         a += 32;
343     }
344 
345     return a;
346 }
347 
348 /**********************************************************************/
349 /* returns the font number if s is one of the 14 std. font names, -1 otherwise; speed-trimmed. */
350 
check_std_t1font(char * s)351 int check_std_t1font(char *s)
352 {
353     static const char *std_t1font_names[] = {
354         "Courier",              /* 0:7 */
355         "Courier-Bold",         /* 1:12 */
356         "Courier-Oblique",      /* 2:15 */
357         "Courier-BoldOblique",  /* 3:19 */
358         "Helvetica",            /* 4:9 */
359         "Helvetica-Bold",       /* 5:14 */
360         "Helvetica-Oblique",    /* 6:17 */
361         "Helvetica-BoldOblique",        /* 7:21 */
362         "Symbol",               /* 8:6 */
363         "Times-Roman",          /* 9:11 */
364         "Times-Bold",           /* 10:10 */
365         "Times-Italic",         /* 11:12 */
366         "Times-BoldItalic",     /* 12:16 */
367         "ZapfDingbats"          /* 13:12 */
368     };
369     static const int index[] =
370         { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6, -1,
371         3, -1, 7
372     };
373     size_t n;
374     int k = -1;
375     assert(s != NULL);
376     n = strlen(s);
377     if (n > 21)
378         return -1;
379     if (n == 12) {              /* three names have length 12 */
380         switch (*s) {
381         case 'C':
382             k = 1;              /* Courier-Bold */
383             break;
384         case 'T':
385             k = 11;             /* Times-Italic */
386             break;
387         case 'Z':
388             k = 13;             /* ZapfDingbats */
389             break;
390         default:
391             return -1;
392         }
393     } else
394         k = index[n];
395     if (k > -1 && strcmp(std_t1font_names[k], s) == 0)
396         return k;
397     return -1;
398 };
399 
400 /**********************************************************************/
401 
fm_scan_line(void)402 static void fm_scan_line(void)
403 {
404     int a, b, c, j, u = 0, v = 0;
405     float d;
406     fm_entry *fm;
407     char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
408     char *p, *q, *r, *s;
409     switch (mitem->type) {
410     case MAPFILE:
411         p = fm_line;
412         do {
413             c = fm_getchar();
414             append_char_to_buf(c, p, fm_line, FM_BUF_SIZE);
415         }
416         while (c != 10);
417         *(--p) = '\0';
418         r = fm_line;
419         break;
420     case MAPLINE:
421         r = mitem->line;        /* work on string from makecstring() */
422         break;
423     default:
424         assert(0);
425     }
426     if (*r == '\0' || is_cfg_comment(*r))
427         return;
428     fm = new_fm_entry();
429     read_field(r, q, buf);
430     set_field(tfm_name);
431     if (!isdigit((unsigned char)*r)) {         /* 2nd field ps_name may not start with a digit */
432         read_field(r, q, buf);
433         set_field(ps_name);
434     }
435     if (isdigit((unsigned char)*r)) {          /* font descriptor /Flags given? */
436         for (s = r; isdigit((unsigned char)*s); s++);
437         if (*s == ' ' || *s == '"' || *s == '<' || *s == '\0') {        /* not e. g. 8r.enc */
438             fm->fd_flags = atoi(r);
439             while (isdigit((unsigned char)*r))
440                 r++;
441         }
442     }
443     while (1) {                 /* loop through "specials", encoding, font file */
444         skip(r, ' ');
445         switch (*r) {
446         case '\0':
447             goto done;
448         case '"':              /* opening quote */
449             r++;
450             u = v = 0;
451             do {
452                 skip(r, ' ');
453                 if (sscanf(r, "%f %n", &d, &j) > 0) {
454                     s = r + j;  /* jump behind number, eat also blanks, if any */
455                     if (*(s - 1) == 'E' || *(s - 1) == 'e')
456                         s--;    /* e. g. 0.5ExtendFont: %f = 0.5E */
457                     if (str_prefix(s, "SlantFont")) {
458                         d *= 1000.0;    /* correct rounding also for neg. numbers */
459                         fm->slant = (integer) (d > 0 ? d + 0.5 : d - 0.5);
460                         r = s + strlen("SlantFont");
461                     } else if (str_prefix(s, "ExtendFont")) {
462                         d *= 1000.0;
463                         fm->extend = (integer) (d > 0 ? d + 0.5 : d - 0.5);
464                         if (fm->extend == 1000)
465                             fm->extend = 0;
466                         r = s + strlen("ExtendFont");
467                     } else {    /* unknown name */
468                         for (r = s; *r != ' ' && *r != '"' && *r != '\0'; r++); /* jump over name */
469                         c = *r; /* remember char for temporary end of string */
470                         *r = '\0';
471                         pdftex_warn
472                             ("invalid entry for `%s': unknown name `%s' ignored",
473                              fm->tfm_name, s);
474                         *r = c;
475                     }
476                 } else
477                     for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
478             }
479             while (*r == ' ');
480             if (*r == '"')      /* closing quote */
481                 r++;
482             else {
483                 pdftex_warn
484                     ("invalid entry for `%s': closing quote missing",
485                      fm->tfm_name);
486                 goto bad_line;
487             }
488             break;
489         case 'P':              /* handle cases for subfonts like 'PidEid=3,1' */
490             if (sscanf(r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
491                 fm->pid = a;
492                 fm->eid = b;
493                 r += c;
494                 break;
495             }
496         default:               /* encoding or font file specification */
497             a = b = 0;
498             if (*r == '<') {
499                 a = *r++;
500                 if (*r == '<' || *r == '[')
501                     b = *r++;
502             }
503             read_field(r, q, buf);
504             /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
505             if (strlen(buf) > 4 && strcasecmp(strend(buf) - 4, ".enc") == 0) {
506                 fm->encname = add_encname(buf);
507                 u = v = 0;      /* u, v used if intervening blank: "<< foo" */
508             } else if (strlen(buf) > 0) {       /* file name given */
509                 /* font file, formats:
510                  * subsetting:    '<cmr10.pfa'
511                  * no subsetting: '<<cmr10.pfa'
512                  * no embedding:  'cmr10.pfa'
513                  */
514                 if (a == '<' || u == '<') {
515                     set_included(fm);
516                     if ((a == '<' && b == 0) || (a == 0 && v == 0))
517                         set_subsetted(fm);
518                     /* otherwise b == '<' (or '[') => no subsetting */
519                 }
520                 set_field(ff_name);
521                 u = v = 0;
522             } else {
523                 u = a;
524                 v = b;
525             }
526         }
527     }
528   done:
529     if (fm->ps_name != NULL && (check_std_t1font(fm->ps_name) >= 0))
530         set_std_t1font(fm);
531     if (is_fontfile(fm) && strlen(fm_fontfile(fm)) > 3) {
532         if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttf") == 0)
533             set_truetype(fm);
534         else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttc") == 0)
535             set_truetype(fm);
536         else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".otf") == 0)
537             set_opentype(fm);
538         else
539             set_type1(fm);
540     } else
541         set_type1(fm);          /* assume a builtin font is Type1 */
542     if (check_fm_entry(fm, true) != 0)
543         goto bad_line;
544     /*
545        Until here the map line has been completely scanned without errors;
546        fm points to a valid, freshly filled-out fm_entry structure.
547        Now follows the actual work of registering/deleting.
548      */
549     if (handle_subfont_fm(fm, mitem->mode))     /* is this a subfont? */
550         return;
551     if (avl_do_entry(fm, mitem->mode) == 0)     /* if success */
552         return;
553   bad_line:
554     delete_fm_entry(fm);
555 }
556 
557 /**********************************************************************/
558 
fm_read_info(void)559 void fm_read_info(void)
560 {
561     if (tfm_tree == NULL)
562         create_avl_trees();
563     if (mitem->line == NULL)    /* nothing to do */
564         return;
565     mitem->lineno = 1;
566     switch (mitem->type) {
567     case MAPFILE:
568         set_cur_file_name(mitem->line);
569         if (!fm_open()) {
570             pdftex_warn("cannot open font map file");
571         } else {
572             cur_file_name = (char *) nameoffile + 1;
573             tex_printf("{%s", cur_file_name);
574             while (!fm_eof()) {
575                 fm_scan_line();
576                 mitem->lineno++;
577             }
578             fm_close();
579             tex_printf("}");
580             fm_file = NULL;
581         }
582         break;
583     case MAPLINE:
584         cur_file_name = NULL;   /* makes pdftex_warn() shorter */
585         fm_scan_line();
586         break;
587     default:
588         assert(0);
589     }
590     mitem->line = NULL;         /* done with this line */
591     cur_file_name = NULL;
592     return;
593 }
594 
595 /**********************************************************************/
596 
fmlookup(internalfontnumber f)597 static fmentryptr fmlookup(internalfontnumber f)
598 {
599     char *tfm;
600     fm_entry *fm;
601     fm_entry tmp;
602     if (tfm_tree == NULL)
603         fm_read_info();         /* only to read default map file */
604     tfm = makecstring(fontname[f]);
605     assert(strcmp(tfm, nontfm) != 0);
606 
607     /* Look up for full <tfmname>[+-]<expand> */
608     tmp.tfm_name = tfm;
609     fm = (fm_entry *) avl_find(tfm_tree, &tmp);
610     if (fm != NULL) {
611         fm->in_use = true;
612         return (fmentryptr) fm;
613     }
614     return (fmentryptr) dummy_fm_entry();
615 }
616 
hasfmentry(internalfontnumber f)617 boolean hasfmentry(internalfontnumber f)
618 {
619     if (pdffontmap[f] == NULL)
620         pdffontmap[f] = fmlookup(f);
621     assert(pdffontmap[f] != NULL);
622     return pdffontmap[f] != (fmentryptr) dummy_fm_entry();
623 }
624 
625 /* check whether a map entry is valid for font replacement */
626 
fm_valid_for_font_replacement(fm_entry * fm)627 static boolean fm_valid_for_font_replacement(fm_entry * fm)
628 {
629     ff_entry *ff;
630 
631     assert(fm != NULL);
632     assert(is_fontfile(fm));    /* ps_tree should contain only entries with font file */
633     assert(is_type1(fm));       /* ps_tree should contain only Type1 entries */
634 
635     ff = check_ff_exist(fm->ff_name, false);
636     assert(ff != NULL);
637     if (ff->ff_path == NULL)    /* ...there is no font file available */
638         return false;
639     return true;                /* all tests passed */
640 }
641 
642 /**********************************************************************/
643 /*
644  * lookup fontmap by ps_name;
645  * used for Type1 font replacement when embedding of PDF files
646  */
647 
lookup_fontmap(char * ps_name)648 fm_entry *lookup_fontmap(char *ps_name)
649 {
650     fm_entry *fm, *fm2, tmp;
651     char *a, *b, *c, *d, *e, *s;
652     int i, sl, ex;
653     struct avl_traverser t, t2;
654     if (tfm_tree == NULL)
655         fm_read_info();         /* only to read default map file */
656     assert(ps_name != NULL);
657     s = ps_name;
658     if (strlen(ps_name) > 7) {  /* check for subsetted name tag */
659         for (i = 0; i < 6; i++, s++)
660             if (*s < 'A' || *s > 'Z')
661                 break;
662         if (i == 6 && *s == '+')
663             s++;                /* if name tag found, skip behind it */
664         else
665             s = ps_name;
666     }
667 
668     /*
669      * Scan -Slant_<slant> and -Extend_<extend> font name extensions;
670      * three valid formats:
671      * <fontname>-Slant_<slant>
672      * <fontname>-Slant_<slant>-Extend_<extend>
673      * <fontname>-Extend_<extend>
674      * Slant entry must come _before_ Extend entry
675      */
676 
677     tmp.slant = 0;
678     tmp.extend = 0;
679     if ((a = strstr(s, "-Slant_")) != NULL) {
680         b = a + strlen("-Slant_");
681         sl = (int) strtol(b, &e, 10);
682         if ((e != b) && (e == strend(b))) {
683             tmp.slant = sl;
684             *a = '\0';          /* ps_name string ends before "-Slant_" */
685         } else {
686             if (e != b) {       /* only if <slant> is valid number */
687                 if ((c = strstr(e, "-Extend_")) != NULL) {
688                     d = c + strlen("-Extend_");
689                     ex = (int) strtol(d, &e, 10);
690                     if ((e != d) && (e == strend(d))) {
691                         tmp.slant = sl;
692                         tmp.extend = ex;
693                         *a = '\0';      /* ps_name string ends before "-Slant_" */
694                     }
695                 }
696             }
697         }
698     } else {
699         if ((a = strstr(s, "-Extend_")) != NULL) {
700             b = a + strlen("-Extend_");
701             ex = (int) strtol(b, &e, 10);
702             if ((e != b) && (e == strend(b))) {
703                 tmp.extend = ex;
704                 *a = '\0';      /* ps_name string ends before "-Extend_" */
705             }
706         }
707     }
708     tmp.ps_name = s;
709 
710     fm = (fm_entry *) avl_t_find(&t, ps_tree, &tmp);
711     if (fm == NULL)
712         return NULL;            /* no entry found */
713 
714     /* at this point we know there is at least one fm_entry with given ps_name;
715      * we test all such entries and return the first one that is valid for font
716      * replacement */
717 
718     t2 = t;
719     fm2 = (fm_entry *) avl_t_prev(&t2);
720 
721     /* search forward */
722     do {
723         if (fm_valid_for_font_replacement(fm))
724             return fm;
725         fm = (fm_entry *) avl_t_next(&t);
726     } while (fm != NULL && comp_fm_entry_ps(fm, &tmp, NULL) == 0);
727 
728     /* search backward */
729     while (fm2 != NULL && comp_fm_entry_ps(fm2, &tmp, NULL) == 0) {
730         if (fm_valid_for_font_replacement(fm2))
731             return fm2;
732         fm2 = (fm_entry *) avl_t_prev(&t2);
733     }
734 
735     return NULL;
736 }
737 
738 /**********************************************************************/
739 /*
740  * Process map file given by its name or map line contents. Items not
741  * beginning with [+-=] flush default map file, if it has not yet been
742  * read. Leading blanks and blanks immediately following [+-=] are
743  * ignored.
744  */
745 
process_map_item(char * s,int type)746 static void process_map_item(char *s, int type)
747 {
748     char *p;
749     int mode;
750     if (*s == ' ')
751         s++;                    /* ignore leading blank */
752     switch (*s) {
753     case '+':                  /* +mapfile.map, +mapline */
754         mode = FM_DUPIGNORE;    /* insert entry, if it is not duplicate */
755         s++;
756         break;
757     case '=':                  /* =mapfile.map, =mapline */
758         mode = FM_REPLACE;      /* try to replace earlier entry */
759         s++;
760         break;
761     case '-':                  /* -mapfile.map, -mapline */
762         mode = FM_DELETE;       /* try to delete entry */
763         s++;
764         break;
765     default:
766         mode = FM_DUPIGNORE;    /* like +, but also: */
767         mitem->line = NULL;     /* flush default map file name */
768     }
769     if (*s == ' ')
770         s++;                    /* ignore blank after [+-=] */
771     p = s;                      /* map item starts here */
772     switch (type) {
773     case MAPFILE:              /* remove blank at end */
774         while (*p != '\0' && *p != ' ')
775             p++;
776         *p = '\0';
777         break;
778     case MAPLINE:              /* blank at end allowed */
779         break;
780     default:
781         assert(0);
782     }
783     if (mitem->line != NULL)    /* read default map file first */
784         fm_read_info();
785     if (*s != '\0') {           /* only if real item to process */
786         mitem->mode = mode;
787         mitem->type = type;
788         mitem->line = s;
789         fm_read_info();
790     }
791 }
792 
pdfmapfile(integer t)793 void pdfmapfile(integer t)
794 {
795     process_map_item(makecstring(tokenstostring(t)), MAPFILE);
796     flushstr(lasttokensstring);
797 }
798 
pdfmapline(integer t)799 void pdfmapline(integer t)
800 {
801     process_map_item(makecstring(tokenstostring(t)), MAPLINE);
802     flushstr(lasttokensstring);
803 }
804 
pdfinitmapfile(const_string map_name)805 void pdfinitmapfile(const_string map_name)
806 {
807     assert(mitem == NULL);
808     mitem = xtalloc(1, mapitem);
809     mitem->mode = FM_DUPIGNORE;
810     mitem->type = MAPFILE;
811     mitem->line = xstrdup(map_name);
812 }
813 
814 /**********************************************************************/
815 /*
816  * Early check whether a font file exists. Search tree ff_tree is used
817  * in 1st instance, as it may be faster than the kpse_find_file(), and
818  * kpse_find_file() is called only once per font file name + expansion
819  * parameter. This might help keeping speed, if many PDF pages with
820  * same fonts are to be embedded.
821  *
822  * The ff_tree contains only font files, which are actually needed,
823  * so this tree typically is much smaller than the tfm_tree or ps_tree.
824  */
825 
check_ff_exist(char * ff_name,boolean is_tt)826 ff_entry *check_ff_exist(char *ff_name, boolean is_tt)
827 {
828     ff_entry *ff;
829     ff_entry tmp;
830     void **aa;
831 
832     assert(ff_name != NULL);
833     tmp.ff_name = ff_name;
834     ff = (ff_entry *) avl_find(ff_tree, &tmp);
835     if (ff == NULL) {           /* not yet in database */
836         ff = new_ff_entry();
837         ff->ff_name = xstrdup(ff_name);
838         if (is_tt)
839             ff->ff_path = kpse_find_file(ff_name, kpse_truetype_format, 0);
840         else
841             ff->ff_path = kpse_find_file(ff_name, kpse_type1_format, 0);
842         aa = avl_probe(ff_tree, ff);
843         assert(aa != NULL);
844     }
845     return ff;
846 }
847 
848 /**********************************************************************/
849 /* cleaning up... */
850 
destroy_fm_entry_tfm(void * pa,void * pb)851 static void destroy_fm_entry_tfm(void *pa, void *pb)
852 {
853     fm_entry *fm;
854     fm = (fm_entry *) pa;
855     if (!has_pslink(fm))
856         delete_fm_entry(fm);
857     else
858         unset_tfmlink(fm);
859 }
860 
destroy_fm_entry_ps(void * pa,void * pb)861 static void destroy_fm_entry_ps(void *pa, void *pb)
862 {
863     fm_entry *fm;
864     fm = (fm_entry *) pa;
865     if (!has_tfmlink(fm))
866         delete_fm_entry(fm);
867     else
868         unset_pslink(fm);
869 }
870 
destroy_ff_entry(void * pa,void * pb)871 static void destroy_ff_entry(void *pa, void *pb)
872 {
873     ff_entry *ff;
874     ff = (ff_entry *) pa;
875     delete_ff_entry(ff);
876 }
877 
fm_free(void)878 void fm_free(void)
879 {
880     if (tfm_tree != NULL) {
881         avl_destroy(tfm_tree, destroy_fm_entry_tfm);
882         tfm_tree = NULL;
883     }
884     if (ps_tree != NULL) {
885         avl_destroy(ps_tree, destroy_fm_entry_ps);
886         ps_tree = NULL;
887     }
888     if (ff_tree != NULL) {
889         avl_destroy(ff_tree, destroy_ff_entry);
890         ff_tree = NULL;
891     }
892 }
893 
894 /**********************************************************************/
895 /* end of mapfile.c */
896 // vim: ts=4
897