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