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