/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. Copyright (C) 2002-2017 by Jin-Hwan Cho and Shunsaku Hirata, the dvipdfmx project team. Copyright (C) 1998, 1999 by Mark A. Wicks This program 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. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "dpx-fontmap.h" #include "tectonic_bridge_core.h" #include "dpx-dpxconf.h" #include "dpx-dpxfile.h" #include "dpx-dpxutil.h" #include "dpx-error.h" #include "dpx-mem.h" #include "dpx-mfileio.h" #include "dpx-subfont.h" /* CIDFont */ static char *strip_options (const char *map_name, fontmap_opt *opt); void pdf_init_fontmap_record (fontmap_rec *mrec) { assert(mrec); mrec->map_name = NULL; /* SFD char mapping */ mrec->charmap.sfd_name = NULL; mrec->charmap.subfont_id = NULL; /* for OFM */ mrec->opt.mapc = -1; /* compatibility */ mrec->font_name = NULL; mrec->enc_name = NULL; mrec->opt.slant = 0.0; mrec->opt.extend = 1.0; mrec->opt.bold = 0.0; mrec->opt.flags = 0; mrec->opt.design_size = -1.0; mrec->opt.tounicode = NULL; mrec->opt.otl_tags = NULL; mrec->opt.index = 0; mrec->opt.charcoll = NULL; mrec->opt.style = FONTMAP_STYLE_NONE; mrec->opt.stemv = -1; /* not given explicitly by an option */ mrec->opt.use_glyph_encoding = 0; } void pdf_clear_fontmap_record (fontmap_rec *mrec) { assert(mrec); free(mrec->map_name); free(mrec->charmap.sfd_name); free(mrec->charmap.subfont_id); free(mrec->enc_name); free(mrec->font_name); free(mrec->opt.tounicode); free(mrec->opt.otl_tags); free(mrec->opt.charcoll); pdf_init_fontmap_record(mrec); } /* strdup: just returns NULL for NULL */ static char * mstrdup (const char *s) { char *r; if (!s) return NULL; r = NEW(strlen(s) + 1, char); strcpy(r, s); return r; } static void pdf_copy_fontmap_record (fontmap_rec *dst, const fontmap_rec *src) { assert( dst && src ); dst->map_name = mstrdup(src->map_name); dst->charmap.sfd_name = mstrdup(src->charmap.sfd_name); dst->charmap.subfont_id = mstrdup(src->charmap.subfont_id); dst->font_name = mstrdup(src->font_name); dst->enc_name = mstrdup(src->enc_name); dst->opt.slant = src->opt.slant; dst->opt.extend = src->opt.extend; dst->opt.bold = src->opt.bold; dst->opt.flags = src->opt.flags; dst->opt.mapc = src->opt.mapc; dst->opt.tounicode = mstrdup(src->opt.tounicode); dst->opt.otl_tags = mstrdup(src->opt.otl_tags); dst->opt.index = src->opt.index; dst->opt.charcoll = mstrdup(src->opt.charcoll); dst->opt.style = src->opt.style; dst->opt.stemv = src->opt.stemv; dst->opt.use_glyph_encoding = src->opt.use_glyph_encoding; } static void hval_free (void *vp) { fontmap_rec *mrec = (fontmap_rec *) vp; pdf_clear_fontmap_record(mrec); free(mrec); } static void fill_in_defaults (fontmap_rec *mrec, const char *tex_name) { if (mrec->enc_name && (streq_ptr(mrec->enc_name, "default") || streq_ptr(mrec->enc_name, "none"))) { mrec->enc_name = mfree(mrec->enc_name); } if (mrec->font_name && (streq_ptr(mrec->font_name, "default") || streq_ptr(mrec->font_name, "none"))) { mrec->font_name = mfree(mrec->font_name); } /* We *must* fill font_name either explicitly or by default */ if (!mrec->font_name) { mrec->font_name = NEW(strlen(tex_name)+1, char); strcpy(mrec->font_name, tex_name); } mrec->map_name = NEW(strlen(tex_name)+1, char); strcpy(mrec->map_name, tex_name); #ifndef WITHOUT_COMPAT /* Use "UCS" character collection for Unicode SFD * and Identity CMap combination. For backward * compatibility. */ if (mrec->charmap.sfd_name && mrec->enc_name && !mrec->opt.charcoll) { if ((streq_ptr(mrec->enc_name, "Identity-H") || streq_ptr(mrec->enc_name, "Identity-V")) && (strstr(mrec->charmap.sfd_name, "Uni") || strstr(mrec->charmap.sfd_name, "UBig") || strstr(mrec->charmap.sfd_name, "UBg") || strstr(mrec->charmap.sfd_name, "UGB") || strstr(mrec->charmap.sfd_name, "UKS") || strstr(mrec->charmap.sfd_name, "UJIS"))) { mrec->opt.charcoll = NEW(strlen("UCS")+1, char); strcpy(mrec->opt.charcoll, "UCS"); } } #endif /* WITHOUT_COMPAT */ return; } static char * tt_readline (char *buf, int buf_len, rust_input_handle_t handle) { char *p, *q; assert(buf && buf_len > 0 && handle); p = tt_mfgets(buf, buf_len, handle); if (!p) return NULL; q = strchr(p, '%'); /* we don't have quoted string */ if (q) *q = '\0'; return p; } #ifndef ISBLANK # define ISBLANK(c) ((c) == ' ' || (c) == '\t') #endif static void skip_blank (const char **pp, const char *endptr) { const char *p = *pp; if (!p || p >= endptr) return; for ( ; p < endptr && ISBLANK(*p); p++); *pp = p; } static char * parse_string_value (const char **pp, const char *endptr) { char *q = NULL; const char *p = *pp; unsigned int n; if (!p || p >= endptr) return NULL; if (*p == '"') q = parse_c_string(&p, endptr); else { for (n = 0; p < endptr && !isspace((unsigned char)*p); p++, n++); if (n == 0) return NULL; q = NEW(n + 1, char); memcpy(q, *pp, n); q[n] = '\0'; } *pp = p; return q; } /* no preceeding spaces allowed */ static char * parse_integer_value (const char **pp, const char *endptr, int base) { char *q; const char *p = *pp; int has_sign = 0, has_prefix = 0, n; assert( base == 0 || (base >= 2 && base <= 36) ); if (!p || p >= endptr) return NULL; if (*p == '-' || *p == '+') { p++; has_sign = 1; } if ((base == 0 || base == 16) && p + 2 <= endptr && p[0] == '0' && p[1] == 'x') { p += 2; has_prefix = 1; } if (base == 0) { if (has_prefix) base = 16; else if (p < endptr && *p == '0') base = 8; else { base = 10; } } #define ISDIGIT_WB(c,b) ( \ ((b) <= 10 && (c) >= '0' && (c) < '0' + (b)) || \ ((b) > 10 && ( \ ((c) >= '0' && (c) <= '9') || \ ((c) >= 'a' && (c) < 'a' + ((b) - 10)) || \ ((c) >= 'A' && (c) < 'A' + ((b) - 10)) \ ) \ ) \ ) for (n = 0; p < endptr && ISDIGIT_WB(*p, base); p++, n++); if (n == 0) return NULL; if (has_sign) n += 1; if (has_prefix) n += 2; q = NEW(n + 1, char); memcpy(q, *pp, n); q[n] = '\0'; *pp = p; return q; } static int fontmap_parse_mapdef_dpm (fontmap_rec *mrec, const char *mapdef, const char *endptr) { const char *p = mapdef; /* * Parse record line in map file. First two fields (after TeX font * name) are position specific. Arguments start at the first token * beginning with a '-'. * * NOTE: * Dvipdfm basically uses parse_ident() for parsing enc_name, * font_name, and other string values which assumes PostScript-like * syntax. * skip_white() skips '\r' and '\n' but they should terminate * fontmap line here. */ skip_blank(&p, endptr); /* encoding field */ if (p < endptr && *p != '-') { /* May be NULL */ mrec->enc_name = parse_string_value(&p, endptr); skip_blank(&p, endptr); } /* fontname or font filename field */ if (p < endptr && *p != '-') { /* May be NULL */ mrec->font_name = parse_string_value(&p, endptr); skip_blank(&p, endptr); } if (mrec->font_name) { char *tmp; /* Several options are encoded in font_name for * compatibility with dvipdfm. */ tmp = strip_options(mrec->font_name, &mrec->opt); if (tmp) { free(mrec->font_name); mrec->font_name = tmp; } } skip_blank(&p, endptr); /* Parse any remaining arguments */ while (p + 1 < endptr && *p != '\r' && *p != '\n' && *p == '-') { char *q, mopt = p[1]; int v; p += 2; skip_blank(&p, endptr); switch (mopt) { case 's': /* Slant option */ q = parse_float_decimal(&p, endptr); if (!q) { dpx_warning("Missing a number value for 's' option."); return -1; } mrec->opt.slant = atof(q); free(q); break; case 'e': /* Extend option */ q = parse_float_decimal(&p, endptr); if (!q) { dpx_warning("Missing a number value for 'e' option."); return -1; } mrec->opt.extend = atof(q); if (mrec->opt.extend <= 0.0) { dpx_warning("Invalid value for 'e' option: %s", q); return -1; } free(q); break; case 'b': /* Fake-bold option */ q = parse_float_decimal(&p, endptr); if (!q) { dpx_warning("Missing a number value for 'b' option."); return -1; } mrec->opt.bold = atof(q); if (mrec->opt.bold <= 0.0) { dpx_warning("Invalid value for 'b' option: %s", q); return -1; } free(q); break; case 'r': /* Remap option; obsolete; just ignore */ break; case 'i': /* TTC index */ q = parse_integer_value(&p, endptr, 10); if (!q) { dpx_warning("Missing TTC index number..."); return -1; } mrec->opt.index = strtoul(q, NULL, 10); free(q); break; case 'p': /* UCS plane: just for testing */ q = parse_integer_value(&p, endptr, 0); if (!q) { dpx_warning("Missing a number for 'p' option."); return -1; } v = strtol(q, NULL, 0); if (v < 0 || v > 16) dpx_warning("Invalid value for option 'p': %s", q); else { mrec->opt.mapc = v << 16; } free(q); break; case 'u': /* ToUnicode */ q = parse_string_value(&p, endptr); if (q) mrec->opt.tounicode = q; else { dpx_warning("Missing string value for option 'u'."); return -1; } break; case 'v': /* StemV */ q = parse_integer_value(&p, endptr, 10); if (!q) { dpx_warning("Missing a number for 'v' option."); return -1; } mrec->opt.stemv = strtol(q, NULL, 0); free(q); break; /* 2017.4.15 back again */ case 'l': q = parse_string_value(&p, endptr); if (q) mrec->opt.otl_tags = q; else { dpx_warning("Missing string value for option 'l'."); return -1; } break; /* Omega uses both single-byte and double-byte set_char command * even for double-byte OFMs. This confuses CMap decoder. */ case 'm': /* Map single bytes char 0xab to double byte char 0xcdab */ if (p + 4 <= endptr && p[0] == '<' && p[3] == '>') { p++; q = parse_integer_value(&p, endptr, 16); if (!q) { dpx_warning("Invalid value for option 'm'."); return -1; } else if (p < endptr && *p != '>') { dpx_warning("Invalid value for option 'm': %s", q); free(q); return -1; } v = strtol(q, NULL, 16); mrec->opt.mapc = ((v << 8) & 0x0000ff00L); free(q); p++; } else if (p + 4 <= endptr && !memcmp(p, "sfd:", strlen("sfd:"))) { char *r; const char *rr; /* SFD mapping: sfd:Big5,00 */ p += 4; skip_blank(&p, endptr); q = parse_string_value(&p, endptr); if (!q) { dpx_warning("Missing value for option 'm'."); return -1; } r = strchr(q, ','); if (!r) { dpx_warning("Invalid value for option 'm': %s", q); free(q); return -1; } *r = 0; rr = ++r; skip_blank(&rr, r + strlen(r)); if (*rr == '\0') { dpx_warning("Invalid value for option 'm': %s,", q); free(q); return -1; } mrec->charmap.sfd_name = mstrdup(q); mrec->charmap.subfont_id = mstrdup(rr); free(q); } else if (p + 4 < endptr && !memcmp(p, "pad:", strlen("pad:"))) { p += 4; skip_blank(&p, endptr); q = parse_integer_value(&p, endptr, 16); if (!q) { dpx_warning("Invalid value for option 'm'."); return -1; } else if (p < endptr && !isspace((unsigned char)*p)) { dpx_warning("Invalid value for option 'm': %s", q); free(q); return -1; } v = strtol(q, NULL, 16); mrec->opt.mapc = ((v << 8) & 0x0000ff00L); free(q); } else { dpx_warning("Invalid value for option 'm'."); return -1; } break; case 'w': /* Writing mode (for unicode encoding) */ if (!mrec->enc_name || strcmp(mrec->enc_name, "unicode")) { dpx_warning("Fontmap option 'w' meaningless for encoding other than \"unicode\"."); return -1; } q = parse_integer_value(&p, endptr, 10); if (!q) { dpx_warning("Missing wmode value..."); return -1; } if (atoi(q) == 1) mrec->opt.flags |= FONTMAP_OPT_VERT; else if (atoi(q) == 0) mrec->opt.flags &= ~FONTMAP_OPT_VERT; else { dpx_warning("Invalid value for option 'w': %s", q); } free(q); break; default: dpx_warning("Unrecognized font map option: '%c'", mopt); return -1; } skip_blank(&p, endptr); } if (p < endptr && *p != '\r' && *p != '\n') { dpx_warning("Invalid char in fontmap line: %c", *p); return -1; } return 0; } /* Parse record line in map file of DVIPS/pdfTeX format. */ static int fontmap_parse_mapdef_dps (fontmap_rec *mrec, const char *mapdef, const char *endptr) { const char *p = mapdef; char *q; skip_blank(&p, endptr); /* The first field (after TFM name) must be PostScript name. */ /* However, pdftex.map allows a line without PostScript name. */ if (*p != '"' && *p != '<') { if (p < endptr) { q = parse_string_value(&p, endptr); free(q); skip_blank(&p, endptr); } else { dpx_warning("Missing a PostScript font name."); return -1; } } if (p >= endptr) return 0; /* Parse any remaining arguments */ while (p < endptr && *p != '\r' && *p != '\n' && (*p == '<' || *p == '"')) { switch (*p) { case '<': /* encoding or fontfile field */ /* If we see <[ or <<, just ignore the second char instead of doing as directed (define encoding file, fully embed); sorry. */ if (++p < endptr && (*p == '[' || *p == '<')) p++; /*skip */ skip_blank(&p, endptr); if ((q = parse_string_value(&p, endptr))) { int n = strlen(q); if (n > 4 && strstartswith(q + n - 4, ".enc")) mrec->enc_name = q; else mrec->font_name = q; } skip_blank(&p, endptr); break; case '"': /* Options */ if ((q = parse_string_value(&p, endptr))) { const char *r = q, *e = q+strlen(q); char *s, *t; skip_blank(&r, e); while (r < e) { if ((s = parse_float_decimal(&r, e))) { skip_blank(&r, e); if ((t = parse_string_value(&r, e))) { if (streq_ptr(t, "SlantFont")) mrec->opt.slant = atof(s); else if (streq_ptr(t, "ExtendFont")) mrec->opt.extend = atof(s); free(t); } free(s); } else if ((s = parse_string_value(&r, e))) { /* skip */ free(s); } skip_blank(&r, e); } free(q); } skip_blank(&p, endptr); break; default: dpx_warning("Found an invalid entry: %s", p); return -1; } skip_blank(&p, endptr); } if (p < endptr && *p != '\r' && *p != '\n') { dpx_warning("Invalid char in fontmap line: %c", *p); return -1; } return 0; } static struct ht_table *fontmap = NULL; #define fontmap_invalid(m) (!(m) || !(m)->map_name || !(m)->font_name) static char * chop_sfd_name (const char *tex_name, char **sfd_name) { char *fontname; char *p, *q; int m, n, len; *sfd_name = NULL; p = strchr(tex_name, '@'); if (!p || p[1] == '\0' || p == tex_name) { return NULL; } m = (int) (p - tex_name); p++; q = strchr(p, '@'); if (!q || q == p) { return NULL; } n = (int) (q - p); q++; len = strlen(tex_name) - n; fontname = NEW(len+1, char); memcpy(fontname, tex_name, m); fontname[m] = '\0'; if (*q) strcat(fontname, q); *sfd_name = NEW(n+1, char); memcpy(*sfd_name, p, n); (*sfd_name)[n] = '\0'; return fontname; } static char * make_subfont_name (const char *map_name, const char *sfd_name, const char *sub_id) { char *tfm_name; int n, m; char *p, *q; p = strchr(map_name, '@'); if (!p || p == map_name) return NULL; m = (int) (p - map_name); q = strchr(p + 1, '@'); if (!q || q == p + 1) return NULL; n = (int) (q - p) + 1; /* including two '@' */ if (strlen(sfd_name) != n - 2 || memcmp(p + 1, sfd_name, n - 2)) return NULL; tfm_name = NEW(strlen(map_name) - n + strlen(sub_id) + 1, char); memcpy(tfm_name, map_name, m); tfm_name[m] = '\0'; strcat(tfm_name, sub_id); if (q[1]) /* not ending with '@' */ strcat(tfm_name, q + 1); return tfm_name; } /* "foo@A@ ..." is expanded to * fooab ... -m sfd:A,ab * ... * fooyz ... -m sfd:A,yz * where 'ab' ... 'yz' is subfont IDs in SFD 'A'. */ int pdf_append_fontmap_record (const char *kp, const fontmap_rec *vp) { fontmap_rec *mrec; char *fnt_name, *sfd_name = NULL; if (!kp || fontmap_invalid(vp)) { dpx_warning("Invalid fontmap record..."); return -1; } if (dpx_conf.verbose_level > 3) dpx_message("fontmap>> append key=\"%s\"...", kp); fnt_name = chop_sfd_name(kp, &sfd_name); if (fnt_name && sfd_name) { char *tfm_name; char **subfont_ids; int n = 0; subfont_ids = sfd_get_subfont_ids(sfd_name, &n); if (!subfont_ids) return -1; while (n-- > 0) { tfm_name = make_subfont_name(kp, sfd_name, subfont_ids[n]); if (!tfm_name) continue; mrec = ht_lookup_table(fontmap, tfm_name, strlen(tfm_name)); if (!mrec) { mrec = NEW(1, fontmap_rec); pdf_init_fontmap_record(mrec); mrec->map_name = mstrdup(kp); /* link */ mrec->charmap.sfd_name = mstrdup(sfd_name); mrec->charmap.subfont_id = mstrdup(subfont_ids[n]); ht_insert_table(fontmap, tfm_name, strlen(tfm_name), mrec); } free(tfm_name); } free(fnt_name); free(sfd_name); } mrec = ht_lookup_table(fontmap, kp, strlen(kp)); if (!mrec) { mrec = NEW(1, fontmap_rec); pdf_copy_fontmap_record(mrec, vp); if (mrec->map_name && streq_ptr(kp, mrec->map_name)) { mrec->map_name = mfree(mrec->map_name); } ht_insert_table(fontmap, kp, strlen(kp), mrec); } if (dpx_conf.verbose_level > 3) dpx_message("\n"); return 0; } int pdf_remove_fontmap_record (const char *kp) { char *fnt_name, *sfd_name = NULL; if (!kp) return -1; if (dpx_conf.verbose_level > 3) dpx_message("fontmap>> remove key=\"%s\"...", kp); fnt_name = chop_sfd_name(kp, &sfd_name); if (fnt_name && sfd_name) { char *tfm_name; char **subfont_ids; int n = 0; subfont_ids = sfd_get_subfont_ids(sfd_name, &n); if (!subfont_ids) return -1; if (dpx_conf.verbose_level > 3) dpx_message("\nfontmap>> Expand @%s@:", sfd_name); while (n-- > 0) { tfm_name = make_subfont_name(kp, sfd_name, subfont_ids[n]); if (!tfm_name) continue; if (dpx_conf.verbose_level > 3) dpx_message(" %s", tfm_name); ht_remove_table(fontmap, tfm_name, strlen(tfm_name)); free(tfm_name); } free(fnt_name); free(sfd_name); } ht_remove_table(fontmap, kp, strlen(kp)); if (dpx_conf.verbose_level > 3) dpx_message("\n"); return 0; } fontmap_rec * pdf_insert_fontmap_record (const char *kp, const fontmap_rec *vp) { fontmap_rec *mrec; char *fnt_name, *sfd_name; if (!kp || fontmap_invalid(vp)) { dpx_warning("Invalid fontmap record..."); return NULL; } if (dpx_conf.verbose_level > 3) dpx_message("fontmap>> insert key=\"%s\"...", kp); fnt_name = chop_sfd_name(kp, &sfd_name); if (fnt_name && sfd_name) { char *tfm_name; char **subfont_ids; int n = 0; subfont_ids = sfd_get_subfont_ids(sfd_name, &n); if (!subfont_ids) { dpx_warning("Could not open SFD file: %s", sfd_name); free(fnt_name); free(sfd_name); return NULL; } if (dpx_conf.verbose_level > 3) dpx_message("\nfontmap>> Expand @%s@:", sfd_name); while (n-- > 0) { tfm_name = make_subfont_name(kp, sfd_name, subfont_ids[n]); if (!tfm_name) continue; if (dpx_conf.verbose_level > 3) dpx_message(" %s", tfm_name); mrec = NEW(1, fontmap_rec); pdf_init_fontmap_record(mrec); mrec->map_name = mstrdup(kp); /* link to this entry */ mrec->charmap.sfd_name = mstrdup(sfd_name); mrec->charmap.subfont_id = mstrdup(subfont_ids[n]); ht_insert_table(fontmap, tfm_name, strlen(tfm_name), mrec); free(tfm_name); } free(fnt_name); free(sfd_name); } mrec = NEW(1, fontmap_rec); pdf_copy_fontmap_record(mrec, vp); if (mrec->map_name && streq_ptr(kp, mrec->map_name)) { mrec->map_name = mfree(mrec->map_name); } ht_insert_table(fontmap, kp, strlen(kp), mrec); if (dpx_conf.verbose_level > 3) dpx_message("\n"); return mrec; } int pdf_read_fontmap_line (fontmap_rec *mrec, const char *mline, int mline_len, int format) { int error; char *q; const char *p, *endptr; assert(mrec); p = mline; endptr = p + mline_len; skip_blank(&p, endptr); if (p >= endptr) return -1; q = parse_string_value(&p, endptr); if (!q) return -1; if (format > 0) /* DVIPDFM format */ error = fontmap_parse_mapdef_dpm(mrec, p, endptr); else /* DVIPS/pdfTeX format */ error = fontmap_parse_mapdef_dps(mrec, p, endptr); if (!error) { char *fnt_name, *sfd_name = NULL; fnt_name = chop_sfd_name(q, &sfd_name); if (fnt_name && sfd_name) { if (!mrec->font_name) { /* In the case of subfonts, the base name (before the character '@') * will be used as a font_name by default. * Otherwise tex_name will be used as a font_name by default. */ mrec->font_name = fnt_name; } else { free(fnt_name); } free(mrec->charmap.sfd_name); mrec->charmap.sfd_name = sfd_name ; } fill_in_defaults(mrec, q); } free(q); return error; } /* DVIPS/pdfTeX fontmap line if one of the following three cases found: * * (1) any line including the character '"' * (2) any line including the character '<' * (3) if the line consists of two entries (tfmname and psname) * * DVIPDFM fontmap line otherwise. */ int is_pdfm_mapline (const char *mline) /* NULL terminated. */ { unsigned int n = 0; const char *p, *endptr; if (strchr(mline, '"') || strchr(mline, '<')) return -1; /* DVIPS/pdfTeX format */ p = mline; endptr = p + strlen(mline); skip_blank(&p, endptr); while (p < endptr) { /* Break if '-' preceeded by blanks is found. (DVIPDFM format) */ if (*p == '-') return 1; for (n++; p < endptr && !ISBLANK(*p); p++); skip_blank(&p, endptr); } /* Two entries: TFM_NAME PS_NAME only (DVIPS format) * Otherwise (DVIPDFM format) */ return (n == 2 ? 0 : 1); } int pdf_load_fontmap_file (const char *filename, int mode) { fontmap_rec *mrec; rust_input_handle_t handle; const char *p = NULL, *endptr; int llen, lpos = 0; int error = 0, format = 0; assert(filename); assert(fontmap); if (dpx_conf.verbose_level) dpx_message(" 0: DVIPDFM, format <= 0: DVIPS/pdfTeX */ error = pdf_read_fontmap_line(mrec, p, llen, format); if (error) { dpx_warning("Invalid map record in fontmap line %d from %s.", lpos, filename); dpx_warning("-- Ignore the current input buffer: %s", p); pdf_clear_fontmap_record(mrec); free(mrec); continue; } else { switch (mode) { case FONTMAP_RMODE_REPLACE: pdf_insert_fontmap_record(mrec->map_name, mrec); break; case FONTMAP_RMODE_APPEND: pdf_append_fontmap_record(mrec->map_name, mrec); break; case FONTMAP_RMODE_REMOVE: pdf_remove_fontmap_record(mrec->map_name); break; } } pdf_clear_fontmap_record(mrec); free(mrec); } ttstub_input_close(handle); if (dpx_conf.verbose_level) dpx_message(">"); return error; } fontmap_rec * pdf_insert_native_fontmap_record (const char *path, uint32_t index, int layout_dir, int extend, int slant, int embolden) { char *fontmap_key; fontmap_rec *mrec; fontmap_rec *ret; assert(path); fontmap_key = xmalloc(strlen(path) + 40); // CHECK sprintf(fontmap_key, "%s/%d/%c/%d/%d/%d", path, index, layout_dir == 0 ? 'H' : 'V', extend, slant, embolden); if (dpx_conf.verbose_level) dpx_message("map_name = fontmap_key; mrec->enc_name = mstrdup(layout_dir == 0 ? "Identity-H" : "Identity-V"); mrec->font_name = mstrdup(path); mrec->opt.index = index; if (layout_dir != 0) mrec->opt.flags |= FONTMAP_OPT_VERT; fill_in_defaults(mrec, fontmap_key); free(fontmap_key); mrec->opt.extend = extend / 65536.0; mrec->opt.slant = slant / 65536.0; mrec->opt.bold = embolden / 65536.0; mrec->opt.use_glyph_encoding = 1; ret = pdf_insert_fontmap_record(mrec->map_name, mrec); pdf_clear_fontmap_record(mrec); free(mrec); if (dpx_conf.verbose_level) dpx_message(">"); return ret; } fontmap_rec * pdf_lookup_fontmap_record (const char *tfm_name) { fontmap_rec *mrec = NULL; if (fontmap && tfm_name) mrec = ht_lookup_table(fontmap, tfm_name, strlen(tfm_name)); return mrec; } void pdf_init_fontmaps (void) { fontmap = NEW(1, struct ht_table); ht_init_table(fontmap, hval_free); } void pdf_close_fontmaps (void) { if (fontmap) { ht_clear_table(fontmap); free(fontmap); } fontmap = NULL; release_sfd_record(); } /* CIDFont options * * FORMAT: * * (:int:)?!?string(/string)?(,string)? */ static char * substr (const char **str, char stop) { char *sstr; const char *endptr; endptr = strchr(*str, stop); if (!endptr || endptr == *str) return NULL; sstr = NEW(endptr-(*str)+1, char); memcpy(sstr, *str, endptr-(*str)); sstr[endptr-(*str)] = '\0'; *str = endptr+1; return sstr; } #include #include #include #include #include #define CID_MAPREC_CSI_DELIM '/' static char * strip_options (const char *map_name, fontmap_opt *opt) { char *font_name; const char *p; char *next = NULL; int have_csi = 0, have_style = 0; assert(opt); p = map_name; font_name = NULL; opt->charcoll = NULL; opt->index = 0; opt->style = FONTMAP_STYLE_NONE; opt->flags = 0; if (*p == ':' && isdigit((unsigned char)*(p+1))) { opt->index = strtoul(p+1, &next, 10); if (*next == ':') p = next + 1; else { opt->index = 0; } } if (*p == '!') { /* no-embedding */ if (*(++p) == '\0') _tt_abort("Invalid map record: %s (--> %s)", map_name, p); opt->flags |= FONTMAP_OPT_NOEMBED; } if ((next = strchr(p, CID_MAPREC_CSI_DELIM)) != NULL) { if (next == p) _tt_abort("Invalid map record: %s (--> %s)", map_name, p); font_name = substr(&p, CID_MAPREC_CSI_DELIM); have_csi = 1; } else if ((next = strchr(p, ',')) != NULL) { if (next == p) _tt_abort("Invalid map record: %s (--> %s)", map_name, p); font_name = substr(&p, ','); have_style = 1; } else { font_name = NEW(strlen(p)+1, char); strcpy(font_name, p); } if (have_csi) { if ((next = strchr(p, ',')) != NULL) { opt->charcoll = substr(&p, ','); have_style = 1; } else if (p[0] == '\0') { _tt_abort("Invalid map record: %s.", map_name); } else { opt->charcoll = NEW(strlen(p)+1, char); strcpy(opt->charcoll, p); } } if (have_style) { if (strstartswith(p, "BoldItalic")) { if (*(p+10)) _tt_abort("Invalid map record: %s (--> %s)", map_name, p); opt->style = FONTMAP_STYLE_BOLDITALIC; } else if (strstartswith(p, "Bold")) { if (*(p+4)) _tt_abort("Invalid map record: %s (--> %s)", map_name, p); opt->style = FONTMAP_STYLE_BOLD; } else if (strstartswith(p, "Italic")) { if (*(p+6)) _tt_abort("Invalid map record: %s (--> %s)", map_name, p); opt->style = FONTMAP_STYLE_ITALIC; } } return font_name; }