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