1 /* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
2
3 Copyright (C) 2002-2015 by Jin-Hwan Cho and Shunsaku Hirata,
4 the dvipdfmx project team.
5
6 Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "system.h"
28 #include "mem.h"
29 #include "error.h"
30
31 #include "dpxfile.h"
32 #include "dpxutil.h"
33
34 #include "subfont.h"
35
36 #include "fontmap.h"
37
38 /* CIDFont */
39 static char *strip_options (const char *map_name, fontmap_opt *opt);
40
41 static int verbose = 0;
42 void
pdf_fontmap_set_verbose(void)43 pdf_fontmap_set_verbose (void)
44 {
45 verbose++;
46 }
47
48
49 void
pdf_init_fontmap_record(fontmap_rec * mrec)50 pdf_init_fontmap_record (fontmap_rec *mrec)
51 {
52 ASSERT(mrec);
53
54 mrec->map_name = NULL;
55
56 /* SFD char mapping */
57 mrec->charmap.sfd_name = NULL;
58 mrec->charmap.subfont_id = NULL;
59 /* for OFM */
60 mrec->opt.mapc = -1; /* compatibility */
61
62 mrec->font_name = NULL;
63 mrec->enc_name = NULL;
64
65 mrec->opt.slant = 0.0;
66 mrec->opt.extend = 1.0;
67 mrec->opt.bold = 0.0;
68
69 mrec->opt.flags = 0;
70
71 mrec->opt.design_size = -1.0;
72
73 mrec->opt.tounicode = NULL;
74 mrec->opt.otl_tags = NULL; /* deactivated */
75 mrec->opt.index = 0;
76 mrec->opt.charcoll = NULL;
77 mrec->opt.style = FONTMAP_STYLE_NONE;
78 mrec->opt.stemv = -1; /* not given explicitly by an option */
79
80 mrec->opt.cff_charsets = NULL;
81 }
82
83 void
pdf_clear_fontmap_record(fontmap_rec * mrec)84 pdf_clear_fontmap_record (fontmap_rec *mrec)
85 {
86 ASSERT(mrec);
87
88 if (mrec->map_name)
89 RELEASE(mrec->map_name);
90 if (mrec->charmap.sfd_name)
91 RELEASE(mrec->charmap.sfd_name);
92 if (mrec->charmap.subfont_id)
93 RELEASE(mrec->charmap.subfont_id);
94 if (mrec->enc_name)
95 RELEASE(mrec->enc_name);
96 if (mrec->font_name)
97 RELEASE(mrec->font_name);
98
99 if (mrec->opt.tounicode)
100 RELEASE(mrec->opt.tounicode);
101 if (mrec->opt.otl_tags)
102 RELEASE(mrec->opt.otl_tags);
103 if (mrec->opt.charcoll)
104 RELEASE(mrec->opt.charcoll);
105 pdf_init_fontmap_record(mrec);
106 }
107
108 /* strdup: just returns NULL for NULL */
109 static char *
mstrdup(const char * s)110 mstrdup (const char *s)
111 {
112 char *r;
113 if (!s)
114 return NULL;
115 r = NEW(strlen(s) + 1, char);
116 strcpy(r, s);
117 return r;
118 }
119
120 static void
pdf_copy_fontmap_record(fontmap_rec * dst,const fontmap_rec * src)121 pdf_copy_fontmap_record (fontmap_rec *dst, const fontmap_rec *src)
122 {
123 ASSERT( dst && src );
124
125 dst->map_name = mstrdup(src->map_name);
126
127 dst->charmap.sfd_name = mstrdup(src->charmap.sfd_name);
128 dst->charmap.subfont_id = mstrdup(src->charmap.subfont_id);
129
130 dst->font_name = mstrdup(src->font_name);
131 dst->enc_name = mstrdup(src->enc_name);
132
133 dst->opt.slant = src->opt.slant;
134 dst->opt.extend = src->opt.extend;
135 dst->opt.bold = src->opt.bold;
136
137 dst->opt.flags = src->opt.flags;
138 dst->opt.mapc = src->opt.mapc;
139
140 dst->opt.tounicode = mstrdup(src->opt.tounicode);
141 dst->opt.otl_tags = mstrdup(src->opt.otl_tags);
142 dst->opt.index = src->opt.index;
143 dst->opt.charcoll = mstrdup(src->opt.charcoll);
144 dst->opt.style = src->opt.style;
145 dst->opt.stemv = src->opt.stemv;
146
147 dst->opt.cff_charsets = src->opt.cff_charsets;
148 }
149
150
151 static void
hval_free(void * vp)152 hval_free (void *vp)
153 {
154 fontmap_rec *mrec = (fontmap_rec *) vp;
155 pdf_clear_fontmap_record(mrec);
156 RELEASE(mrec);
157 }
158
159
160 static void
fill_in_defaults(fontmap_rec * mrec,const char * tex_name)161 fill_in_defaults (fontmap_rec *mrec, const char *tex_name)
162 {
163 if (mrec->enc_name &&
164 (!strcmp(mrec->enc_name, "default") ||
165 !strcmp(mrec->enc_name, "none"))) {
166 RELEASE(mrec->enc_name);
167 mrec->enc_name = NULL;
168 }
169 if (mrec->font_name &&
170 (!strcmp(mrec->font_name, "default") ||
171 !strcmp(mrec->font_name, "none"))) {
172 RELEASE(mrec->font_name);
173 mrec->font_name = NULL;
174 }
175 /* We *must* fill font_name either explicitly or by default */
176 if (!mrec->font_name) {
177 mrec->font_name = NEW(strlen(tex_name)+1, char);
178 strcpy(mrec->font_name, tex_name);
179 }
180
181 mrec->map_name = NEW(strlen(tex_name)+1, char);
182 strcpy(mrec->map_name, tex_name);
183
184 #ifndef WITHOUT_COMPAT
185 /* Use "UCS" character collection for Unicode SFD
186 * and Identity CMap combination. For backward
187 * compatibility.
188 */
189 if (mrec->charmap.sfd_name && mrec->enc_name &&
190 !mrec->opt.charcoll) {
191 if ((!strcmp(mrec->enc_name, "Identity-H") ||
192 !strcmp(mrec->enc_name, "Identity-V"))
193 &&
194 (strstr(mrec->charmap.sfd_name, "Uni") ||
195 strstr(mrec->charmap.sfd_name, "UBig") ||
196 strstr(mrec->charmap.sfd_name, "UBg") ||
197 strstr(mrec->charmap.sfd_name, "UGB") ||
198 strstr(mrec->charmap.sfd_name, "UKS") ||
199 strstr(mrec->charmap.sfd_name, "UJIS"))) {
200 mrec->opt.charcoll = NEW(strlen("UCS")+1, char);
201 strcpy(mrec->opt.charcoll, "UCS");
202 }
203 }
204 #endif /* WITHOUT_COMPAT */
205
206 return;
207 }
208
209 static char *
readline(char * buf,int buf_len,FILE * fp)210 readline (char *buf, int buf_len, FILE *fp)
211 {
212 char *p, *q;
213 ASSERT( buf && buf_len > 0 && fp );
214 p = mfgets(buf, buf_len, fp);
215 if (!p)
216 return NULL;
217 q = strchr(p, '%'); /* we don't have quoted string */
218 if (q)
219 *q = '\0';
220 return p;
221 }
222
223 #ifndef ISBLANK
224 # define ISBLANK(c) ((c) == ' ' || (c) == '\t')
225 #endif
226 static void
skip_blank(const char ** pp,const char * endptr)227 skip_blank (const char **pp, const char *endptr)
228 {
229 const char *p = *pp;
230 if (!p || p >= endptr)
231 return;
232 for ( ; p < endptr && ISBLANK(*p); p++);
233 *pp = p;
234 }
235
236 static char *
parse_string_value(const char ** pp,const char * endptr)237 parse_string_value (const char **pp, const char *endptr)
238 {
239 char *q = NULL;
240 const char *p = *pp;
241 int n;
242
243 if (!p || p >= endptr)
244 return NULL;
245 if (*p == '"')
246 q = parse_c_string(&p, endptr);
247 else {
248 for (n = 0; p < endptr && !isspace((unsigned char)*p); p++, n++);
249 if (n == 0)
250 return NULL;
251 q = NEW(n + 1, char);
252 memcpy(q, *pp, n); q[n] = '\0';
253 }
254
255 *pp = p;
256 return q;
257 }
258
259 /* no preceeding spaces allowed */
260 static char *
parse_integer_value(const char ** pp,const char * endptr,int base)261 parse_integer_value (const char **pp, const char *endptr, int base)
262 {
263 char *q;
264 const char *p = *pp;
265 int has_sign = 0, has_prefix = 0, n;
266
267 ASSERT( base == 0 || (base >= 2 && base <= 36) );
268
269 if (!p || p >= endptr)
270 return NULL;
271
272 if (*p == '-' || *p == '+') {
273 p++; has_sign = 1;
274 }
275 if ((base == 0 || base == 16) &&
276 p + 2 <= endptr &&
277 p[0] == '0' && p[1] == 'x') {
278 p += 2; has_prefix = 1;
279 }
280 if (base == 0) {
281 if (has_prefix)
282 base = 16;
283 else if (p < endptr && *p == '0')
284 base = 8;
285 else {
286 base = 10;
287 }
288 }
289 #define ISDIGIT_WB(c,b) ( \
290 ((b) <= 10 && (c) >= '0' && (c) < '0' + (b)) || \
291 ((b) > 10 && ( \
292 ((c) >= '0' && (c) <= '9') || \
293 ((c) >= 'a' && (c) < 'a' + ((b) - 10)) || \
294 ((c) >= 'A' && (c) < 'A' + ((b) - 10)) \
295 ) \
296 ) \
297 )
298 for (n = 0; p < endptr && ISDIGIT_WB(*p, base); p++, n++);
299 if (n == 0)
300 return NULL;
301 if (has_sign)
302 n += 1;
303 if (has_prefix)
304 n += 2;
305
306 q = NEW(n + 1, char);
307 memcpy(q, *pp, n); q[n] = '\0';
308
309 *pp = p;
310 return q;
311 }
312
313 static int
fontmap_parse_mapdef_dpm(fontmap_rec * mrec,const char * mapdef,const char * endptr)314 fontmap_parse_mapdef_dpm (fontmap_rec *mrec,
315 const char *mapdef, const char *endptr)
316 {
317 const char *p = mapdef;
318
319 /*
320 * Parse record line in map file. First two fields (after TeX font
321 * name) are position specific. Arguments start at the first token
322 * beginning with a '-'.
323 *
324 * NOTE:
325 * Dvipdfm basically uses parse_ident() for parsing enc_name,
326 * font_name, and other string values which assumes PostScript-like
327 * syntax.
328 * skip_white() skips '\r' and '\n' but they should terminate
329 * fontmap line here.
330 */
331
332 skip_blank(&p, endptr);
333 /* encoding field */
334 if (p < endptr && *p != '-') { /* May be NULL */
335 mrec->enc_name = parse_string_value(&p, endptr);
336 skip_blank(&p, endptr);
337 }
338
339 /* fontname or font filename field */
340 if (p < endptr && *p != '-') { /* May be NULL */
341 mrec->font_name = parse_string_value(&p, endptr);
342 skip_blank(&p, endptr);
343 }
344 if (mrec->font_name) {
345 char *tmp;
346 /* Several options are encoded in font_name for
347 * compatibility with dvipdfm.
348 */
349 tmp = strip_options(mrec->font_name, &mrec->opt);
350 if (tmp) {
351 RELEASE(mrec->font_name);
352 mrec->font_name = tmp;
353 }
354 }
355
356 skip_blank(&p, endptr);
357 /* Parse any remaining arguments */
358 while (p + 1 < endptr &&
359 *p != '\r' && *p != '\n' && *p == '-') {
360 char *q, mopt = p[1];
361 long v;
362
363 p += 2; skip_blank(&p, endptr);
364 switch (mopt) {
365
366 case 's': /* Slant option */
367 q = parse_float_decimal(&p, endptr);
368 if (!q) {
369 WARN("Missing a number value for 's' option.");
370 return -1;
371 }
372 mrec->opt.slant = atof(q);
373 RELEASE(q);
374 break;
375
376 case 'e': /* Extend option */
377 q = parse_float_decimal(&p, endptr);
378 if (!q) {
379 WARN("Missing a number value for 'e' option.");
380 return -1;
381 }
382 mrec->opt.extend = atof(q);
383 if (mrec->opt.extend <= 0.0) {
384 WARN("Invalid value for 'e' option: %s", q);
385 return -1;
386 }
387 RELEASE(q);
388 break;
389
390 case 'b': /* Fake-bold option */
391 q = parse_float_decimal(&p, endptr);
392 if (!q) {
393 WARN("Missing a number value for 'b' option.");
394 return -1;
395 }
396 mrec->opt.bold = atof(q);
397 if (mrec->opt.bold <= 0.0) {
398 WARN("Invalid value for 'b' option: %s", q);
399 return -1;
400 }
401 RELEASE(q);
402 break;
403
404 case 'r': /* Remap option; obsolete; just ignore */
405 break;
406
407 case 'i': /* TTC index */
408 q = parse_integer_value(&p, endptr, 10);
409 if (!q) {
410 WARN("Missing TTC index number...");
411 return -1;
412 }
413 mrec->opt.index = atoi(q);
414 if (mrec->opt.index < 0) {
415 WARN("Invalid TTC index number: %s", q);
416 return -1;
417 }
418 RELEASE(q);
419 break;
420
421 case 'p': /* UCS plane: just for testing */
422 q = parse_integer_value(&p, endptr, 0);
423 if (!q) {
424 WARN("Missing a number for 'p' option.");
425 return -1;
426 }
427 v = strtol(q, NULL, 0);
428 if (v < 0 || v > 16)
429 WARN("Invalid value for option 'p': %s", q);
430 else {
431 mrec->opt.mapc = v << 16;
432 }
433 RELEASE(q);
434 break;
435
436 case 'u': /* ToUnicode */
437 q = parse_string_value(&p, endptr);
438 if (q)
439 mrec->opt.tounicode = q;
440 else {
441 WARN("Missing string value for option 'u'.");
442 return -1;
443 }
444 break;
445
446 case 'v': /* StemV */
447 q = parse_integer_value(&p, endptr, 10);
448 if (!q) {
449 WARN("Missing a number for 'v' option.");
450 return -1;
451 }
452 mrec->opt.stemv = strtol(q, NULL, 0);
453 RELEASE(q);
454 break;
455
456 /* Omega uses both single-byte and double-byte set_char command
457 * even for double-byte OFMs. This confuses CMap decoder.
458 */
459 case 'm':
460 /* Map single bytes char 0xab to double byte char 0xcdab */
461 if (p + 4 <= endptr &&
462 p[0] == '<' && p[3] == '>') {
463 p++;
464 q = parse_integer_value(&p, endptr, 16);
465 if (!q) {
466 WARN("Invalid value for option 'm'.");
467 return -1;
468 } else if (p < endptr && *p != '>') {
469 WARN("Invalid value for option 'm': %s", q);
470 RELEASE(q);
471 return -1;
472 }
473 v = strtol(q, NULL, 16);
474 mrec->opt.mapc = ((v << 8) & 0x0000ff00L);
475 RELEASE(q); p++;
476 } else if (p + 4 <= endptr &&
477 !memcmp(p, "sfd:", strlen("sfd:"))) {
478 char *r;
479 const char *rr;
480 /* SFD mapping: sfd:Big5,00 */
481 p += 4; skip_blank(&p, endptr);
482 q = parse_string_value(&p, endptr);
483 if (!q) {
484 WARN("Missing value for option 'm'.");
485 return -1;
486 }
487 r = strchr(q, ',');
488 if (!r) {
489 WARN("Invalid value for option 'm': %s", q);
490 RELEASE(q);
491 return -1;
492 }
493 *r = 0; rr = ++r; skip_blank(&rr, r + strlen(r));
494 if (*rr == '\0') {
495 WARN("Invalid value for option 'm': %s,", q);
496 RELEASE(q);
497 return -1;
498 }
499 mrec->charmap.sfd_name = mstrdup(q);
500 mrec->charmap.subfont_id = mstrdup(rr);
501 RELEASE(q);
502 } else if (p + 4 < endptr &&
503 !memcmp(p, "pad:", strlen("pad:"))) {
504 p += 4; skip_blank(&p, endptr);
505 q = parse_integer_value(&p, endptr, 16);
506 if (!q) {
507 WARN("Invalid value for option 'm'.");
508 return -1;
509 } else if (p < endptr && !isspace((unsigned char)*p)) {
510 WARN("Invalid value for option 'm': %s", q);
511 RELEASE(q);
512 return -1;
513 }
514 v = strtol(q, NULL, 16);
515 mrec->opt.mapc = ((v << 8) & 0x0000ff00L);
516 RELEASE(q);
517 } else {
518 WARN("Invalid value for option 'm'.");
519 return -1;
520 }
521 break;
522
523 case 'w': /* Writing mode (for unicode encoding) */
524 if (!mrec->enc_name ||
525 strcmp(mrec->enc_name, "unicode")) {
526 WARN("Fontmap option 'w' meaningless for encoding other than \"unicode\".");
527 return -1;
528 }
529 q = parse_integer_value(&p, endptr, 10);
530 if (!q) {
531 WARN("Missing wmode value...");
532 return -1;
533 }
534 if (atoi(q) == 1)
535 mrec->opt.flags |= FONTMAP_OPT_VERT;
536 else if (atoi(q) == 0)
537 mrec->opt.flags &= ~FONTMAP_OPT_VERT;
538 else {
539 WARN("Invalid value for option 'w': %s", q);
540 }
541 RELEASE(q);
542 break;
543
544 default:
545 WARN("Unrecognized font map option: '%c'", mopt);
546 return -1;
547 }
548 skip_blank(&p, endptr);
549 }
550
551 if (p < endptr && *p != '\r' && *p != '\n') {
552 WARN("Invalid char in fontmap line: %c", *p);
553 return -1;
554 }
555
556 return 0;
557 }
558
559
560 /* Parse record line in map file of DVIPS/pdfTeX format. */
561 static int
fontmap_parse_mapdef_dps(fontmap_rec * mrec,const char * mapdef,const char * endptr)562 fontmap_parse_mapdef_dps (fontmap_rec *mrec,
563 const char *mapdef, const char *endptr)
564 {
565 const char *p = mapdef;
566 char *q;
567
568 skip_blank(&p, endptr);
569
570 /* The first field (after TFM name) must be PostScript name. */
571 /* However, pdftex.map allows a line without PostScript name. */
572
573 if (*p != '"' && *p != '<') {
574 if (p < endptr) {
575 q = parse_string_value(&p, endptr);
576 if (q) RELEASE(q);
577 skip_blank(&p, endptr);
578 } else {
579 WARN("Missing a PostScript font name.");
580 return -1;
581 }
582 }
583
584 if (p >= endptr) return 0;
585
586 /* Parse any remaining arguments */
587 while (p < endptr && *p != '\r' && *p != '\n' && (*p == '<' || *p == '"')) {
588 switch (*p) {
589 case '<': /* encoding or fontfile field */
590 /* If we see <[ or <<, just ignore the second char instead
591 of doing as directed (define encoding file, fully embed); sorry. */
592 if (++p < endptr && (*p == '[' || *p == '<')) p++; /*skip */
593 skip_blank(&p, endptr);
594 if ((q = parse_string_value(&p, endptr))) {
595 int n = strlen(q);
596 if (n > 4 && strncmp(q+n-4, ".enc", 4) == 0)
597 mrec->enc_name = q;
598 else
599 mrec->font_name = q;
600 }
601 skip_blank(&p, endptr);
602 break;
603
604 case '"': /* Options */
605 if ((q = parse_string_value(&p, endptr))) {
606 const char *r = q, *e = q+strlen(q);
607 char *s, *t;
608 skip_blank(&r, e);
609 while (r < e) {
610 if ((s = parse_float_decimal(&r, e))) {
611 skip_blank(&r, e);
612 if ((t = parse_string_value(&r, e))) {
613 if (strcmp(t, "SlantFont") == 0)
614 mrec->opt.slant = atof(s);
615 else if (strcmp(t, "ExtendFont") == 0)
616 mrec->opt.extend = atof(s);
617 RELEASE(t);
618 }
619 RELEASE(s);
620 } else if ((s = parse_string_value(&r, e))) { /* skip */
621 RELEASE(s);
622 }
623 skip_blank(&r, e);
624 }
625 RELEASE(q);
626 }
627 skip_blank(&p, endptr);
628 break;
629
630 default:
631 WARN("Found an invalid entry: %s", p);
632 return -1;
633 }
634 skip_blank(&p, endptr);
635 }
636
637 if (p < endptr && *p != '\r' && *p != '\n') {
638 WARN("Invalid char in fontmap line: %c", *p);
639 return -1;
640 }
641
642 return 0;
643 }
644
645
646 static struct ht_table *fontmap = NULL;
647
648 #define fontmap_invalid(m) (!(m) || !(m)->map_name || !(m)->font_name)
649 static char *
chop_sfd_name(const char * tex_name,char ** sfd_name)650 chop_sfd_name (const char *tex_name, char **sfd_name)
651 {
652 char *fontname;
653 char *p, *q;
654 int m, n, len;
655
656 *sfd_name = NULL;
657
658 p = strchr(tex_name, '@');
659 if (!p ||
660 p[1] == '\0' || p == tex_name) {
661 return NULL;
662 }
663 m = (int) (p - tex_name);
664 p++;
665 q = strchr(p, '@');
666 if (!q || q == p) {
667 return NULL;
668 }
669 n = (int) (q - p);
670 q++;
671
672 len = strlen(tex_name) - n;
673 fontname = NEW(len+1, char);
674 memcpy(fontname, tex_name, m);
675 fontname[m] = '\0';
676 if (*q)
677 strcat(fontname, q);
678
679 *sfd_name = NEW(n+1, char);
680 memcpy(*sfd_name, p, n);
681 (*sfd_name)[n] = '\0';
682
683 return fontname;
684 }
685
686 static char *
make_subfont_name(const char * map_name,const char * sfd_name,const char * sub_id)687 make_subfont_name (const char *map_name, const char *sfd_name, const char *sub_id)
688 {
689 char *tfm_name;
690 int n, m;
691 char *p, *q;
692
693 p = strchr(map_name, '@');
694 if (!p || p == map_name)
695 return NULL;
696 m = (int) (p - map_name);
697 q = strchr(p + 1, '@');
698 if (!q || q == p + 1)
699 return NULL;
700 n = (int) (q - p) + 1; /* including two '@' */
701 if (strlen(sfd_name) != n - 2 ||
702 memcmp(p + 1, sfd_name, n - 2))
703 return NULL;
704 tfm_name = NEW(strlen(map_name) - n + strlen(sub_id) + 1, char);
705 memcpy(tfm_name, map_name, m);
706 tfm_name[m] = '\0';
707 strcat(tfm_name, sub_id);
708 if (q[1]) /* not ending with '@' */
709 strcat(tfm_name, q + 1);
710
711 return tfm_name;
712 }
713
714 /* "foo@A@ ..." is expanded to
715 * fooab ... -m sfd:A,ab
716 * ...
717 * fooyz ... -m sfd:A,yz
718 * where 'ab' ... 'yz' is subfont IDs in SFD 'A'.
719 */
720 int
pdf_append_fontmap_record(const char * kp,const fontmap_rec * vp)721 pdf_append_fontmap_record (const char *kp, const fontmap_rec *vp)
722 {
723 fontmap_rec *mrec;
724 char *fnt_name, *sfd_name = NULL;
725
726 if (!kp || fontmap_invalid(vp)) {
727 WARN("Invalid fontmap record...");
728 return -1;
729 }
730
731 if (verbose > 3)
732 MESG("fontmap>> append key=\"%s\"...", kp);
733
734 fnt_name = chop_sfd_name(kp, &sfd_name);
735 if (fnt_name && sfd_name) {
736 char *tfm_name;
737 char **subfont_ids;
738 int n = 0;
739 subfont_ids = sfd_get_subfont_ids(sfd_name, &n);
740 if (!subfont_ids)
741 return -1;
742 while (n-- > 0) {
743 tfm_name = make_subfont_name(kp, sfd_name, subfont_ids[n]);
744 if (!tfm_name)
745 continue;
746 mrec = ht_lookup_table(fontmap, tfm_name, strlen(tfm_name));
747 if (!mrec) {
748 mrec = NEW(1, fontmap_rec);
749 pdf_init_fontmap_record(mrec);
750 mrec->map_name = mstrdup(kp); /* link */
751 mrec->charmap.sfd_name = mstrdup(sfd_name);
752 mrec->charmap.subfont_id = mstrdup(subfont_ids[n]);
753 ht_insert_table(fontmap, tfm_name, strlen(tfm_name), mrec);
754 }
755 RELEASE(tfm_name);
756 }
757 RELEASE(fnt_name);
758 RELEASE(sfd_name);
759 }
760
761 mrec = ht_lookup_table(fontmap, kp, strlen(kp));
762 if (!mrec) {
763 mrec = NEW(1, fontmap_rec);
764 pdf_copy_fontmap_record(mrec, vp);
765 if (mrec->map_name && !strcmp(kp, mrec->map_name)) {
766 RELEASE(mrec->map_name);
767 mrec->map_name = NULL;
768 }
769 ht_insert_table(fontmap, kp, strlen(kp), mrec);
770 }
771 if (verbose > 3)
772 MESG("\n");
773
774 return 0;
775 }
776
777 int
pdf_remove_fontmap_record(const char * kp)778 pdf_remove_fontmap_record (const char *kp)
779 {
780 char *fnt_name, *sfd_name = NULL;
781
782 if (!kp)
783 return -1;
784
785 if (verbose > 3)
786 MESG("fontmap>> remove key=\"%s\"...", kp);
787
788 fnt_name = chop_sfd_name(kp, &sfd_name);
789 if (fnt_name && sfd_name) {
790 char *tfm_name;
791 char **subfont_ids;
792 int n = 0;
793 subfont_ids = sfd_get_subfont_ids(sfd_name, &n);
794 if (!subfont_ids)
795 return -1;
796 if (verbose > 3)
797 MESG("\nfontmap>> Expand @%s@:", sfd_name);
798 while (n-- > 0) {
799 tfm_name = make_subfont_name(kp, sfd_name, subfont_ids[n]);
800 if (!tfm_name)
801 continue;
802 if (verbose > 3)
803 MESG(" %s", tfm_name);
804 ht_remove_table(fontmap, tfm_name, strlen(tfm_name));
805 RELEASE(tfm_name);
806 }
807 RELEASE(fnt_name);
808 RELEASE(sfd_name);
809 }
810
811 ht_remove_table(fontmap, kp, strlen(kp));
812
813 if (verbose > 3)
814 MESG("\n");
815
816 return 0;
817 }
818
819 fontmap_rec *
pdf_insert_fontmap_record(const char * kp,const fontmap_rec * vp)820 pdf_insert_fontmap_record (const char *kp, const fontmap_rec *vp)
821 {
822 fontmap_rec *mrec;
823 char *fnt_name, *sfd_name;
824
825 if (!kp || fontmap_invalid(vp)) {
826 WARN("Invalid fontmap record...");
827 return NULL;
828 }
829
830 if (verbose > 3)
831 MESG("fontmap>> insert key=\"%s\"...", kp);
832
833 fnt_name = chop_sfd_name(kp, &sfd_name);
834 if (fnt_name && sfd_name) {
835 char *tfm_name;
836 char **subfont_ids;
837 int n = 0;
838 subfont_ids = sfd_get_subfont_ids(sfd_name, &n);
839 if (!subfont_ids) {
840 RELEASE(fnt_name);
841 RELEASE(sfd_name);
842 WARN("Could not open SFD file: %s", sfd_name);
843 return NULL;
844 }
845 if (verbose > 3)
846 MESG("\nfontmap>> Expand @%s@:", sfd_name);
847 while (n-- > 0) {
848 tfm_name = make_subfont_name(kp, sfd_name, subfont_ids[n]);
849 if (!tfm_name)
850 continue;
851 if (verbose > 3)
852 MESG(" %s", tfm_name);
853 mrec = NEW(1, fontmap_rec);
854 pdf_init_fontmap_record(mrec);
855 mrec->map_name = mstrdup(kp); /* link to this entry */
856 mrec->charmap.sfd_name = mstrdup(sfd_name);
857 mrec->charmap.subfont_id = mstrdup(subfont_ids[n]);
858 ht_insert_table(fontmap, tfm_name, strlen(tfm_name), mrec);
859 RELEASE(tfm_name);
860 }
861 RELEASE(fnt_name);
862 RELEASE(sfd_name);
863 }
864
865 mrec = NEW(1, fontmap_rec);
866 pdf_copy_fontmap_record(mrec, vp);
867 if (mrec->map_name && !strcmp(kp, mrec->map_name)) {
868 RELEASE(mrec->map_name);
869 mrec->map_name = NULL;
870 }
871 ht_insert_table(fontmap, kp, strlen(kp), mrec);
872
873 if (verbose > 3)
874 MESG("\n");
875
876 return mrec;
877 }
878
879
880 int
pdf_read_fontmap_line(fontmap_rec * mrec,const char * mline,long mline_len,int format)881 pdf_read_fontmap_line (fontmap_rec *mrec, const char *mline, long mline_len, int format)
882 {
883 int error;
884 char *q;
885 const char *p, *endptr;
886
887 ASSERT(mrec);
888
889 p = mline;
890 endptr = p + mline_len;
891
892 skip_blank(&p, endptr);
893 if (p >= endptr)
894 return -1;
895
896 q = parse_string_value(&p, endptr);
897 if (!q)
898 return -1;
899
900 if (format > 0) /* DVIPDFM format */
901 error = fontmap_parse_mapdef_dpm(mrec, p, endptr);
902 else /* DVIPS/pdfTeX format */
903 error = fontmap_parse_mapdef_dps(mrec, p, endptr);
904 if (!error) {
905 char *fnt_name, *sfd_name = NULL;
906 fnt_name = chop_sfd_name(q, &sfd_name);
907 if (fnt_name && sfd_name) {
908 if (!mrec->font_name) {
909 /* In the case of subfonts, the base name (before the character '@')
910 * will be used as a font_name by default.
911 * Otherwise tex_name will be used as a font_name by default.
912 */
913 mrec->font_name = fnt_name;
914 } else {
915 RELEASE(fnt_name);
916 }
917 if (mrec->charmap.sfd_name)
918 RELEASE(mrec->charmap.sfd_name);
919 mrec->charmap.sfd_name = sfd_name ;
920 }
921 fill_in_defaults(mrec, q);
922 }
923 RELEASE(q);
924
925 return error;
926 }
927
928 /* DVIPS/pdfTeX fontmap line if one of the following three cases found:
929 *
930 * (1) any line including the character '"'
931 * (2) any line including the character '<'
932 * (3) if the line consists of two entries (tfmname and psname)
933 *
934 * DVIPDFM fontmap line otherwise.
935 */
936 int
is_pdfm_mapline(const char * mline)937 is_pdfm_mapline (const char *mline) /* NULL terminated. */
938 {
939 int n = 0;
940 const char *p, *endptr;
941
942 if (strchr(mline, '"') || strchr(mline, '<'))
943 return -1; /* DVIPS/pdfTeX format */
944
945 p = mline;
946 endptr = p + strlen(mline);
947
948 skip_blank(&p, endptr);
949
950 while (p < endptr) {
951 /* Break if '-' preceeded by blanks is found. (DVIPDFM format) */
952 if (*p == '-') return 1;
953 for (n++; p < endptr && !ISBLANK(*p); p++);
954 skip_blank(&p, endptr);
955 }
956
957 /* Two entries: TFM_NAME PS_NAME only (DVIPS format)
958 * Otherwise (DVIPDFM format) */
959 return (n == 2 ? 0 : 1);
960 }
961
962 int
pdf_load_fontmap_file(const char * filename,int mode)963 pdf_load_fontmap_file (const char *filename, int mode)
964 {
965 fontmap_rec *mrec;
966 FILE *fp;
967 const char *p = NULL, *endptr;
968 long llen, lpos = 0;
969 int error = 0, format = 0;
970
971 ASSERT(filename);
972 ASSERT(fontmap) ;
973
974 if (verbose)
975 MESG("<FONTMAP:");
976 fp = DPXFOPEN(filename, DPX_RES_TYPE_FONTMAP); /* outputs path if verbose */
977 if (!fp) {
978 WARN("Couldn't open font map file \"%s\".", filename);
979 return -1;
980 }
981
982 while (!error &&
983 (p = readline(work_buffer, WORK_BUFFER_SIZE, fp)) != NULL) {
984 int m;
985
986 lpos++;
987 llen = strlen(work_buffer);
988 endptr = p + llen;
989
990 skip_blank(&p, endptr);
991 if (p == endptr)
992 continue;
993
994 m = is_pdfm_mapline(p);
995
996 if (format * m < 0) { /* mismatch */
997 WARN("Found a mismatched fontmap line %d from %s.", lpos, filename);
998 WARN("-- Ignore the current input buffer: %s", p);
999 continue;
1000 } else
1001 format += m;
1002
1003 mrec = NEW(1, fontmap_rec);
1004 pdf_init_fontmap_record(mrec);
1005
1006 /* format > 0: DVIPDFM, format <= 0: DVIPS/pdfTeX */
1007 error = pdf_read_fontmap_line(mrec, p, llen, format);
1008 if (error) {
1009 WARN("Invalid map record in fontmap line %d from %s.", lpos, filename);
1010 WARN("-- Ignore the current input buffer: %s", p);
1011 pdf_clear_fontmap_record(mrec);
1012 RELEASE(mrec);
1013 continue;
1014 } else {
1015 switch (mode) {
1016 case FONTMAP_RMODE_REPLACE:
1017 pdf_insert_fontmap_record(mrec->map_name, mrec);
1018 break;
1019 case FONTMAP_RMODE_APPEND:
1020 pdf_append_fontmap_record(mrec->map_name, mrec);
1021 break;
1022 case FONTMAP_RMODE_REMOVE:
1023 pdf_remove_fontmap_record(mrec->map_name);
1024 break;
1025 }
1026 }
1027 pdf_clear_fontmap_record(mrec);
1028 RELEASE(mrec);
1029 }
1030 DPXFCLOSE(fp);
1031
1032 if (verbose)
1033 MESG(">");
1034
1035 return error;
1036 }
1037
1038 fontmap_rec *
pdf_insert_native_fontmap_record(const char * path,uint32_t index,int layout_dir,int extend,int slant,int embolden)1039 pdf_insert_native_fontmap_record (const char *path, uint32_t index,
1040 int layout_dir, int extend, int slant, int embolden)
1041 {
1042 char *fontmap_key;
1043 fontmap_rec *mrec;
1044 fontmap_rec *ret;
1045
1046 ASSERT(path);
1047
1048 fontmap_key = malloc(strlen(path) + 40); // CHECK
1049 sprintf(fontmap_key, "%s/%d/%c/%d/%d/%d", path, index, layout_dir == 0 ? 'H' : 'V', extend, slant, embolden);
1050
1051 if (verbose)
1052 MESG("<NATIVE-FONTMAP:%s", fontmap_key);
1053
1054 mrec = NEW(1, fontmap_rec);
1055 pdf_init_fontmap_record(mrec);
1056
1057 mrec->map_name = fontmap_key;
1058 mrec->enc_name = mstrdup(layout_dir == 0 ? "Identity-H" : "Identity-V");
1059 mrec->font_name = mstrdup(path);
1060 mrec->opt.index = index;
1061 if (layout_dir != 0)
1062 mrec->opt.flags |= FONTMAP_OPT_VERT;
1063
1064 fill_in_defaults(mrec, fontmap_key);
1065
1066 mrec->opt.extend = extend / 65536.0;
1067 mrec->opt.slant = slant / 65536.0;
1068 mrec->opt.bold = embolden / 65536.0;
1069
1070 ret = pdf_insert_fontmap_record(mrec->map_name, mrec);
1071 pdf_clear_fontmap_record(mrec);
1072 RELEASE(mrec);
1073
1074 if (verbose)
1075 MESG(">");
1076
1077 return ret;
1078 }
1079
1080 #if 0
1081 /* tfm_name="dmjhira10", map_name="dmj@DNP@10", sfd_name="DNP"
1082 * --> sub_id="hira"
1083 * Test if tfm_name can be really considered as subfont.
1084 */
1085 static int
1086 test_subfont (const char *tfm_name, const char *map_name, const char *sfd_name)
1087 {
1088 int r = 0;
1089 char **ids;
1090 int n, m;
1091 char *p = (char *) map_name;
1092 char *q = (char *) tfm_name;
1093
1094 ASSERT( tfm_name && map_name && sfd_name );
1095
1096 /* until first occurence of '@' */
1097 for ( ; *p && *q && *p == *q && *p != '@'; p++, q++);
1098 if (*p != '@')
1099 return 0;
1100 p++;
1101 /* compare sfd_name (should be always true here) */
1102 if (strlen(p) <= strlen(sfd_name) ||
1103 memcmp(p, sfd_name, strlen(sfd_name)) ||
1104 p[strlen(sfd_name)] != '@')
1105 return 0;
1106 /* check tfm_name follows second '@' */
1107 p += strlen(sfd_name) + 1;
1108 if (*p) {
1109 char *r = (char *) tfm_name;
1110 r += strlen(tfm_name) - strlen(p);
1111 if (strcmp(r, p))
1112 return 0;
1113 }
1114 /* Now 'p' is located at next to SFD name terminator
1115 * (second '@') in map_name and 'q' is at first char
1116 * of subfont_id substring in tfm_name.
1117 */
1118 n = strlen(q) - strlen(p); /* length of subfont_id string */
1119 if (n <= 0)
1120 return 0;
1121 /* check if n-length substring 'q' is valid as subfont ID */
1122 ids = sfd_get_subfont_ids(sfd_name, &m);
1123 if (!ids)
1124 return 0;
1125 while (!r && m-- > 0) {
1126 if (strlen(ids[m]) == n &&
1127 !memcmp(q, ids[m], n)) {
1128 r = 1;
1129 }
1130 }
1131
1132 return r;
1133 }
1134 #endif /* 0 */
1135
1136
1137 fontmap_rec *
pdf_lookup_fontmap_record(const char * tfm_name)1138 pdf_lookup_fontmap_record (const char *tfm_name)
1139 {
1140 fontmap_rec *mrec = NULL;
1141
1142 if (fontmap && tfm_name)
1143 mrec = ht_lookup_table(fontmap, tfm_name, strlen(tfm_name));
1144
1145 return mrec;
1146 }
1147
1148
1149 void
pdf_init_fontmaps(void)1150 pdf_init_fontmaps (void)
1151 {
1152 fontmap = NEW(1, struct ht_table);
1153 ht_init_table(fontmap, hval_free);
1154 }
1155
1156 void
pdf_close_fontmaps(void)1157 pdf_close_fontmaps (void)
1158 {
1159 if (fontmap) {
1160 ht_clear_table(fontmap);
1161 RELEASE(fontmap);
1162 }
1163 fontmap = NULL;
1164
1165 release_sfd_record();
1166 }
1167
1168 #if 0
1169 void
1170 pdf_clear_fontmaps (void)
1171 {
1172 pdf_close_fontmaps();
1173 pdf_init_fontmaps();
1174 }
1175 #endif
1176
1177 /* CIDFont options
1178 *
1179 * FORMAT:
1180 *
1181 * (:int:)?!?string(/string)?(,string)?
1182 */
1183
1184 static char *
substr(const char ** str,char stop)1185 substr (const char **str, char stop)
1186 {
1187 char *sstr;
1188 const char *endptr;
1189
1190 endptr = strchr(*str, stop);
1191 if (!endptr || endptr == *str)
1192 return NULL;
1193 sstr = NEW(endptr-(*str)+1, char);
1194 memcpy(sstr, *str, endptr-(*str));
1195 sstr[endptr-(*str)] = '\0';
1196
1197 *str = endptr+1;
1198 return sstr;
1199 }
1200
1201 #include <ctype.h>
1202 #define CID_MAPREC_CSI_DELIM '/'
1203
1204 static char *
strip_options(const char * map_name,fontmap_opt * opt)1205 strip_options (const char *map_name, fontmap_opt *opt)
1206 {
1207 char *font_name;
1208 const char *p;
1209 char *next = NULL;
1210 int have_csi = 0, have_style = 0;
1211
1212 ASSERT(opt);
1213
1214 p = map_name;
1215 font_name = NULL;
1216 opt->charcoll = NULL;
1217 opt->index = 0;
1218 opt->style = FONTMAP_STYLE_NONE;
1219 opt->flags = 0;
1220
1221 if (*p == ':' && isdigit((unsigned char)*(p+1))) {
1222 opt->index = (int) strtoul(p+1, &next, 10);
1223 if (*next == ':')
1224 p = next + 1;
1225 else {
1226 opt->index = 0;
1227 }
1228 }
1229 if (*p == '!') { /* no-embedding */
1230 if (*(++p) == '\0')
1231 ERROR("Invalid map record: %s (--> %s)", map_name, p);
1232 opt->flags |= FONTMAP_OPT_NOEMBED;
1233 }
1234
1235 if ((next = strchr(p, CID_MAPREC_CSI_DELIM)) != NULL) {
1236 if (next == p)
1237 ERROR("Invalid map record: %s (--> %s)", map_name, p);
1238 font_name = substr(&p, CID_MAPREC_CSI_DELIM);
1239 have_csi = 1;
1240 } else if ((next = strchr(p, ',')) != NULL) {
1241 if (next == p)
1242 ERROR("Invalid map record: %s (--> %s)", map_name, p);
1243 font_name = substr(&p, ',');
1244 have_style = 1;
1245 } else {
1246 font_name = NEW(strlen(p)+1, char);
1247 strcpy(font_name, p);
1248 }
1249
1250 if (have_csi) {
1251 if ((next = strchr(p, ',')) != NULL) {
1252 opt->charcoll = substr(&p, ',');
1253 have_style = 1;
1254 } else if (p[0] == '\0') {
1255 ERROR("Invalid map record: %s.", map_name);
1256 } else {
1257 opt->charcoll = NEW(strlen(p)+1, char);
1258 strcpy(opt->charcoll, p);
1259 }
1260 }
1261
1262 if (have_style) {
1263 if (!strncmp(p, "BoldItalic", 10)) {
1264 if (*(p+10))
1265 ERROR("Invalid map record: %s (--> %s)", map_name, p);
1266 opt->style = FONTMAP_STYLE_BOLDITALIC;
1267 } else if (!strncmp(p, "Bold", 4)) {
1268 if (*(p+4))
1269 ERROR("Invalid map record: %s (--> %s)", map_name, p);
1270 opt->style = FONTMAP_STYLE_BOLD;
1271 } else if (!strncmp(p, "Italic", 6)) {
1272 if (*(p+6))
1273 ERROR("Invalid map record: %s (--> %s)", map_name, p);
1274 opt->style = FONTMAP_STYLE_ITALIC;
1275 }
1276 }
1277
1278 return font_name;
1279 }
1280
1281 #if DPXTEST
1282 static void
dump_fontmap_rec(const char * key,const fontmap_rec * mrec)1283 dump_fontmap_rec (const char *key, const fontmap_rec *mrec)
1284 {
1285 fontmap_opt *opt = (fontmap_opt *) &mrec->opt;
1286
1287 if (mrec->map_name)
1288 fprintf(stdout, " <!-- subfont");
1289 else
1290 fprintf(stdout, " <insert");
1291 fprintf(stdout, " id=\"%s\"", key);
1292 if (mrec->map_name)
1293 fprintf(stdout, " map-name=\"%s\"", mrec->map_name);
1294 if (mrec->enc_name)
1295 fprintf(stdout, " enc-name=\"%s\"", mrec->enc_name);
1296 if (mrec->font_name)
1297 fprintf(stdout, " font-name=\"%s\"", mrec->font_name);
1298 if (mrec->charmap.sfd_name && mrec->charmap.subfont_id) {
1299 fprintf(stdout, " charmap=\"sfd:%s,%s\"",
1300 mrec->charmap.sfd_name, mrec->charmap.subfont_id);
1301 }
1302 if (opt->slant != 0.0)
1303 fprintf(stdout, " font-slant=\"%g\"", opt->slant);
1304 if (opt->extend != 1.0)
1305 fprintf(stdout, " font-extend=\"%g\"", opt->extend);
1306 if (opt->charcoll)
1307 fprintf(stdout, " glyph-order=\"%s\"", opt->charcoll);
1308 if (opt->tounicode)
1309 fprintf(stdout, " tounicode=\"%s\"", opt->tounicode);
1310 if (opt->index != 0)
1311 fprintf(stdout, " ttc-index=\"%d\"", opt->index);
1312 if (opt->flags & FONTMAP_OPT_NOEMBED)
1313 fprintf(stdout, " embedding=\"no\"");
1314 if (opt->mapc >= 0) {
1315 fprintf(stdout, " charmap=\"pad:");
1316 if (opt->mapc > 0xffff)
1317 fprintf(stdout, "%02x %02x", (opt->mapc >> 16) & 0xff, (opt->mapc >> 8) & 0xff);
1318 else
1319 fprintf(stdout, "%02x", (opt->mapc >> 8) & 0xff);
1320 fprintf(stdout, "\"");
1321 }
1322 if (opt->flags & FONTMAP_OPT_VERT)
1323 fprintf(stdout, " writing-mode=\"vertical\"");
1324 if (opt->style != FONTMAP_STYLE_NONE) {
1325 fprintf(stdout, " font-style=\"");
1326 switch (opt->style) {
1327 case FONTMAP_STYLE_BOLD:
1328 fprintf(stdout, "bold");
1329 break;
1330 case FONTMAP_STYLE_ITALIC:
1331 fprintf(stdout, "italic");
1332 break;
1333 case FONTMAP_STYLE_BOLDITALIC:
1334 fprintf(stdout, "bolditalic");
1335 break;
1336 }
1337 fprintf(stdout, "\"");
1338 }
1339 if (mrec->map_name)
1340 fprintf(stdout, " / -->\n");
1341 else
1342 fprintf(stdout, " />\n");
1343 }
1344
1345 void
dump_fontmaps(void)1346 dump_fontmaps (void)
1347 {
1348 struct ht_iter iter;
1349 fontmap_rec *mrec;
1350 char key[128], *kp;
1351 int kl;
1352
1353 if (!fontmap)
1354 return;
1355
1356 fprintf(stdout, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1357 fprintf(stdout, "<!DOCTYPE fontmap SYSTEM \"fontmap.dtd\">\n");
1358 fprintf(stdout, "<fontmap id=\"%s\">\n", "foo");
1359 if (ht_set_iter(fontmap, &iter) == 0) {
1360 do {
1361 kp = ht_iter_getkey(&iter, &kl);
1362 mrec = ht_iter_getval(&iter);
1363 if (kl > 127)
1364 continue;
1365 memcpy(key, kp, kl); key[kl] = 0;
1366 dump_fontmap_rec(key, mrec);
1367 } while (!ht_iter_next(&iter));
1368 }
1369 ht_clear_iter(&iter);
1370 fprintf(stdout, "</fontmap>\n");
1371
1372 return;
1373 }
1374
1375 void
test_fontmap_help(void)1376 test_fontmap_help (void)
1377 {
1378 fprintf(stdout, "usage: fontmap [options] [mapfile...]\n");
1379 fprintf(stdout, "-l, --lookup string\n");
1380 fprintf(stdout, " Lookup fontmap entry for 'string' after loading mapfile(s).\n");
1381 }
1382
1383 int
test_fontmap_main(int argc,char * argv[])1384 test_fontmap_main (int argc, char *argv[])
1385 {
1386 int i;
1387 char *key = NULL;
1388
1389 for (;;) {
1390 int c, optidx = 0;
1391 static struct option long_options[] = {
1392 {"lookup", 1, 0, 'l'},
1393 {"help", 0, 0, 'h'},
1394 {0, 0, 0, 0}
1395 };
1396 c = getopt_long(argc, argv, "l:h", long_options, &optidx);
1397 if (c == -1)
1398 break;
1399
1400 switch (c) {
1401 case 'l':
1402 key = optarg;
1403 break;
1404 case 'h':
1405 test_fontmap_help();
1406 return 0;
1407 break;
1408 default:
1409 test_fontmap_help();
1410 return -1;
1411 break;
1412 }
1413 }
1414
1415 pdf_init_fontmaps();
1416 for (i = optind; i < argc; i++)
1417 pdf_load_fontmap_file(argv[i], FONTMAP_RMODE_REPLACE);
1418
1419 if (key == NULL)
1420 dump_fontmaps();
1421 else {
1422 fontmap_rec *mrec;
1423 mrec = pdf_lookup_fontmap_record(key);
1424 if (mrec)
1425 dump_fontmap_rec(key, mrec);
1426 else {
1427 WARN("Fontmap entry \"%s\" not found.", key);
1428 }
1429 }
1430 pdf_close_fontmaps();
1431
1432 return 0;
1433 }
1434 #endif /* DPXTEST */
1435