beta-0.89.2
[luatex.git] / source / texk / web2c / luatexdir / font / mapfile.w
blob939468af4d89fb20f91957bdb755745afc3ad7a7
1 % mapfile.w
3 % Copyright 1996-2006 Han The Thanh <thanh@@pdftex.org>
4 % Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org>
6 % This file is part of LuaTeX.
8 % LuaTeX is free software; you can redistribute it and/or modify it under
9 % the terms of the GNU General Public License as published by the Free
10 % Software Foundation; either version 2 of the License, or (at your
11 % option) any later version.
13 % LuaTeX is distributed in the hope that it will be useful, but WITHOUT
14 % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 % FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 % License for more details.
18 % You should have received a copy of the GNU General Public License along
19 % with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
21 @ @c
24 #include "ptexlib.h"
25 #include <math.h>
26 #include <kpathsea/c-auto.h>
27 #include <kpathsea/c-memstr.h>
28 #include <string.h>
30 #define FM_BUF_SIZE 1024
32 static FILE *fm_file;
34 static unsigned char *fm_buffer = NULL;
35 static int fm_size = 0;
36 static int fm_curbyte = 0;
38 #define fm_open(a) (fm_file = fopen((char *)(a), FOPEN_RBIN_MODE))
39 #define fm_read_file() readbinfile(fm_file,&fm_buffer,&fm_size)
40 #define fm_close() xfclose(fm_file, cur_file_name)
41 #define fm_getchar() fm_buffer[fm_curbyte++]
42 #define fm_eof() (fm_curbyte>fm_size)
43 #define is_cfg_comment(c) \
44 (c == 10 || c == '*' || c == '#' || c == ';' || c == '%')
46 typedef enum { FM_DUPIGNORE, FM_REPLACE, FM_DELETE } updatemode;
48 typedef struct mitem {
49 updatemode mode; /* FM_DUPIGNORE or FM_REPLACE or FM_DELETE */
50 maptype type; /* map file or map line */
51 char *line; /* pointer to map file name or map line */
52 int lineno; /* line number in map file */
53 } mapitem;
54 mapitem *mitem = NULL;
56 #define read_field(r, q, buf) do { \
57 q = buf; \
58 while (*r != ' ' && *r != '<' && *r != '"' && *r != '\0') \
59 *q++ = *r++; \
60 *q = '\0'; \
61 skip (r, ' '); \
62 } while (0)
64 #define set_field(F) do { \
65 if (q > buf) \
66 fm->F = xstrdup(buf); \
67 if (*r == '\0') \
68 goto done; \
69 } while (0)
71 fm_entry *new_fm_entry(void)
73 fm_entry *fm;
74 fm = xtalloc(1, fm_entry);
75 fm->tfm_name = NULL;
76 fm->sfd_name = NULL;
77 fm->ps_name = NULL;
78 fm->fd_flags = FD_FLAGS_NOT_SET_IN_MAPLINE;
79 fm->ff_name = NULL;
80 fm->encname = NULL;
81 fm->type = 0;
82 fm->slant = 0;
83 fm->extend = 1000;
84 fm->pid = -1;
85 fm->eid = -1;
86 fm->subfont = NULL;
87 unset_slantset(fm);
88 unset_extendset(fm);
89 unset_inuse(fm);
90 return fm;
93 void delete_fm_entry(fm_entry * fm)
95 xfree(fm->tfm_name);
96 xfree(fm->sfd_name);
97 xfree(fm->ps_name);
98 xfree(fm->ff_name);
99 xfree(fm);
102 static ff_entry *new_ff_entry(void)
104 ff_entry *ff;
105 ff = xtalloc(1, ff_entry);
106 ff->ff_name = NULL;
107 ff->ff_path = NULL;
108 return ff;
111 static void delete_ff_entry(ff_entry * ff)
113 xfree(ff->ff_name);
114 xfree(ff->ff_path);
115 xfree(ff);
118 /**********************************************************************/
120 static struct avl_table *tfm_tree = NULL;
121 static struct avl_table *ff_tree = NULL;
122 static struct avl_table *encname_tree = NULL;
124 /* AVL sort fm_entry into tfm_tree by tfm_name */
126 static int comp_fm_entry_tfm(const void *pa, const void *pb, void *p)
128 (void) p;
129 return strcmp(((const fm_entry *) pa)->tfm_name,
130 ((const fm_entry *) pb)->tfm_name);
133 /* AVL sort ff_entry into ff_tree by ff_name */
135 static int comp_ff_entry(const void *pa, const void *pb, void *p)
137 (void) p;
138 return strcmp(((const ff_entry *) pa)->ff_name,
139 ((const ff_entry *) pb)->ff_name);
142 static void create_avl_trees(void)
144 assert(tfm_tree == NULL);
145 tfm_tree = avl_create(comp_fm_entry_tfm, NULL, &avl_xallocator);
146 assert(tfm_tree != NULL);
147 assert(ff_tree == NULL);
148 ff_tree = avl_create(comp_ff_entry, NULL, &avl_xallocator);
149 assert(ff_tree != NULL);
150 assert(encname_tree == NULL);
151 encname_tree = avl_create(comp_string_entry, NULL, &avl_xallocator);
152 assert(encname_tree != NULL);
155 int avl_do_entry(fm_entry * fm, int mode)
157 fm_entry *p;
158 void *a;
159 void **aa;
160 int delete_new = 0;
161 if (tfm_tree == NULL)
162 create_avl_trees();
163 p = (fm_entry *) avl_find(tfm_tree, fm);
164 if (p != NULL) {
165 switch (mode) {
166 case FM_DUPIGNORE:
167 formatted_warning("map file", "entry for '%s' already exists, duplicates ignored", fm->tfm_name);
168 delete_new = 1;
169 break;
170 case FM_REPLACE:
171 case FM_DELETE:
172 if (is_inuse(p)) {
173 formatted_warning("map file", "entry for '%s' has been used, replace/delete not allowed", fm->tfm_name);
174 delete_new = 1;
175 } else {
176 a = avl_delete(tfm_tree, p);
177 assert(a != NULL);
178 delete_fm_entry(p);
180 break;
181 default:
182 assert(0);
185 if ((mode == FM_DUPIGNORE || mode == FM_REPLACE) && delete_new == 0) {
186 aa = avl_probe(tfm_tree, fm);
187 assert(aa != NULL);
188 } else
189 delete_new = 1;
190 return delete_new;
193 /* add the encoding name to an AVL tree. this has nothing to do with writeenc.c */
195 static char *add_encname(char *s)
197 char *p;
198 void **aa;
199 assert(s != NULL);
200 assert(encname_tree != NULL);
201 if ((p = (char *) avl_find(encname_tree, s)) == NULL) { /* encoding name not yet registered */
202 p = xstrdup(s);
203 aa = avl_probe(encname_tree, p);
204 assert(aa != NULL);
206 return p;
209 /**********************************************************************/
210 /* consistency check for map entry, with warn flag */
212 static int check_fm_entry(fm_entry * fm, boolean warn)
214 int a = 0;
215 assert(fm != NULL);
217 if (is_fontfile(fm) && !is_included(fm)) {
218 if (warn)
219 formatted_warning("map file",
220 "ambiguous entry for '%s': font file present but not included, "
221 "will be treated as font file not present", fm->tfm_name);
222 xfree(fm->ff_name);
223 /* do not set variable |a| as this entry will be still accepted */
226 /* if both ps_name and font file are missing, drop this entry */
227 if (fm->ps_name == NULL && !is_fontfile(fm)) {
228 if (warn)
229 formatted_warning("map file", "invalid entry for '%s': both ps_name and font file missing", fm->tfm_name);
230 a += 1;
233 /* TrueType fonts cannot be reencoded without subsetting */
234 if (is_truetype(fm) && is_reencoded(fm) && !is_subsetted(fm)) {
235 if (warn)
236 formatted_warning("map file", "invalid entry for '%s': only subsetted TrueType font can be reencoded", fm->tfm_name);
237 a += 2;
240 /* the value of SlantFont and ExtendFont must be reasonable */
241 if (fm->slant < FONT_SLANT_MIN || fm->slant > FONT_SLANT_MAX) {
242 if (warn)
243 formatted_warning("map file", "invalid entry for '%s': value '%g' is to large for SlantFont",
244 fm->tfm_name, fm->slant / 1000.0);
245 a += 8;
247 if (fm->extend < FONT_EXTEND_MIN || fm->extend > FONT_EXTEND_MAX) {
248 if (warn)
249 formatted_warning("map file", "invalid entry for '%s': value '%g' is too large for ExtendFont",
250 fm->tfm_name, fm->extend / 1000.0);
251 a += 16;
254 /* subfonts must be used with subsetted non-reencoded TrueType fonts */
255 if (fm->pid != -1 &&
256 !(is_truetype(fm) && is_subsetted(fm) && !is_reencoded(fm))) {
257 if (warn)
258 formatted_warning("map file", "invalid entry for '%s': PidEid can be used only with subsetted non-reencoded TrueType fonts",
259 fm->tfm_name);
260 a += 32;
263 return a;
266 /**********************************************************************/
267 /* returns the font number if s is one of the 14 std. font names, -1 otherwise; speed-trimmed. */
269 int check_std_t1font(char *s)
271 static const char *std_t1font_names[] = {
272 "Courier", /* 0:7 */
273 "Courier-Bold", /* 1:12 */
274 "Courier-Oblique", /* 2:15 */
275 "Courier-BoldOblique", /* 3:19 */
276 "Helvetica", /* 4:9 */
277 "Helvetica-Bold", /* 5:14 */
278 "Helvetica-Oblique", /* 6:17 */
279 "Helvetica-BoldOblique", /* 7:21 */
280 "Symbol", /* 8:6 */
281 "Times-Roman", /* 9:11 */
282 "Times-Bold", /* 10:10 */
283 "Times-Italic", /* 11:12 */
284 "Times-BoldItalic", /* 12:16 */
285 "ZapfDingbats" /* 13:12 */
287 static const int index[] =
288 { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6, -1,
289 3, -1, 7
291 size_t n;
292 int k = -1;
293 assert(s != NULL);
294 n = strlen(s);
295 if (n > 21)
296 return -1;
297 if (n == 12) { /* three names have length 12 */
298 switch (*s) {
299 case 'C':
300 k = 1; /* Courier-Bold */
301 break;
302 case 'T':
303 k = 11; /* Times-Italic */
304 break;
305 case 'Z':
306 k = 13; /* ZapfDingbats */
307 break;
308 default:
309 return -1;
311 } else
312 k = index[n];
313 if (k > -1 && !strcmp(std_t1font_names[k], s))
314 return k;
315 return -1;
318 /**********************************************************************/
320 static void fm_scan_line(void)
322 int a, b, c, j, u = 0, v = 0;
323 char cc;
324 float d;
325 fm_entry *fm;
326 char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE];
327 char *p, *q, *s;
328 char *r = NULL;
329 switch (mitem->type) {
330 case MAPFILE:
331 p = fm_line;
332 while (!fm_eof()) {
333 if (fm_curbyte == fm_size) {
334 fm_curbyte++;
335 cc = 10;
336 } else {
337 cc = (char) fm_getchar();
339 append_char_to_buf(cc, p, fm_line, FM_BUF_SIZE);
340 if (cc == 10)
341 break;
343 *(--p) = '\0';
344 r = fm_line;
345 break;
346 case MAPLINE:
347 r = mitem->line; /* work on string from makecstring() */
348 break;
349 default:
350 assert(0);
352 if (*r == '\0' || is_cfg_comment(*r))
353 return;
354 fm = new_fm_entry();
355 read_field(r, q, buf);
356 set_field(tfm_name);
357 if (!isdigit((unsigned char)*r)) { /* 2nd field ps_name may not start with a digit */
358 read_field(r, q, buf);
359 set_field(ps_name);
361 if (isdigit((unsigned char)*r)) { /* font descriptor /Flags given? */
362 for (s = r; isdigit((unsigned char)*s); s++);
363 if (*s == ' ' || *s == '"' || *s == '<' || *s == '\0') { /* not e. g. 8r.enc */
364 fm->fd_flags = atoi(r);
365 while (isdigit((unsigned char)*r))
366 r++;
369 while (1) { /* loop through "specials", encoding, font file */
370 skip(r, ' ');
371 switch (*r) {
372 case '\0':
373 goto done;
374 case '"': /* opening quote */
375 r++;
376 u = v = 0;
377 do {
378 skip(r, ' ');
379 if (sscanf(r, "%f %n", &d, &j) > 0) {
380 s = r + j; /* jump behind number, eat also blanks, if any */
381 if (*(s - 1) == 'E' || *(s - 1) == 'e')
382 s--; /* e. g. 0.5ExtendFont: %f = 0.5E */
383 if (str_prefix(s, "SlantFont")) {
384 d *= (float) 1000.0; /* correct rounding also for neg. numbers */
385 fm->slant = (int) (d > 0 ? d + 0.5 : d - 0.5);
386 set_slantset(fm);
387 r = s + strlen("SlantFont");
388 } else if (str_prefix(s, "ExtendFont")) {
389 d *= (float) 1000.0;
390 fm->extend = (int) (d > 0 ? d + 0.5 : d - 0.5);
391 set_extendset(fm);
392 r = s + strlen("ExtendFont");
393 } else { /* unknown name */
394 for (r = s; *r != ' ' && *r != '"' && *r != '\0'; r++); /* jump over name */
395 c = *r; /* remember char for temporary end of string */
396 *r = '\0';
397 formatted_warning("map file", "invalid entry for '%s': unknown name '%s' ignored", fm->tfm_name, s);
398 *r = (char) c;
400 } else
401 for (; *r != ' ' && *r != '"' && *r != '\0'; r++);
403 while (*r == ' ');
404 if (*r == '"') /* closing quote */
405 r++;
406 else {
407 formatted_warning("map file", "invalid entry for '%s': closing quote missing", fm->tfm_name);
408 goto bad_line;
410 break;
411 case 'P': /* handle cases for subfonts like 'PidEid=3,1' */
412 if (sscanf(r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) {
413 fm->pid = (short) a;
414 fm->eid = (short) b;
415 r += c;
416 break;
418 default: /* encoding or font file specification */
419 a = b = 0;
420 if (*r == '<') {
421 a = *r++;
422 if (*r == '<' || *r == '[')
423 b = *r++;
425 read_field(r, q, buf);
426 /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */
427 if (strlen(buf) > 4 && strcasecmp(strend(buf) - 4, ".enc") == 0) {
428 fm->encname = add_encname(buf);
429 u = v = 0; /* u, v used if intervening blank: "<< foo" */
430 } else if (strlen(buf) > 0) { /* file name given */
431 /* font file, formats:
432 * subsetting: '<cmr10.pfa'
433 * no subsetting: '<<cmr10.pfa'
434 * no embedding: 'cmr10.pfa'
436 if (a == '<' || u == '<') {
437 set_included(fm);
438 if ((a == '<' && b == 0) || (a == 0 && v == 0))
439 set_subsetted(fm);
440 /* otherwise b == '<' (or '[') => no subsetting */
442 set_field(ff_name);
443 u = v = 0;
444 } else {
445 u = a;
446 v = b;
450 done:
451 if (fm->ps_name != NULL && (check_std_t1font(fm->ps_name) >= 0))
452 set_std_t1font(fm);
453 if (is_fontfile(fm) && strlen(fm_fontfile(fm)) > 3) {
454 if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttf") == 0)
455 set_truetype(fm);
456 else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttc") == 0)
457 set_truetype(fm);
458 else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".otf") == 0)
459 set_opentype(fm);
460 else
461 set_type1(fm);
462 } else
463 set_type1(fm); /* assume a builtin font is Type1 */
464 if (check_fm_entry(fm, true) != 0)
465 goto bad_line;
467 Until here the map line has been completely scanned without errors;
468 fm points to a valid, freshly filled-out fm_entry structure.
469 Now follows the actual work of registering/deleting.
471 if (handle_subfont_fm(fm, mitem->mode)) /* is this a subfont? */
472 return;
473 if (avl_do_entry(fm, mitem->mode) == 0)
474 return;
475 bad_line:
476 delete_fm_entry(fm);
479 /**********************************************************************/
481 static void fm_read_info(void)
483 int callback_id;
484 int file_opened = 0;
486 if (tfm_tree == NULL)
487 create_avl_trees();
488 if (mitem->line == NULL) /* nothing to do */
489 return;
490 mitem->lineno = 1;
491 switch (mitem->type) {
492 case MAPFILE:
493 xfree(fm_buffer);
494 fm_curbyte = 0;
495 fm_size = 0;
496 cur_file_name = luatex_find_file(mitem->line, find_map_file_callback);
497 if (cur_file_name) {
498 callback_id = callback_defined(read_map_file_callback);
499 if (callback_id > 0) {
500 if (run_callback(callback_id, "S->bSd", cur_file_name,
501 &file_opened, &fm_buffer, &fm_size)) {
502 if (file_opened) {
503 if (fm_size > 0) {
504 report_start_file(filetype_map,cur_file_name);
505 while (!fm_eof()) {
506 fm_scan_line();
507 mitem->lineno++;
509 report_stop_file(filetype_map);
510 fm_file = NULL;
512 } else {
513 formatted_warning("map file", "cannot open font map file '%s'", cur_file_name);
515 } else {
516 formatted_warning("map file", "cannot open font map file '%s'", cur_file_name);
518 } else {
519 if (!fm_open(cur_file_name)) {
520 formatted_warning("map file", "cannot open font map file '%s'", cur_file_name);
521 } else {
522 fm_read_file();
523 report_start_file(filetype_map,cur_file_name);
524 while (!fm_eof()) {
525 fm_scan_line();
526 mitem->lineno++;
528 fm_close();
529 report_stop_file(filetype_map);
530 fm_file = NULL;
533 cur_file_name = NULL;
535 break;
536 case MAPLINE:
537 cur_file_name = NULL;
538 fm_scan_line();
539 break;
540 default:
541 assert(0);
543 mitem->line = NULL; /* done with this line */
544 cur_file_name = NULL;
545 return;
548 /**********************************************************************/
550 fm_entry *getfontmap(char *tfm_name)
552 fm_entry *fm;
553 fm_entry tmp;
554 if (tfm_name == NULL) /* wide, lua loaded fonts may not have a name */
555 return NULL;
556 if (tfm_tree == NULL)
557 fm_read_info(); /* only to read default map file */
558 tmp.tfm_name = tfm_name; /* Look up for tfmname */
559 fm = (fm_entry *) avl_find(tfm_tree, &tmp);
560 if (fm == NULL)
561 return NULL;
562 set_inuse(fm);
563 return fm;
566 /**********************************************************************/
568 * Process map file given by its name or map line contents. Items not
569 * beginning with [+-=] flush default map file, if it has not yet been
570 * read. Leading blanks and blanks immediately following [+-=] are
571 * ignored.
574 void process_map_item(char *s, int type)
576 char *p;
577 int mode;
578 if (*s == ' ')
579 s++; /* ignore leading blank */
580 switch (*s) {
581 case '+': /* +mapfile.map, +mapline */
582 mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */
583 s++;
584 break;
585 case '=': /* =mapfile.map, =mapline */
586 mode = FM_REPLACE; /* try to replace earlier entry */
587 s++;
588 break;
589 case '-': /* -mapfile.map, -mapline */
590 mode = FM_DELETE; /* try to delete entry */
591 s++;
592 break;
593 default:
594 mode = FM_DUPIGNORE; /* like +, but also: */
595 mitem->line = NULL; /* flush default map file name */
597 if (*s == ' ')
598 s++; /* ignore blank after [+-=] */
599 p = s; /* map item starts here */
600 switch (type) {
601 case MAPFILE: /* remove blank at end */
602 while (*p != '\0' && *p != ' ')
603 p++;
604 *p = '\0';
605 break;
606 case MAPLINE: /* blank at end allowed */
607 break;
608 default:
609 assert(0);
611 if (mitem->line != NULL) /* read default map file first */
612 fm_read_info();
613 if (*s != '\0') { /* only if real item to process */
614 mitem->mode = mode;
615 mitem->type = type;
616 mitem->line = s;
617 fm_read_info();
621 void pdfmapfile(int t)
623 char *s = tokenlist_to_cstring(t, true, NULL);
624 process_map_item(s, MAPFILE);
625 free(s);
628 void pdfmapline(int t)
630 char *s = tokenlist_to_cstring(t, true, NULL);
631 process_map_item(s, MAPLINE);
632 free(s);
635 void pdf_init_map_file(char *map_name)
637 assert(mitem == NULL);
638 mitem = xtalloc(1, mapitem);
639 mitem->mode = FM_DUPIGNORE;
640 mitem->type = MAPFILE;
641 mitem->line = map_name;
644 /**********************************************************************/
646 * Early check whether a font file exists. Search tree ff_tree is used
647 * in 1st instance, as it may be faster than the kpse_find_file(), and
648 * kpse_find_file() is called only once per font file name + expansion
649 * parameter. This might help keeping speed, if many PDF pages with
650 * same fonts are to be embedded.
652 * The ff_tree contains only font files, which are actually needed,
653 * so this tree typically is much smaller than the tfm_tree.
656 ff_entry *check_ff_exist(char *ff_name, boolean is_tt)
658 ff_entry *ff;
659 ff_entry tmp;
660 void **aa;
661 int callback_id;
662 char *filepath = NULL;
664 assert(ff_name != NULL);
665 tmp.ff_name = ff_name;
666 ff = (ff_entry *) avl_find(ff_tree, &tmp);
667 if (ff == NULL) { /* not yet in database */
668 ff = new_ff_entry();
669 ff->ff_name = xstrdup(ff_name);
670 if (is_tt) {
671 callback_id = callback_defined(find_truetype_file_callback);
672 if (callback_id > 0) {
673 run_callback(callback_id, "S->S", ff_name, &filepath);
674 if (filepath && strlen(filepath) == 0)
675 filepath = NULL;
676 ff->ff_path = filepath;
677 } else {
678 ff->ff_path = kpse_find_file(ff_name, kpse_truetype_format, 0);
680 } else {
681 callback_id = callback_defined(find_type1_file_callback);
682 if (callback_id > 0) {
683 run_callback(callback_id, "S->S", ff_name, &filepath);
684 if (filepath && strlen(filepath) == 0)
685 filepath = NULL;
686 ff->ff_path = filepath;
687 } else {
688 ff->ff_path = kpse_find_file(ff_name, kpse_type1_format, 0);
691 aa = avl_probe(ff_tree, ff);
692 assert(aa != NULL);
694 return ff;
697 /**********************************************************************/
699 int is_subsetable(fm_entry * fm)
701 assert(is_included(fm));
702 return is_subsetted(fm);
705 /**********************************************************************/
706 /* cleaning up... */
708 static void destroy_fm_entry_tfm(void *pa, void *pb)
710 fm_entry *fm;
711 (void) pb;
712 fm = (fm_entry *) pa;
713 delete_fm_entry(fm);
716 static void destroy_ff_entry(void *pa, void *pb)
718 ff_entry *ff;
719 (void) pb;
720 ff = (ff_entry *) pa;
721 delete_ff_entry(ff);
724 void fm_free(void)
726 if (tfm_tree != NULL) {
727 avl_destroy(tfm_tree, destroy_fm_entry_tfm);
728 tfm_tree = NULL;
730 if (ff_tree != NULL) {
731 avl_destroy(ff_tree, destroy_ff_entry);
732 ff_tree = NULL;