@ @c /* mapfile.c Copyright 1996-2006 Han The Thanh Copyright 2006-2010 Taco Hoekwater This file is part of LuaTeX. LuaTeX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License along with LuaTeX; if not, see . */ static const char _svn_version[] = "$Id: mapfile.w 3786 2010-08-02 15:25:12Z taco $ " "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.70.1/source/texk/web2c/luatexdir/font/mapfile.w $"; #include #include "ptexlib.h" #include #include #include #define FM_BUF_SIZE 1024 static FILE *fm_file; static unsigned char *fm_buffer = NULL; static int fm_size = 0; static int fm_curbyte = 0; #define fm_open(a) (fm_file = fopen((char *)(a), FOPEN_RBIN_MODE)) #define fm_read_file() readbinfile(fm_file,&fm_buffer,&fm_size) #define fm_close() xfclose(fm_file, cur_file_name) #define fm_getchar() fm_buffer[fm_curbyte++] #define fm_eof() (fm_curbyte>fm_size) #define is_cfg_comment(c) \ (c == 10 || c == '*' || c == '#' || c == ';' || c == '%') typedef enum { FM_DUPIGNORE, FM_REPLACE, FM_DELETE } updatemode; typedef struct mitem { updatemode mode; /* FM_DUPIGNORE or FM_REPLACE or FM_DELETE */ maptype type; /* map file or map line */ char *line; /* pointer to map file name or map line */ int lineno; /* line number in map file */ } mapitem; mapitem *mitem = NULL; #define read_field(r, q, buf) do { \ q = buf; \ while (*r != ' ' && *r != '<' && *r != '"' && *r != '\0') \ *q++ = *r++; \ *q = '\0'; \ skip (r, ' '); \ } while (0) #define set_field(F) do { \ if (q > buf) \ fm->F = xstrdup(buf); \ if (*r == '\0') \ goto done; \ } while (0) fm_entry *new_fm_entry(void) { fm_entry *fm; fm = xtalloc(1, fm_entry); fm->tfm_name = NULL; fm->sfd_name = NULL; fm->ps_name = NULL; fm->fd_flags = FD_FLAGS_NOT_SET_IN_MAPLINE; fm->ff_name = NULL; fm->encname = NULL; fm->type = 0; fm->slant = 0; fm->extend = 1000; fm->pid = -1; fm->eid = -1; fm->subfont = NULL; unset_slantset(fm); unset_extendset(fm); unset_inuse(fm); return fm; } void delete_fm_entry(fm_entry * fm) { xfree(fm->tfm_name); xfree(fm->sfd_name); xfree(fm->ps_name); xfree(fm->ff_name); xfree(fm); } static ff_entry *new_ff_entry(void) { ff_entry *ff; ff = xtalloc(1, ff_entry); ff->ff_name = NULL; ff->ff_path = NULL; return ff; } static void delete_ff_entry(ff_entry * ff) { xfree(ff->ff_name); xfree(ff->ff_path); xfree(ff); } /**********************************************************************/ static struct avl_table *tfm_tree = NULL; static struct avl_table *ff_tree = NULL; static struct avl_table *encname_tree = NULL; /* AVL sort fm_entry into tfm_tree by tfm_name */ static int comp_fm_entry_tfm(const void *pa, const void *pb, void *p) { (void) p; return strcmp(((const fm_entry *) pa)->tfm_name, ((const fm_entry *) pb)->tfm_name); } /* AVL sort ff_entry into ff_tree by ff_name */ static int comp_ff_entry(const void *pa, const void *pb, void *p) { (void) p; return strcmp(((const ff_entry *) pa)->ff_name, ((const ff_entry *) pb)->ff_name); } static void create_avl_trees(void) { assert(tfm_tree == NULL); tfm_tree = avl_create(comp_fm_entry_tfm, NULL, &avl_xallocator); assert(tfm_tree != NULL); assert(ff_tree == NULL); ff_tree = avl_create(comp_ff_entry, NULL, &avl_xallocator); assert(ff_tree != NULL); assert(encname_tree == NULL); encname_tree = avl_create(comp_string_entry, NULL, &avl_xallocator); assert(encname_tree != NULL); } int avl_do_entry(fm_entry * fm, int mode) { fm_entry *p; void *a; void **aa; int delete_new = 0; if (tfm_tree == NULL) create_avl_trees(); p = (fm_entry *) avl_find(tfm_tree, fm); if (p != NULL) { switch (mode) { case FM_DUPIGNORE: pdftex_warn ("fontmap entry for `%s' already exists, duplicates ignored", fm->tfm_name); delete_new = 1; break; case FM_REPLACE: case FM_DELETE: if (is_inuse(p)) { pdftex_warn ("fontmap entry for `%s' has been used, replace/delete not allowed", fm->tfm_name); delete_new = 1; } else { a = avl_delete(tfm_tree, p); assert(a != NULL); delete_fm_entry(p); } break; default: assert(0); } } if ((mode == FM_DUPIGNORE || mode == FM_REPLACE) && delete_new == 0) { aa = avl_probe(tfm_tree, fm); assert(aa != NULL); } else delete_new = 1; return delete_new; } /* add the encoding name to an AVL tree. this has nothing to do with writeenc.c */ static char *add_encname(char *s) { char *p; void **aa; assert(s != NULL); assert(encname_tree != NULL); if ((p = (char *) avl_find(encname_tree, s)) == NULL) { /* encoding name not yet registered */ p = xstrdup(s); aa = avl_probe(encname_tree, p); assert(aa != NULL); } return p; } /**********************************************************************/ /* consistency check for map entry, with warn flag */ static int check_fm_entry(fm_entry * fm, boolean warn) { int a = 0; assert(fm != NULL); if (is_fontfile(fm) && !is_included(fm)) { if (warn) pdftex_warn ("ambiguous entry for `%s': font file present but not included, " "will be treated as font file not present", fm->tfm_name); xfree(fm->ff_name); /* do not set variable |a| as this entry will be still accepted */ } /* if both ps_name and font file are missing, drop this entry */ if (fm->ps_name == NULL && !is_fontfile(fm)) { if (warn) pdftex_warn ("invalid entry for `%s': both ps_name and font file missing", fm->tfm_name); a += 1; } /* TrueType fonts cannot be reencoded without subsetting */ if (is_truetype(fm) && is_reencoded(fm) && !is_subsetted(fm)) { if (warn) pdftex_warn ("invalid entry for `%s': only subsetted TrueType font can be reencoded", fm->tfm_name); a += 2; } /* the value of SlantFont and ExtendFont must be reasonable */ if (fm->slant < FONT_SLANT_MIN || fm->slant > FONT_SLANT_MAX) { if (warn) pdftex_warn ("invalid entry for `%s': too big value of SlantFont (%g)", fm->tfm_name, fm->slant / 1000.0); a += 8; } if (fm->extend < FONT_EXTEND_MIN || fm->extend > FONT_EXTEND_MAX) { if (warn) pdftex_warn ("invalid entry for `%s': too big value of ExtendFont (%g)", fm->tfm_name, fm->extend / 1000.0); a += 16; } /* subfonts must be used with subsetted non-reencoded TrueType fonts */ if (fm->pid != -1 && !(is_truetype(fm) && is_subsetted(fm) && !is_reencoded(fm))) { if (warn) pdftex_warn ("invalid entry for `%s': PidEid can be used only with subsetted non-reencoded TrueType fonts", fm->tfm_name); a += 32; } return a; } /**********************************************************************/ /* returns the font number if s is one of the 14 std. font names, -1 otherwise; speed-trimmed. */ int check_std_t1font(char *s) { static const char *std_t1font_names[] = { "Courier", /* 0:7 */ "Courier-Bold", /* 1:12 */ "Courier-Oblique", /* 2:15 */ "Courier-BoldOblique", /* 3:19 */ "Helvetica", /* 4:9 */ "Helvetica-Bold", /* 5:14 */ "Helvetica-Oblique", /* 6:17 */ "Helvetica-BoldOblique", /* 7:21 */ "Symbol", /* 8:6 */ "Times-Roman", /* 9:11 */ "Times-Bold", /* 10:10 */ "Times-Italic", /* 11:12 */ "Times-BoldItalic", /* 12:16 */ "ZapfDingbats" /* 13:12 */ }; static const int index[] = { -1, -1, -1, -1, -1, -1, 8, 0, -1, 4, 10, 9, -1, -1, 5, 2, 12, 6, -1, 3, -1, 7 }; size_t n; int k = -1; assert(s != NULL); n = strlen(s); if (n > 21) return -1; if (n == 12) { /* three names have length 12 */ switch (*s) { case 'C': k = 1; /* Courier-Bold */ break; case 'T': k = 11; /* Times-Italic */ break; case 'Z': k = 13; /* ZapfDingbats */ break; default: return -1; } } else k = index[n]; if (k > -1 && !strcmp(std_t1font_names[k], s)) return k; return -1; } /**********************************************************************/ static void fm_scan_line(void) { int a, b, c, j, u = 0, v = 0; char cc; float d; fm_entry *fm; char fm_line[FM_BUF_SIZE], buf[FM_BUF_SIZE]; char *p, *q, *r, *s; switch (mitem->type) { case MAPFILE: p = fm_line; while (!fm_eof()) { if (fm_curbyte == fm_size) { fm_curbyte++; cc = 10; } else { cc = (char) fm_getchar(); } append_char_to_buf(cc, p, fm_line, FM_BUF_SIZE); if (cc == 10) break; } *(--p) = '\0'; r = fm_line; break; case MAPLINE: r = mitem->line; /* work on string from makecstring() */ break; default: assert(0); } if (*r == '\0' || is_cfg_comment(*r)) return; fm = new_fm_entry(); read_field(r, q, buf); set_field(tfm_name); if (!isdigit(*r)) { /* 2nd field ps_name may not start with a digit */ read_field(r, q, buf); set_field(ps_name); } if (isdigit(*r)) { /* font descriptor /Flags given? */ for (s = r; isdigit(*s); s++); if (*s == ' ' || *s == '"' || *s == '<' || *s == '\0') { /* not e. g. 8r.enc */ fm->fd_flags = atoi(r); while (isdigit(*r)) r++; } } while (1) { /* loop through "specials", encoding, font file */ skip(r, ' '); switch (*r) { case '\0': goto done; case '"': /* opening quote */ r++; u = v = 0; do { skip(r, ' '); if (sscanf(r, "%f %n", &d, &j) > 0) { s = r + j; /* jump behind number, eat also blanks, if any */ if (*(s - 1) == 'E' || *(s - 1) == 'e') s--; /* e. g. 0.5ExtendFont: %f = 0.5E */ if (str_prefix(s, "SlantFont")) { d *= (float) 1000.0; /* correct rounding also for neg. numbers */ fm->slant = (int) (d > 0 ? d + 0.5 : d - 0.5); set_slantset(fm); r = s + strlen("SlantFont"); } else if (str_prefix(s, "ExtendFont")) { d *= (float) 1000.0; fm->extend = (int) (d > 0 ? d + 0.5 : d - 0.5); set_extendset(fm); r = s + strlen("ExtendFont"); } else { /* unknown name */ for (r = s; *r != ' ' && *r != '"' && *r != '\0'; r++); /* jump over name */ c = *r; /* remember char for temporary end of string */ *r = '\0'; pdftex_warn ("invalid entry for `%s': unknown name `%s' ignored", fm->tfm_name, s); *r = (char) c; } } else for (; *r != ' ' && *r != '"' && *r != '\0'; r++); } while (*r == ' '); if (*r == '"') /* closing quote */ r++; else { pdftex_warn ("invalid entry for `%s': closing quote missing", fm->tfm_name); goto bad_line; } break; case 'P': /* handle cases for subfonts like 'PidEid=3,1' */ if (sscanf(r, "PidEid=%i, %i %n", &a, &b, &c) >= 2) { fm->pid = (short) a; fm->eid = (short) b; r += c; break; } default: /* encoding or font file specification */ a = b = 0; if (*r == '<') { a = *r++; if (*r == '<' || *r == '[') b = *r++; } read_field(r, q, buf); /* encoding, formats: '8r.enc' or '<8r.enc' or '<[8r.enc' */ if (strlen(buf) > 4 && strcasecmp(strend(buf) - 4, ".enc") == 0) { fm->encname = add_encname(buf); u = v = 0; /* u, v used if intervening blank: "<< foo" */ } else if (strlen(buf) > 0) { /* file name given */ /* font file, formats: * subsetting: ' no subsetting */ } set_field(ff_name); u = v = 0; } else { u = a; v = b; } } } done: if (fm->ps_name != NULL && (check_std_t1font(fm->ps_name) >= 0)) set_std_t1font(fm); if (is_fontfile(fm) && strlen(fm_fontfile(fm)) > 3) { if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttf") == 0) set_truetype(fm); else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".ttc") == 0) set_truetype(fm); else if (strcasecmp(strend(fm_fontfile(fm)) - 4, ".otf") == 0) set_opentype(fm); else set_type1(fm); } else set_type1(fm); /* assume a builtin font is Type1 */ if (check_fm_entry(fm, true) != 0) goto bad_line; /* Until here the map line has been completely scanned without errors; fm points to a valid, freshly filled-out fm_entry structure. Now follows the actual work of registering/deleting. */ if (handle_subfont_fm(fm, mitem->mode)) /* is this a subfont? */ return; if (avl_do_entry(fm, mitem->mode) == 0) return; bad_line: delete_fm_entry(fm); } /**********************************************************************/ static void fm_read_info(void) { int callback_id; int file_opened = 0; if (tfm_tree == NULL) create_avl_trees(); if (mitem->line == NULL) /* nothing to do */ return; mitem->lineno = 1; switch (mitem->type) { case MAPFILE: xfree(fm_buffer); fm_curbyte = 0; fm_size = 0; cur_file_name = luatex_find_file(mitem->line, find_map_file_callback); if (cur_file_name) { callback_id = callback_defined(read_map_file_callback); if (callback_id > 0) { if (run_callback(callback_id, "S->bSd", cur_file_name, &file_opened, &fm_buffer, &fm_size)) { if (file_opened) { if (fm_size > 0) { if (tracefilenames) tex_printf("{%s", cur_file_name); while (!fm_eof()) { fm_scan_line(); mitem->lineno++; } if (tracefilenames) tex_printf("}"); fm_file = NULL; } } else { pdftex_warn("cannot open font map file (%s)", cur_file_name); } } else { pdftex_warn("cannot open font map file (%s)", cur_file_name); } } else { if (!fm_open(cur_file_name)) { pdftex_warn("cannot open font map file (%s)", cur_file_name); } else { fm_read_file(); tex_printf("{%s", cur_file_name); while (!fm_eof()) { fm_scan_line(); mitem->lineno++; } fm_close(); tex_printf("}"); fm_file = NULL; } } cur_file_name = NULL; } break; case MAPLINE: cur_file_name = NULL; /* makes pdftex_warn() shorter */ fm_scan_line(); break; default: assert(0); } mitem->line = NULL; /* done with this line */ cur_file_name = NULL; return; } /**********************************************************************/ fm_entry *getfontmap(char *tfm_name) { fm_entry *fm; fm_entry tmp; if (tfm_name == NULL) /* wide, lua loaded fonts may not have a name */ return NULL; if (tfm_tree == NULL) fm_read_info(); /* only to read default map file */ tmp.tfm_name = tfm_name; /* Look up for tfmname */ fm = (fm_entry *) avl_find(tfm_tree, &tmp); if (fm == NULL) return NULL; set_inuse(fm); return fm; } /**********************************************************************/ /* * Process map file given by its name or map line contents. Items not * beginning with [+-=] flush default map file, if it has not yet been * read. Leading blanks and blanks immediately following [+-=] are * ignored. */ void process_map_item(char *s, int type) { char *p; int mode; if (*s == ' ') s++; /* ignore leading blank */ switch (*s) { case '+': /* +mapfile.map, +mapline */ mode = FM_DUPIGNORE; /* insert entry, if it is not duplicate */ s++; break; case '=': /* =mapfile.map, =mapline */ mode = FM_REPLACE; /* try to replace earlier entry */ s++; break; case '-': /* -mapfile.map, -mapline */ mode = FM_DELETE; /* try to delete entry */ s++; break; default: mode = FM_DUPIGNORE; /* like +, but also: */ mitem->line = NULL; /* flush default map file name */ } if (*s == ' ') s++; /* ignore blank after [+-=] */ p = s; /* map item starts here */ switch (type) { case MAPFILE: /* remove blank at end */ while (*p != '\0' && *p != ' ') p++; *p = '\0'; break; case MAPLINE: /* blank at end allowed */ break; default: assert(0); } if (mitem->line != NULL) /* read default map file first */ fm_read_info(); if (*s != '\0') { /* only if real item to process */ mitem->mode = mode; mitem->type = type; mitem->line = s; fm_read_info(); } } void pdfmapfile(int t) { char *s = tokenlist_to_cstring(t, true, NULL); process_map_item(s, MAPFILE); free(s); } void pdfmapline(int t) { char *s = tokenlist_to_cstring(t, true, NULL); process_map_item(s, MAPLINE); free(s); } void pdf_init_map_file(char *map_name) { assert(mitem == NULL); mitem = xtalloc(1, mapitem); mitem->mode = FM_DUPIGNORE; mitem->type = MAPFILE; mitem->line = map_name; } /**********************************************************************/ /* * Early check whether a font file exists. Search tree ff_tree is used * in 1st instance, as it may be faster than the kpse_find_file(), and * kpse_find_file() is called only once per font file name + expansion * parameter. This might help keeping speed, if many PDF pages with * same fonts are to be embedded. * * The ff_tree contains only font files, which are actually needed, * so this tree typically is much smaller than the tfm_tree. */ ff_entry *check_ff_exist(char *ff_name, boolean is_tt) { ff_entry *ff; ff_entry tmp; void **aa; int callback_id; char *filepath = NULL; assert(ff_name != NULL); tmp.ff_name = ff_name; ff = (ff_entry *) avl_find(ff_tree, &tmp); if (ff == NULL) { /* not yet in database */ ff = new_ff_entry(); ff->ff_name = xstrdup(ff_name); if (is_tt) { callback_id = callback_defined(find_truetype_file_callback); if (callback_id > 0) { run_callback(callback_id, "S->S", ff_name, &filepath); if (filepath && strlen(filepath) == 0) filepath = NULL; ff->ff_path = filepath; } else { ff->ff_path = kpse_find_file(ff_name, kpse_truetype_format, 0); } } else { callback_id = callback_defined(find_type1_file_callback); if (callback_id > 0) { run_callback(callback_id, "S->S", ff_name, &filepath); if (filepath && strlen(filepath) == 0) filepath = NULL; ff->ff_path = filepath; } else { ff->ff_path = kpse_find_file(ff_name, kpse_type1_format, 0); } } aa = avl_probe(ff_tree, ff); assert(aa != NULL); } return ff; } /**********************************************************************/ int is_subsetable(fm_entry * fm) { assert(is_included(fm)); return is_subsetted(fm); } /**********************************************************************/ /* cleaning up... */ static void destroy_fm_entry_tfm(void *pa, void *pb) { fm_entry *fm; (void) pb; fm = (fm_entry *) pa; delete_fm_entry(fm); } static void destroy_ff_entry(void *pa, void *pb) { ff_entry *ff; (void) pb; ff = (ff_entry *) pa; delete_ff_entry(ff); } void fm_free(void) { if (tfm_tree != NULL) { avl_destroy(tfm_tree, destroy_fm_entry_tfm); tfm_tree = NULL; } if (ff_tree != NULL) { avl_destroy(ff_tree, destroy_ff_entry); ff_tree = NULL; } }