1 /* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
2 
3     Copyright (C) 2002-2014 by Jin-Hwan Cho and Shunsaku Hirata,
4     the dvipdfmx project team.
5 
6     This program 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     This program 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
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <errno.h>
26 
27 #include "system.h"
28 #include "mem.h"
29 #include "error.h"
30 #include "dpxfile.h"
31 
32 #include "subfont.h"
33 
34 static int verbose = 0;
35 void
subfont_set_verbose(void)36 subfont_set_verbose (void)
37 {
38   verbose++;
39 }
40 
41 /* Don't forget fontmap reading now requires information
42  * from SFD files. You must initialize at least sfd_file_
43  * cache before starting loading of fontmaps.
44  */
45 
46 /* Subfont Definition File:
47  *  struct sfd_file_ is for storing subfont identifiers
48  *  contained in a SFD file and for mapping string pair
49  *  <SFD_file, Subfont_id> to internal code mapping table
50  *  ID which is index within an array of struct sfd_rec_.
51  *  We store code mapping tables in different place than
52  *  struct sfd_file_.
53  */
54 struct sfd_file_
55 {
56   char  *ident;  /* SFD file name */
57   char **sub_id; /* Subfont IDs   */
58 
59   int   *rec_id; /* indices within struct sfd_rec_ array "sfd_record" */
60 
61   int    max_subfonts;
62   int    num_subfonts;
63 };
64 
65 /* Mapping table */
66 struct sfd_rec_
67 {
68   /* unsigned char  misbit[32]; */
69   unsigned short vector[256]; /* 0 for undefined */
70 };
71 
72 static void
init_sfd_file_(struct sfd_file_ * sfd)73 init_sfd_file_ (struct sfd_file_ *sfd)
74 {
75   sfd->ident  = NULL;
76   sfd->sub_id = NULL;
77   sfd->rec_id = NULL;
78   sfd->max_subfonts = sfd->num_subfonts = 0;
79 }
80 
81 static void
clean_sfd_file_(struct sfd_file_ * sfd)82 clean_sfd_file_ (struct sfd_file_ *sfd)
83 {
84   int  i;
85   if (sfd->ident)
86     RELEASE(sfd->ident);
87   if (sfd->sub_id) {
88     for (i = 0; i < sfd->num_subfonts; i++) {
89       if (sfd->sub_id[i])
90         RELEASE(sfd->sub_id[i]);
91     }
92     RELEASE(sfd->sub_id);
93   }
94   if (sfd->rec_id)
95     RELEASE(sfd->rec_id);
96   init_sfd_file_(sfd);
97 }
98 
99 static struct sfd_file_ *sfd_files = NULL;
100 static int num_sfd_files = 0, max_sfd_files = 0;
101 
102 static struct sfd_rec_ *sfd_record = NULL;
103 static int num_sfd_records = 0, max_sfd_records = 0;
104 
105 
106 
107 /* Another buffer...
108  * We want buffer size at least 7 x 256 + a
109  * 4096 is usually enough.
110  */
111 #define LINE_BUF_SIZE 4096
112 static char line_buf[LINE_BUF_SIZE];
113 
114 /* Each lines describes character code mapping for each
115  * subfonts. '#' is start of comment.
116  * SFD file format uses a '\' before newline sequence
117  * for line-continuation.
118  */
119 static char *
readline(char * buf,int buf_len,FILE * fp)120 readline (char *buf, int buf_len, FILE *fp)
121 {
122   char  *r, *q, *p = buf;
123   int    n = 0, c = 0;
124 
125   while (buf_len - n > 0 && (q = mfgets(p, buf_len - n, fp))) {
126     c++;
127     r = strchr(q, '#');
128     /* Comment is converted to single wsp (followed by a newline). */
129     if (r) {
130       *r = ' ';
131       *(r + 1) = '\0';
132     }
133     if (strlen(q) == 0)
134       break; /* empty line */
135     n += strlen(q);
136     q += strlen(q) - 1;
137     if (*q != '\\')
138       break;
139     else { /* line continued */
140       n -= 1;
141       p  = buf + n;
142     }
143   }
144   if (n >= buf_len - 1) {
145     WARN("Possible buffer overflow in reading SFD file (buffer full, size=%d bytes)",
146          buf_len - 1);
147   }
148 
149   return  (c > 0 ? buf : NULL);
150 }
151 
152 #define clear_vector(v) if ((v)) { \
153   int __i; \
154   for (__i = 0; __i < 256; __i++) \
155     (v)[__i] = 0; \
156 }
157 
158 /* subfont_id ( integer ':' | integer '_' integer | integer )*
159  *
160  *  0x32: ==> Subsequent integers are place into slots starting at 0x32.
161  *    0x32: 0xA1A1 0xA1A2 ... ==> 0x32 is mappned to 0xA1A1, 0x33 to 0xA1A2
162  *  0xA1A1_0xA1A5 ==> Expanded to 0xA1A1 0xA1A2 ... 0xA1A5
163  */
164 
165 /* subfont_id is already consumed here. */
166 static int
read_sfd_record(struct sfd_rec_ * rec,const char * lbuf)167 read_sfd_record (struct sfd_rec_ *rec, const char *lbuf)
168 {
169   const char *p = lbuf, *q;
170   char  *r;
171   int    repos  = 0;
172   long   c,  v1 = 0, v2 = 0;
173   int    curpos = 0;
174   int    error  = 0;
175 
176 #define IS_TOKSEP(c) ((c) == '\0' || isspace((unsigned char)(c)))
177   for ( ; *p && isspace((unsigned char)*p); p++);
178   while (!error && *p) {
179     repos = 0; q = p;
180     v1    = strtol(p, &r, 0);
181     q = r;
182     if (q == p ||
183         (!IS_TOKSEP(*q) && *q != ':' && *q != '_')) {
184       WARN("Unknown token in subfont mapping table: %c", *q);
185       return  -1;
186     }
187 
188     switch (*q) {
189     case  ':':
190       if (v1 < 0 || v1 > 0xff) {
191         WARN("Invalud value for subfont table offset: %ld", v1);
192         return  -1;
193       }
194       repos = 1;
195       q++;
196       break;
197     case  '_':
198       p  = q + 1;
199       v2 = strtol(p, &r, 0);
200       q = r;
201       if (v1 < 0 || v1 > 0xffffL ||
202           v2 < 0 || v2 > 0xffffL) {
203         WARN("Invalid value in subfont mapping table: 0x%x_0x%x", v1, v2);
204         return -1;
205       } else if (q == p || !IS_TOKSEP(*q)) {
206         WARN("Invalid char in subfont mapping table: %c", *q);
207         return  -1;
208       }
209       break;
210     default:
211       if (v1 < 0 || v1 > 0xffffL) {
212         WARN("Invalid character code in subfont mapping table: 0x%x", v1);
213         return -1;
214       }
215       v2 = v1;
216       break;
217     }
218 
219     if (repos)
220       curpos = v1;
221     else {
222       if (v2 < v1 || curpos + (v2 - v1) > 0xff) {
223         WARN("Invalid range in subfont mapping: curpos=\"0x%02x\" range=\"0x%04x,0x%04x\"",
224              curpos, v1, v2);
225         return  -1;
226       }
227       for (c = v1; c <= v2; c++) {
228         if (rec->vector[curpos] != 0) {
229           WARN("Subfont mapping for slot=\"0x%02x\" already defined...", curpos);
230           return  -1;
231         }
232         ASSERT( curpos >= 0 && curpos <= 255 );
233         rec->vector[curpos++] = (unsigned short) c;
234       }
235     }
236     for (p = q; *p && isspace((unsigned char)*p); p++);
237   }
238 
239   return  error;
240 }
241 
242 /* Scan for subfont IDs */
243 static int
scan_sfd_file(struct sfd_file_ * sfd,FILE * fp)244 scan_sfd_file (struct sfd_file_ *sfd, FILE *fp)
245 {
246   char  *id;
247   char  *q, *p;
248   int    n, lpos = 0;
249 
250   ASSERT( sfd && fp );
251 
252   if (verbose > 3) {
253     MESG("\nsubfont>> Scanning SFD file \"%s\"...\n", sfd->ident);
254   }
255 
256   rewind(fp);
257   sfd->max_subfonts = sfd->num_subfonts = 0;
258   while ((p = readline(line_buf, LINE_BUF_SIZE, fp)) != NULL) {
259     lpos++;
260     for ( ; *p && isspace((unsigned char)*p); p++);
261     if (*p == 0)
262       continue; /* empty */
263 
264     /* Saw non-wsp here */
265     for (n = 0, q = p; *p && !isspace((unsigned char)*p); p++, n++);
266     id = NEW(n + 1, char);
267     memcpy(id, q, n); id[n] = '\0';
268     if (sfd->num_subfonts >= sfd->max_subfonts) {
269       sfd->max_subfonts += 16;
270       sfd->sub_id = RENEW(sfd->sub_id, sfd->max_subfonts, char *);
271     }
272 
273     if (verbose > 3) {
274       MESG("subfont>>   id=\"%s\" at line=\"%d\"\n", id, lpos);
275     }
276     sfd->sub_id[sfd->num_subfonts] = id;
277     sfd->num_subfonts++;
278   }
279 
280   sfd->rec_id = NEW(sfd->num_subfonts, int);
281   for (n = 0; n < sfd->num_subfonts; n++) {
282     sfd->rec_id[n] = -1; /* Not loaded yet. We do lazy loading of map definitions. */
283   }
284 
285   if (verbose > 3) {
286     MESG("subfont>> %d entries found in SFD file \"%s\".\n", sfd->num_subfonts, sfd->ident);
287   }
288 
289   return  0;
290 }
291 
292 
293 /* Open SFD file and gather subfont IDs. We do not read mapping tables
294  * here but only read subfont IDs used in SFD file.
295  */
296 static int
find_sfd_file(const char * sfd_name)297 find_sfd_file (const char *sfd_name)
298 {
299   int    id = -1;
300   int    i, error = -1;
301 
302   /* Check if we already opened SFD file */
303   for (i = 0; i < num_sfd_files; i++) {
304     if (!strcmp(sfd_files[i].ident, sfd_name)) {
305       id = i;
306       break;
307     }
308   }
309 
310   if (id < 0) {
311     struct sfd_file_ *sfd = NULL;
312     FILE  *fp;
313 
314     if (num_sfd_files >= max_sfd_files) {
315       max_sfd_files += 8;
316       sfd_files = RENEW(sfd_files, max_sfd_files, struct sfd_file_);
317     }
318     sfd = &sfd_files[num_sfd_files];
319     init_sfd_file_(sfd);
320     sfd->ident = NEW(strlen(sfd_name) + 1, char);
321     strcpy(sfd->ident, sfd_name);
322     fp = DPXFOPEN(sfd->ident, DPX_RES_TYPE_SFD);
323     if (!fp) {
324       clean_sfd_file_(sfd);
325       return  -1;
326     }
327     error = scan_sfd_file(sfd, fp);
328     DPXFCLOSE(fp);
329     if (!error)
330       id = num_sfd_files++;
331     else {
332       WARN("Error occured while reading SFD file \"%s\"", sfd_name);
333       clean_sfd_file_(sfd);
334       id = -1;
335     }
336   }
337 
338   return  id;
339 }
340 
341 char **
sfd_get_subfont_ids(const char * sfd_name,int * num_ids)342 sfd_get_subfont_ids (const char *sfd_name, int *num_ids)
343 {
344   int  sfd_id;
345 
346   if (!sfd_name)
347     return  NULL;
348 
349   sfd_id = find_sfd_file(sfd_name);
350   if (sfd_id < 0)
351     return  NULL;
352 
353   if (num_ids)
354     *num_ids = sfd_files[sfd_id].num_subfonts;
355   return  sfd_files[sfd_id].sub_id;
356 }
357 
358 /* Make sure that sfd_name does not have the extension '.sfd'.
359  * Mapping tables are actually read here.
360  */
361 int
sfd_load_record(const char * sfd_name,const char * subfont_id)362 sfd_load_record (const char *sfd_name, const char *subfont_id)
363 {
364   int               rec_id = -1;
365   struct sfd_file_ *sfd;
366   FILE             *fp;
367   int               sfd_id, i, error = 0;
368   char             *p, *q;
369 
370   if (!sfd_name || !subfont_id)
371     return  -1;
372 
373   sfd_id = find_sfd_file(sfd_name);
374   if (sfd_id < 0)
375     return  -1;
376 
377   sfd = &sfd_files[sfd_id];
378   /* Check if we already loaded mapping table. */
379   for (i = 0;
380        i < sfd->num_subfonts && strcmp(sfd->sub_id[i], subfont_id); i++);
381   if (i == sfd->num_subfonts) {
382     WARN("Subfont id=\"%s\" not exist in SFD file \"%s\"...",
383          subfont_id, sfd->ident);
384     return  -1;
385   } else if (sfd->rec_id[i] >= 0) {
386     return  sfd->rec_id[i];
387   }
388 
389   if (verbose > 3) {
390     MESG("\nsubfont>> Loading SFD mapping table for <%s,%s>...",
391          sfd->ident, subfont_id);
392   }
393 
394   /* reopen */
395   fp = DPXFOPEN(sfd->ident, DPX_RES_TYPE_SFD);
396   if (!fp) {
397     return  -1;
398     /* ERROR("Could not open SFD file \"%s\"", sfd_name); */
399   }
400 
401   /* Seek to record for 'sub_name'. */
402   while ((p = readline(line_buf, LINE_BUF_SIZE, fp))) {
403     for ( ; *p && isspace((unsigned char)*p); p++);
404     if (*p == 0)
405       continue; /* empty line */
406 
407     /* q = parse_ident(&p, p + strlen(p)); */
408     for (q = p; *p && !isspace((unsigned char)*p); p++);
409     *p = '\0'; p++;
410     if (!strcmp(q, subfont_id)) {
411       if (num_sfd_records >= max_sfd_records) {
412         max_sfd_records += 16;
413         sfd_record = RENEW(sfd_record, max_sfd_records, struct sfd_rec_);
414       }
415       clear_vector(sfd_record[num_sfd_records].vector);
416       error = read_sfd_record(&sfd_record[num_sfd_records], p);
417       if (error)
418         WARN("Error occured while reading SFD file: file=\"%s\" subfont_id=\"%s\"",
419              sfd->ident, subfont_id);
420       else {
421         rec_id = num_sfd_records++;
422       }
423     }
424   }
425   if (rec_id < 0) {
426     WARN("Failed to load subfont mapping table for SFD=\"%s\" subfont_id=\"%s\"",
427          sfd->ident, subfont_id);
428   }
429   sfd->rec_id[i] = rec_id;
430   DPXFCLOSE(fp);
431 
432   if (verbose > 3) {
433     int __i;
434     if (rec_id >= 0) {
435       MESG(" at id=\"%d\"", rec_id);
436       MESG("\nsubfont>> Content of mapping table:");
437       for (__i = 0; __i < 256; __i++) {
438         if (__i % 16 == 0)
439           MESG("\nsubfont>>  ");
440         MESG(" %04x", sfd_record[rec_id].vector[__i]);
441       }
442     }
443     MESG("\n");
444   }
445 
446   return  rec_id;
447 }
448 
449 
450 /* Lookup mapping table */
451 unsigned short
lookup_sfd_record(int rec_id,unsigned char c)452 lookup_sfd_record (int rec_id, unsigned char c)
453 {
454   if (!sfd_record ||
455        rec_id < 0 || rec_id >= num_sfd_records)
456     ERROR("Invalid subfont_id: %d", rec_id);
457   return sfd_record[rec_id].vector[c];
458 }
459 
460 void
release_sfd_record(void)461 release_sfd_record (void)
462 {
463   int  i;
464 
465   if (sfd_record) {
466     RELEASE(sfd_record);
467   }
468   if (sfd_files) {
469     for (i = 0; i < num_sfd_files; i++) {
470       clean_sfd_file_(&sfd_files[i]);
471     }
472     RELEASE(sfd_files);
473   }
474   sfd_record = NULL;
475   sfd_files  = NULL;
476   num_sfd_records = max_sfd_records = 0;
477   num_sfd_files = max_sfd_files = 0;
478 }
479 
480 
481 #if  DPXTEST
482 /* SFD file dumper */
483 #ifdef HAVE_ICONV
484 #include <iconv.h>
485 #else
486 typedef int iconv_t;
487 #endif
488 #include <string.h>
489 
490 static void
dump_table(const char * sfd_name,const char * sub_name,iconv_t cd)491 dump_table (const char *sfd_name, const char *sub_name, iconv_t cd)
492 {
493   int  rec_id, i;
494 
495   rec_id = sfd_load_record(sfd_name, sub_name);
496   if (rec_id < 0) {
497     WARN("Could not load SFD mapping for \"%s\"", sub_name);
498     return;
499   }
500   fprintf(stdout, "  <subfont id=\"%s\">\n", sub_name);
501   for (i = 0; i < 256; i++) {
502     unsigned short c = lookup_sfd_record(rec_id, i);
503     char    *p, inbuf[2];
504     char    *q, outbuf[32];
505     size_t   r, inbufleft = 2, outbufleft = 32;
506 
507     if (c == 0)
508       fprintf(stdout, "    <!-- %02x: undefined -->", i);
509     else {
510       fprintf(stdout, "    <a bi=\"%02x\" bo=\"%02x %02x\"", i, (c >> 8) & 0xff, c & 0xff);
511       if (cd != (iconv_t) -1) {
512         p = inbuf; q = outbuf;
513         inbuf[0] = (c >> 8) & 0xff;
514         inbuf[1] = c & 0xff;
515 #ifdef HAVE_ICONV
516         r = iconv(cd, &p, &inbufleft, &q, &outbufleft);
517         if (r == -1) {
518           if (verbose) {
519             WARN("Conversion to Unicode failed for subfont-id=\"%s\" code=\"0x%02x\"",
520                  sub_name, i);
521             WARN(">> with: %s", strerror(errno));
522           }
523         } else {
524           outbuf[32-outbufleft] = 0;
525           fprintf(stdout, " uc=\"%s\"", outbuf);
526         }
527 #endif /* HAVE_ICONV */
528       }
529       fprintf(stdout, " />");
530     }
531     fprintf(stdout, "\n");
532   }
533   fprintf(stdout, "  </subfont>\n");
534   return;
535 }
536 
537 #define subfontDefinition_DTD "\
538 <!ELEMENT subfontDefinition (subfont+)>\n\
539 <!ATTLIST subfontDefinition\n\
540   id CDATA #REQUIRED\n\
541   output-encoding CDATA #IMPLIED\n\
542 >\n\
543 <!ELEMENT subfont (a*)>\n\
544 <!ATTLIST subfont\n\
545   id CDATA #REQUIRED\n\
546 >\n\
547 <!ELEMENT a EMPTY>\n\
548 <!ATTLIST a\n\
549   bi NMTOKENS #REQUIRED\n\
550   bo NMTOKENS #REQUIRED\n\
551   uc CDATA #IMPLIED\n\
552 >\
553 "
554 
555 void
test_subfont_help(void)556 test_subfont_help (void)
557 {
558   fprintf(stdout, "usage: subfont [options] SFD_name\n");
559   fprintf(stdout, "-e, --encoding string\n");
560   fprintf(stdout, "  Target (output) encoding of SFD mapping is 'string'.\n");
561   fprintf(stdout, "  It must be an encoding name recognized by iconv.\n");
562   fprintf(stdout, "  With this option write Unicode character in auxiliary attribute 'uc'.\n");
563   fprintf(stdout, "-s, --subfont-id string\n");
564   fprintf(stdout, "  Load and dump mapping table only for subfont 'string'.\n");
565 }
566 
567 int
test_subfont_main(int argc,char * argv[])568 test_subfont_main (int argc, char *argv[])
569 {
570   char   *sfd_name = NULL, *sub_name = NULL;
571   char   *from = NULL;
572   int     i;
573   iconv_t cd = (iconv_t) -1;
574 
575   for (;;) {
576     int  c, optidx = 0;
577     static struct option long_options[] = {
578       {"encoding",   1, 0, 'e'}, /* for to-Unicode conversion */
579       {"subfont-id", 1, 0, 's'},
580       {"help",       0, 0, 'h'},
581       {0, 0, 0, 0}
582     };
583     c = getopt_long(argc, argv, "e:s:h", long_options, &optidx);
584     if (c == -1)
585       break;
586 
587     switch (c) {
588     case  'e':
589       from = optarg;
590       break;
591     case  's':
592       sub_name = optarg;
593       break;
594     case  'h':
595       test_subfont_help();
596       return  0;
597       break;
598     default:
599       test_subfont_help();
600       return  -1;
601       break;
602     }
603   }
604   if (optind < argc) {
605     sfd_name = argv[optind++];
606   }
607 
608   if (sfd_name == NULL) {
609     WARN("No SFD file name specified.");
610     test_subfont_help();
611     return  -1;
612   }
613   if (!from)
614     cd = (iconv_t) -1;
615   else {
616 #ifdef HAVE_ICONV
617     cd = iconv_open("utf-8", from);
618     if (cd == (iconv_t) -1) {
619       WARN("Can't open iconv conversion descriptor for %s --> utf-8", from);
620       return  -1;
621     }
622 #else
623     WARN("Your system doesn't have iconv() in libc!");
624 #endif
625   }
626 
627   fprintf(stdout, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
628   fprintf(stdout, "<!DOCTYPE subfontDefinition [\n%s\n]>\n",
629           subfontDefinition_DTD);
630   fprintf(stdout, "<subfontDefinition id=\"%s\" output-encoding=\"%s\">\n",
631           sfd_name, from ? from : "unknown");
632   if (sub_name == NULL || !strcmp(sub_name, "all")) {
633     char **sub_id;
634     int    num_ids = 0;
635     sub_id = sfd_get_subfont_ids(sfd_name, &num_ids);
636     if (!sub_id)
637       WARN("Could not open SFD file: %s", sfd_name);
638     else {
639       for (i = 0; i < num_ids; i++)
640         dump_table(sfd_name, sub_id[i], cd);
641     }
642   } else {
643     dump_table(sfd_name, sub_name, cd);
644   }
645   fprintf(stdout, "</subfontDefinition>\n");
646 
647 #ifdef HAVE_ICONV
648   if (cd != (iconv_t) -1)
649     iconv_close(cd);
650 #endif
651 
652   return  0;
653 }
654 
655 #endif  /* DPXTEST */
656