/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. Copyright (C) 2002-2019 by Jin-Hwan Cho and Shunsaku Hirata, the dvipdfmx project team. 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. */ /* * TrueType glyf table is sorted by CID and no CIDToGIDMap is used here. * GhostScript can't handle CIDToGIDMap correctly. */ #include "tectonic_bridge_core.h" #include "dpx-dpxconf.h" #include "dpx-dpxfile.h" #include "dpx-error.h" #include "dpx-mem.h" #include "dpx-numbers.h" /* pseudo unique tag */ #include "dpx-pdffont.h" #include "dpx-pdflimits.h" #include "dpx-pdfobj.h" #ifndef PDF_NAME_LEN_MAX # define PDF_NAME_LEN_MAX 255 #endif #include "dpx-cidtype2.h" #include #include #include #include #include #include "dpx-cid.h" /* CID font */ #include "dpx-cmap.h" /* TrueType */ #include "dpx-sfnt.h" #include "dpx-tt_aux.h" #include "dpx-tt_cmap.h" #include "dpx-tt_glyf.h" #include "dpx-tt_gsub.h" #include "dpx-tt_table.h" #include "dpx-type0.h" #include "dpx-unicode.h" /* * PDF viewer applications use following tables (CIDFontType 2) * * head, hhea, loca, maxp, glyf, hmtx, fpgm, cvt_, prep * * - from PDF Ref. v.1.3, 2nd ed. * * The fpgm, cvt_, and prep tables appears only when TrueType instructions * requires them. Those tables must be preserved if they exist. * We use must_exist flag to indicate `preserve it if present' * and to make sure not to cause an error when it does not exist. * * post and name table must exist in ordinary TrueType font file, * but when a TrueType font is converted to CIDFontType 2 font, those tables * are no longer required. * * The OS/2 table (required for TrueType font for Windows and OS/2) contains * liscencing information, but PDF viewers seems not using them. * * The 'name' table added. See comments in ttf.c. */ static struct { const char *name; int must_exist; } required_table[] = { {"OS/2", 0}, {"head", 1}, {"hhea", 1}, {"loca", 1}, {"maxp", 1}, {"name", 1}, {"glyf", 1}, {"hmtx", 1}, {"fpgm", 0}, {"cvt ", 0}, {"prep", 0}, {NULL, 0} }; static int validate_name (char *fontname, int len) { int i, count; char *p; static const char *badstrlist[] = { "-WIN-RKSJ-H", "-WINP-RKSJ-H", "-WING-RKSJ-H", "-90pv-RKSJ-H", NULL }; for (count = 0, i = 0; i < len; i++) { if (fontname[i] == 0) { memmove(fontname + i, fontname + i + 1, len - i); count++; len--; } } if (count > 0) { dpx_warning("Removed %d null character(s) from fontname --> %s", count, fontname); } fontname[len] = '\0'; /* For some fonts that have bad PS name. ad hoc. remove me. */ for (i = 0; badstrlist[i] != NULL; i++) { p = strstr(fontname, badstrlist[i]); if (p && p > fontname) { dpx_warning("Removed string \"%s\" from fontname \"%s\".", badstrlist[i], fontname); p[0] = '\0'; len = (int) (p - fontname); break; } } if (len < 1) { dpx_warning("No valid character found in fontname string."); return -1; } return 0; } /* * We will follow the convension for finding ToUnicode CMap described in PDF * Reference 4th ed., page 432. The name of "ToCode" (not limited to Unicode * here) CMap is obtained by concatenating the registry, ordering, and the * name of encoding. * * UCSms-UCS4, UCSms-UCS2, UCS4 added... */ #define WIN_UCS_INDEX_MAX 1 #define KNOWN_ENCODINGS_MAX 9 static struct { unsigned short platform; unsigned short encoding; const char *pdfnames[5]; } known_encodings[] = { {TT_WIN, TT_WIN_UCS4, {"UCSms-UCS4", "UCSms-UCS2", "UCS4", "UCS2", NULL}}, {TT_WIN, TT_WIN_UNICODE, {"UCSms-UCS4", "UCSms-UCS2", "UCS4", "UCS2", NULL}}, {TT_WIN, TT_WIN_SJIS, {"90ms-RKSJ", NULL}}, {TT_WIN, TT_WIN_RPC, {"GBK-EUC", NULL}}, {TT_WIN, TT_WIN_BIG5, {"ETen-B5", NULL}}, {TT_WIN, TT_WIN_WANSUNG, {"KSCms-UHC", NULL}}, {TT_MAC, TT_MAC_JAPANESE, {"90pv-RKSJ", NULL}}, {TT_MAC, TT_MAC_TRADITIONAL_CHINESE, {"B5pc", NULL}}, {TT_MAC, TT_MAC_SIMPLIFIED_CHINESE, {"GBpc-EUC", NULL}}, {TT_MAC, TT_MAC_KOREAN, {"KSCpc-EUC", NULL}}, {0, 0, {NULL}} }; static CMap * find_tocode_cmap (const char *reg, const char *ord, int select) { int cmap_id = -1, i; char *cmap_name; const char *append; if (!reg || !ord || select < 0 || select > KNOWN_ENCODINGS_MAX) { dpx_warning("Character set unknown."); return NULL; } if (streq_ptr(ord, "UCS") && select <= WIN_UCS_INDEX_MAX) return NULL; for (i = 0; cmap_id < 0 && i < 5; i++) { append = known_encodings[select].pdfnames[i]; if (!append) break; cmap_name = NEW(strlen(reg) + strlen(ord) + strlen(append) + 3, char); sprintf(cmap_name, "%s-%s-%s", reg, ord, append); cmap_id = CMap_cache_find(cmap_name); free(cmap_name); } if (cmap_id < 0) { dpx_warning("Could not find CID-to-Code mapping for \"%s-%s\".", reg, ord); dpx_warning("I tried to load (one of) the following file(s):"); for (i = 0; i < 5; i++) { append = known_encodings[select].pdfnames[i]; if (!append) break; dpx_message(" %s-%s-%s", reg, ord, append); } dpx_warning("Please check if this file exists."); return NULL; } return CMap_cache_get(cmap_id); } /* * CIDFont glyph metrics: * Mostly same as add_CID[HV]Metrics in cidtype0.c. */ #define PDFUNIT(v) ((double) (ROUND(1000.0*(v)/(g->emsize), 1))) static void add_TTCIDHMetrics (pdf_obj *fontdict, struct tt_glyphs *g, char *used_chars, unsigned char *cidtogidmap, unsigned short last_cid) { int cid, start = 0, prev = 0; pdf_obj *w_array, *an_array = NULL; double dw; int empty = 1; w_array = pdf_new_array(); if (g->dw != 0 && g->dw <= g->emsize) { dw = PDFUNIT(g->dw); } else { dw = PDFUNIT(g->gd[0].advw); } for (cid = 0; cid <= last_cid; cid++) { USHORT idx, gid; double width; if (!is_used_char2(used_chars, cid)) continue; gid = (cidtogidmap) ? ((cidtogidmap[2*cid] << 8)|cidtogidmap[2*cid+1]) : cid; idx = tt_get_index(g, gid); if (cid != 0 && idx == 0) continue; width = PDFUNIT((g->gd)[idx].advw); if (width == dw) { if (an_array) { pdf_add_array(w_array, pdf_new_number(start)); pdf_add_array(w_array, an_array); an_array = NULL; empty = 0; } } else { if (cid != prev + 1) { if (an_array) { pdf_add_array(w_array, pdf_new_number(start)); pdf_add_array(w_array, an_array); an_array = NULL; empty = 0; } } if (an_array == NULL) { an_array = pdf_new_array(); start = cid; } pdf_add_array(an_array, pdf_new_number(width)); prev = cid; } } if (an_array) { pdf_add_array(w_array, pdf_new_number(start)); pdf_add_array(w_array, an_array); empty = 0; } pdf_add_dict(fontdict, pdf_new_name("DW"), pdf_new_number(dw)); if (!empty) { pdf_add_dict(fontdict, pdf_new_name("W"), pdf_ref_obj(w_array)); } pdf_release_obj(w_array); return; } static void add_TTCIDVMetrics (pdf_obj *fontdict, struct tt_glyphs *g, char *used_chars, unsigned short last_cid) { pdf_obj *w2_array, *an_array = NULL; int cid; double defaultVertOriginY, defaultAdvanceHeight; int empty = 1; defaultVertOriginY = PDFUNIT(g->default_advh - g->default_tsb); defaultAdvanceHeight = PDFUNIT(g->default_advh); w2_array = pdf_new_array(); for (cid = 0; cid <= last_cid; cid++) { USHORT idx; double vertOriginX, vertOriginY, advanceHeight; if (!is_used_char2(used_chars, cid)) continue; idx = tt_get_index(g, (USHORT)cid); if (cid != 0 && idx == 0) continue; advanceHeight = PDFUNIT(g->gd[idx].advh); vertOriginX = PDFUNIT(0.5*(g->gd[idx].advw)); vertOriginY = PDFUNIT(g->gd[idx].tsb + g->gd[idx].ury); /* * c_first c_last w1_y v_x v_y * This form may hit Acrobat's implementation limit of array element size, * 8192. AFPL GhostScript 8.11 stops with rangecheck error with this. * Maybe GS's bug? */ if (vertOriginY != defaultVertOriginY || advanceHeight != defaultAdvanceHeight) { pdf_add_array(w2_array, pdf_new_number(cid)); pdf_add_array(w2_array, pdf_new_number(cid)); pdf_add_array(w2_array, pdf_new_number(-advanceHeight)); pdf_add_array(w2_array, pdf_new_number(vertOriginX)); pdf_add_array(w2_array, pdf_new_number(vertOriginY)); empty = 0; } } if (defaultVertOriginY != 880 || defaultAdvanceHeight != 1000) { an_array = pdf_new_array(); pdf_add_array(an_array, pdf_new_number(defaultVertOriginY)); pdf_add_array(an_array, pdf_new_number(-defaultAdvanceHeight)); pdf_add_dict(fontdict, pdf_new_name ("DW2"), an_array); } if (!empty) { pdf_add_dict(fontdict, pdf_new_name("W2"), pdf_ref_obj(w2_array)); } pdf_release_obj(w2_array); return; } /* * The following routine fixes few problems caused by vendor specific * Unicode mappings. */ #define FIX_CJK_UNIOCDE_SYMBOLS 1 static unsigned short fix_CJK_symbols (unsigned short code) { unsigned short alt_code; static struct { unsigned short alt1; unsigned short alt2; } CJK_Uni_symbols[] = { /* * Microsoft/Apple Unicode mapping difference: * They are taken from SJIS-Unicode mapping difference but nearly * same thing might be applied to Chinese (e.g., Big5) too. */ {0x2014, 0x2015}, /* EM DASH <-> HORIZONTAL BAR */ {0x2016, 0x2225}, /* DOUBLE VERTICAL LINE <-> PARALLEL TO */ {0x203E, 0xFFE3}, /* OVERLINE <-> FULLWIDTH MACRON */ {0x2026, 0x22EF}, /* HORIZONTAL ELLIPSIS <-> MIDLINE HORIZONTAL ELLIPSIS */ {0x2212, 0xFF0D}, /* MINUS SIGN <-> FULLWIDTH HYPHEN-MINUS */ {0x301C, 0xFF5E}, /* WAVE DASH <-> FULLWIDTH TILDE */ {0xFFE0, 0x00A2}, /* FULLWIDTH CENT SIGN <-> CENT SIGN */ {0xFFE1, 0x00A3}, /* FULLWIDTH POUND SIGN <-> POUND SIGN */ {0xFFE2, 0x00AC}, /* FULLWIDTH NOT SIGN <-> NOT SIGN */ {0xFFFF, 0xFFFF}, /* EOD */ }; #define NUM_CJK_SYMBOLS (sizeof(CJK_Uni_symbols)/sizeof(CJK_Uni_symbols[0])) unsigned int i; alt_code = code; for (i = 0; i < NUM_CJK_SYMBOLS; i++) { if (CJK_Uni_symbols[i].alt1 == code) { alt_code = CJK_Uni_symbols[i].alt2; break; } else if (CJK_Uni_symbols[i].alt2 == code) { alt_code = CJK_Uni_symbols[i].alt1; break; } } return alt_code; } static int32_t cid_to_code (CMap *cmap, CID cid, int unicode_cmap) { unsigned char inbuf[2], outbuf[32]; size_t inbytesleft = 2, outbytesleft = 32; const unsigned char *p; unsigned char *q; if (!cmap) return cid; inbuf[0] = (cid >> 8) & 0xff; inbuf[1] = cid & 0xff; p = inbuf; q = outbuf; CMap_decode_char(cmap, &p, &inbytesleft, &q, &outbytesleft); if (inbytesleft != 0) return -1; else if (outbytesleft == 31) return (int32_t) outbuf[0]; else if (outbytesleft == 30) return (int32_t) (outbuf[0] << 8|outbuf[1]); else if (outbytesleft == 28) { if (unicode_cmap) { /* We assume the output encoding is UTF-16. */ int32_t uc; const unsigned char *endptr; p = outbuf; endptr = p + 4; uc = UC_UTF16BE_decode_char(&p, endptr); if (p != endptr) dpx_warning("CID=%u mapped to non-single Unicode characters...", cid); return p == endptr ? uc : -1; } else { return (outbuf[0] << 24)|(outbuf[1] << 16)|(outbuf[2] << 8)|outbuf[3]; } } return -1; } static uint16_t cid_to_gid (CMap *cmap, CID cid) { unsigned char inbuf[2], outbuf[2]; size_t inbytesleft = 2, outbytesleft = 2; const unsigned char *p; unsigned char *q; if (!cmap) return cid; inbuf[0] = (cid >> 8) & 0xff; inbuf[1] = cid & 0xff; p = inbuf; q = outbuf; CMap_decode_char(cmap, &p, &inbytesleft, &q, &outbytesleft); if (inbytesleft != 0 || outbytesleft != 0) return 0; return (uint16_t) (outbuf[0] << 8|outbuf[1]); } /* #define NO_GHOSTSCRIPT_BUG 1 */ int CIDFont_type2_dofont (pdf_font *font) { pdf_obj *fontfile; sfnt *sfont; char *h_used_chars, *v_used_chars, *used_chars; struct tt_glyphs *glyphs; CMap *cmap = NULL; tt_cmap *ttcmap = NULL; ULONG offset = 0; CID cid, last_cid; unsigned char *cidtogidmap; USHORT num_glyphs; enum { glyph_ordering, via_cid_to_code, via_cid_to_gid, } maptype = via_cid_to_code; int i, unicode_cmap = 0; rust_input_handle_t handle = NULL; if (!font->reference) return 0; pdf_add_dict(font->resource, pdf_new_name("FontDescriptor"), pdf_ref_obj(font->descriptor)); if (font->flags & PDF_FONT_FLAG_BASEFONT) return 0; /* * CIDSystemInfo comes here since Supplement can be increased. */ { pdf_obj *tmp; tmp = pdf_new_dict (); pdf_add_dict(tmp, pdf_new_name("Registry"), pdf_new_string(font->cid.csi.registry, strlen(font->cid.csi.registry))); pdf_add_dict(tmp, pdf_new_name("Ordering"), pdf_new_string(font->cid.csi.ordering, strlen(font->cid.csi.ordering))); pdf_add_dict(tmp, pdf_new_name("Supplement"), pdf_new_number(font->cid.csi.supplement)); pdf_add_dict(font->resource, pdf_new_name("CIDSystemInfo"), tmp); } /* Quick exit for non-embedded & fixed-pitch font. */ if (!font->cid.options.embed && (opt_flags_cidfont & CIDFONT_FORCE_FIXEDPITCH)) { pdf_add_dict(font->resource, pdf_new_name("DW"), pdf_new_number(1000.0)); return 0; } handle = dpx_open_truetype_file(font->filename); if (!handle) { handle = dpx_open_dfont_file(font->filename); if (!handle) { dpx_warning("Could not open TTF/dfont file: %s", font->filename); return -1; } sfont = dfont_open(handle, font->index); } else { sfont = sfnt_open(handle); } if (!sfont) { dpx_warning("Could not open TTF file: %s", font->filename); ttstub_input_close(handle); return -1; } switch (sfont->type) { case SFNT_TYPE_TTC: offset = ttc_read_offset(sfont, font->index); if (offset == 0) { dpx_warning("Invalid TTC index in %s.", font->filename); sfnt_close(sfont); ttstub_input_close(handle); return -1; } break; case SFNT_TYPE_TRUETYPE: if (font->index > 0) { dpx_warning("Found TrueType font file while expecting TTC file (%s).", font->filename); sfnt_close(sfont); ttstub_input_close(handle); return -1; } offset = 0; break; case SFNT_TYPE_DFONT: offset = sfont->offset; break; default: dpx_warning("Not a TrueType/TTC font (%s)?", font->filename); sfnt_close(sfont); ttstub_input_close(handle); return -1; break; } if (sfnt_read_table_directory(sfont, offset) < 0) { dpx_warning("Could not read TrueType table directory (%s).", font->filename); sfnt_close(sfont); ttstub_input_close(handle); return -1; } /* * Adobe-Identity means font's internal glyph ordering here. */ if (streq_ptr(font->cid.csi.registry, "Adobe") && streq_ptr(font->cid.csi.ordering, "Identity")) { maptype = glyph_ordering; ttcmap = NULL; cmap = NULL; } else { if (font->cid.csi.registry && font->cid.csi.ordering) { char *cmap_name; size_t len; int cmap_id; len = strlen(font->cid.csi.registry) + strlen(font->cid.csi.ordering); len += strlen(font->fontname); len += 3; cmap_name = NEW(len+1, char); snprintf(cmap_name, len + 1, "%s-%s-%s", font->cid.csi.registry, font->cid.csi.ordering, font->fontname); cmap_id = CMap_cache_find(cmap_name); free(cmap_name); if (cmap_id >= 0) { cmap = CMap_cache_get(cmap_id); ttcmap = NULL; maptype = via_cid_to_gid; } } if (maptype != via_cid_to_gid) { maptype = via_cid_to_code; /* * This part contains a bug. It may choose SJIS encoding TrueType cmap * table for Adobe-GB1. */ for (i = 0; i <= KNOWN_ENCODINGS_MAX; i++) { ttcmap = tt_cmap_read(sfont, known_encodings[i].platform, known_encodings[i].encoding); if (ttcmap) break; } if (!ttcmap) { dpx_warning("No usable TrueType cmap table found for font \"%s\".", font->filename); dpx_warning("CID character collection for this font is set to \"%s-%s\"", font->cid.csi.registry, font->cid.csi.ordering); dpx_warning("Cannot continue without this..."); sfnt_close(sfont); ttstub_input_close(handle); return -1; } else if (i <= WIN_UCS_INDEX_MAX) { unicode_cmap = 1; } else { unicode_cmap = 0; } if (streq_ptr(font->cid.csi.ordering, "UCS") && i <= WIN_UCS_INDEX_MAX) { cmap = NULL; } else { cmap = find_tocode_cmap(font->cid.csi.registry, font->cid.csi.ordering, i); if (!cmap) { sfnt_close(sfont); ttstub_input_close(handle); return -1; } } } } glyphs = tt_build_init(); last_cid = 0; num_glyphs = 1; /* .notdef */ used_chars = h_used_chars = v_used_chars = NULL; { int c; h_used_chars = font->usedchars; v_used_chars = font->cid.usedchars_v; assert(h_used_chars || v_used_chars); /* * Quick check of max CID. */ c = 0; for (i = 8191; i >= 0; i--) { if (h_used_chars && h_used_chars[i] != 0) { last_cid = i * 8 + 7; c = h_used_chars[i]; break; } } for (i = 8191; i >= 0; i--) { if (v_used_chars && v_used_chars[i] != 0) { if (i * 8 + 7 >= last_cid) { c = (i * 8 + 7 > last_cid) ? (v_used_chars[i]) : (c | v_used_chars[i]); last_cid = i * 8 + 7; break; } } } if (last_cid > 0) { for (i = 0; i < 8; i++) { if ((c >> i) & 1) break; last_cid--; } } assert(last_cid < 0xFFFFu); } #ifndef NO_GHOSTSCRIPT_BUG cidtogidmap = NULL; #else cidtogidmap = NEW((last_cid + 1) * 2, unsigned char); memset(cidtogidmap, 0, (last_cid + 1) * 2); #endif /* !NO_GHOSTSCRIPT_BUG */ /* * Map CIDs to GIDs. * Horizontal and vertical used_chars are merged. */ /* * Horizontal */ if (h_used_chars) { used_chars = h_used_chars; for (cid = 1; cid <= last_cid; cid++) { int32_t code; uint16_t gid = 0; if (!is_used_char2(h_used_chars, cid)) continue; switch (maptype) { case glyph_ordering: gid = cid; code = cid; break; case via_cid_to_gid: gid = cid_to_gid(cmap, cid); code = cid; break; case via_cid_to_code: code = cid_to_code(cmap, cid, unicode_cmap); if (code < 0) { dpx_warning("Unable to map CID to code: CID=%u", cid); } else { gid = tt_cmap_lookup(ttcmap, code); #ifdef FIX_CJK_UNIOCDE_SYMBOLS if (gid == 0 && unicode_cmap) { int alt_code; alt_code = fix_CJK_symbols((unsigned short)code); if (alt_code != code) { gid = tt_cmap_lookup(ttcmap, alt_code); if (gid != 0) { dpx_warning("Unicode char U+%04x replaced with U+%04x.", code, alt_code); } } } #endif /* FIX_CJK_UNIOCDE_SYMBOLS */ } break; } if (gid == 0) { if (code >= 0) dpx_warning("Glyph missing in font. (CID=%u, code=0x%04x)", cid, code); } /* TODO: duplicated glyph */ #ifndef NO_GHOSTSCRIPT_BUG gid = tt_add_glyph(glyphs, gid, cid); #else gid = tt_add_glyph(glyphs, gid, num_glyphs); cidtogidmap[2*cid ] = gid >> 8; cidtogidmap[2*cid+1] = gid & 0xff; #endif /* !NO_GHOSTSCRIPT_BUG */ num_glyphs++; } } /* * Vertical */ if (v_used_chars) { otl_gsub *gsub_list = NULL; /* * Require `vrt2' or `vert'. */ if (maptype != via_cid_to_code) { gsub_list = NULL; } else { gsub_list = otl_gsub_new(); if (otl_gsub_add_feat(gsub_list, "*", "*", "vrt2", sfont) < 0) { if (otl_gsub_add_feat(gsub_list, "*", "*", "vert", sfont) < 0) { dpx_warning("GSUB feature vrt2/vert not found."); otl_gsub_release(gsub_list); gsub_list = NULL; } else { otl_gsub_select(gsub_list, "*", "*", "vert"); } } else { otl_gsub_select(gsub_list, "*", "*", "vrt2"); } } for (cid = 1; cid <= last_cid; cid++) { int32_t code; uint16_t gid; if (!is_used_char2(v_used_chars, cid)) continue; /* There may be conflict of horizontal and vertical glyphs * when font is used with /UCS. However, we simply ignore * that... */ if (h_used_chars && is_used_char2(h_used_chars, cid)) { continue; } switch (maptype) { case glyph_ordering: gid = cid; code = cid; break; case via_cid_to_gid: gid = cid_to_gid(cmap, cid); code = cid; break; case via_cid_to_code: code = cid_to_code(cmap, cid, unicode_cmap); if (code < 0) { dpx_warning("Unable to map CID to code: CID=%u", cid); } else { gid = tt_cmap_lookup(ttcmap, code); #ifdef FIX_CJK_UNIOCDE_SYMBOLS if (gid == 0 && unicode_cmap) { int alt_code; alt_code = fix_CJK_symbols((unsigned short)code); if (alt_code != code) { gid = tt_cmap_lookup(ttcmap, alt_code); if (gid != 0) { dpx_warning("Unicode char U+%04x replaced with U+%04x.", code, alt_code); } } } #endif /* FIX_CJK_UNIOCDE_SYMBOLS */ } break; } if (gid == 0) { if (code >= 0) dpx_warning("Glyph missing in font. (CID=%u, code=0x%04x)", cid, code); } else if (gsub_list) { otl_gsub_apply(gsub_list, &gid); } #ifndef NO_GHOSTSCRIPT_BUG gid = tt_add_glyph(glyphs, gid, cid); #else gid = tt_add_glyph(glyphs, gid, num_glyphs); cidtogidmap[2*cid ] = gid >> 8; cidtogidmap[2*cid+1] = gid & 0xff; #endif /* !NO_GHOSTSCRIPT_BUG */ if (used_chars) /* merge vertical used_chars to horizontal */ add_to_used_chars2(used_chars, cid); num_glyphs++; } if (gsub_list) otl_gsub_release(gsub_list); if (!used_chars) /* We have no horizontal. */ used_chars = v_used_chars; } assert(used_chars); tt_cmap_release(ttcmap); if (font->cid.options.embed) { if (tt_build_tables(sfont, glyphs) < 0) { dpx_warning("Could not created FontFile stream."); if (cidtogidmap) free(cidtogidmap); sfnt_close(sfont); ttstub_input_close(handle); return -1; } if (dpx_conf.verbose_level > 1) dpx_message("[%u glyphs (Max CID: %u)]", glyphs->num_glyphs, last_cid); } else { if (tt_get_metrics(sfont, glyphs) < 0) { dpx_warning("Reading glyph metrics failed..."); if (cidtogidmap) free(cidtogidmap); sfnt_close(sfont); ttstub_input_close(handle); return -1; } } /* * DW, W, DW2, and W2 */ if (opt_flags_cidfont & CIDFONT_FORCE_FIXEDPITCH) { pdf_add_dict(font->resource, pdf_new_name("DW"), pdf_new_number(1000.0)); } else { add_TTCIDHMetrics(font->resource, glyphs, used_chars, cidtogidmap, last_cid); if (font->cid.need_vmetrics) add_TTCIDVMetrics(font->resource, glyphs, used_chars, last_cid); } /* CIDSet * NOTE: All glyphs including component glyph and dummy glyph must be * listed in CIDSet. However, .notdef glyph should be ommitted. */ if (pdf_check_version(2, 0) < 0) { pdf_obj *cidset; char *cidset_data; cidset_data = NEW(glyphs->last_gid/8 + 1, char); memset(cidset_data, 0, glyphs->last_gid/8 + 1); for (i = 1; i <= glyphs->last_gid; i++) cidset_data[i/8] |= (1 << (7 - i % 8)); cidset = pdf_new_stream(STREAM_COMPRESS); pdf_add_stream(cidset, cidset_data, glyphs->last_gid/8 + 1); free(cidset_data); pdf_add_dict(font->descriptor, pdf_new_name("CIDSet"), pdf_ref_obj(cidset)); pdf_release_obj(cidset); } tt_build_finish(glyphs); /* Finish here if not embedded. */ if (!font->cid.options.embed) { free(cidtogidmap); sfnt_close(sfont); if (handle) ttstub_input_close(handle); return 0; } /* Create font file */ for (i = 0; required_table[i].name; i++) { if (sfnt_require_table(sfont, required_table[i].name, required_table[i].must_exist) < 0) { dpx_warning("Some required TrueType table (%s) does not exist.", required_table[i].name); free(cidtogidmap); sfnt_close(sfont); ttstub_input_close(handle); return -1; } } /* * FontFile2 */ fontfile = sfnt_create_FontFile_stream(sfont); sfnt_close(sfont); if (handle) ttstub_input_close(handle); if (!fontfile) { dpx_warning("Could not created FontFile stream for \"%s\".", font->filename); free(cidtogidmap); return -1; } if (dpx_conf.verbose_level > 1) { dpx_message("[%d bytes]", pdf_stream_length(fontfile)); } pdf_add_dict(font->descriptor, pdf_new_name("FontFile2"), pdf_ref_obj (fontfile)); pdf_release_obj(fontfile); /* * CIDToGIDMap * Adobe's PDF Reference had been describing it as "optional" and * default value as "Identity". However, ISO 32000-1 requires it * for Type 2 CIDFonts with embedded font programs. */ if (!cidtogidmap) { pdf_add_dict(font->resource, pdf_new_name("CIDToGIDMap"), pdf_new_name("Identity")); } else { pdf_obj *c2gmstream; c2gmstream = pdf_new_stream(STREAM_COMPRESS); pdf_add_stream(c2gmstream, cidtogidmap, (last_cid + 1) * 2); pdf_add_dict(font->resource, pdf_new_name("CIDToGIDMap"), pdf_ref_obj (c2gmstream)); pdf_release_obj(c2gmstream); free(cidtogidmap); } return 0; } int CIDFont_type2_open (pdf_font *font, const char *name, int index, cid_opt *opt) { int error; char *fontname; sfnt *sfont; ULONG offset = 0; rust_input_handle_t handle = NULL; assert(font && opt); handle = dpx_open_truetype_file(name); if (!handle) { handle = dpx_open_dfont_file(name); if (!handle) return -1; sfont = dfont_open(handle, index); } else { sfont = sfnt_open(handle); } if (!sfont) { ttstub_input_close(handle); return -1; } switch (sfont->type) { case SFNT_TYPE_TTC: offset = ttc_read_offset(sfont, index); break; case SFNT_TYPE_TRUETYPE: if (index > 0) { dpx_warning("Invalid TTC index (not TTC font): %s", name); sfnt_close(sfont); ttstub_input_close(handle); return -1; } offset = 0; break; case SFNT_TYPE_DFONT: offset = sfont->offset; break; default: sfnt_close(sfont); if (handle) ttstub_input_close(handle); return -1; } if (sfnt_read_table_directory(sfont, offset) < 0) { dpx_warning("Reading TrueType table directory failed."); sfnt_close(sfont); ttstub_input_close(handle); return -1; } /* Ignore TrueType Collection with CFF table. */ if (sfont->type == SFNT_TYPE_TTC && sfnt_find_table_pos(sfont, "CFF ")) { sfnt_close(sfont); if (handle) ttstub_input_close(handle); return -1; } { char *shortname; int namelen; /* MAC-ROMAN-EN-POSTSCRIPT or WIN-UNICODE-EN(US)-POSTSCRIPT */ shortname = NEW(PDF_NAME_LEN_MAX, char); namelen = tt_get_ps_fontname(sfont, shortname, PDF_NAME_LEN_MAX); if (namelen == 0) { memset(shortname, 0, PDF_NAME_LEN_MAX); strncpy(shortname, name, PDF_NAME_LEN_MAX); namelen = strlen(shortname); } error = validate_name(shortname, namelen); /* for SJIS, UTF-16, ... string */ if (error) { free(shortname); sfnt_close(sfont); ttstub_input_close(handle); return -1; } /* * Strlen works, after validate_named string. * Mangled name requires more 7 bytes. * Style requires more 11 bytes. */ fontname = NEW(strlen(shortname)+19, char); strcpy(fontname, shortname); free(shortname); } if (opt->embed && opt->style != FONT_STYLE_NONE) { dpx_warning("Embedding disabled due to style option for %s.", name); opt->embed = 0; } switch (opt->style) { case FONT_STYLE_BOLD: strcat(fontname, ",Bold"); break; case FONT_STYLE_ITALIC: strcat(fontname, ",Italic"); break; case FONT_STYLE_BOLDITALIC: strcat(fontname, ",BoldItalic"); break; } /* * CIDSystemInfo is determined from CMap or from map record option. */ font->fontname = fontname; font->subtype = PDF_FONT_FONTTYPE_CIDTYPE2; if (opt->csi.registry && opt->csi.ordering) { font->cid.csi.registry = NEW(strlen(opt->csi.registry)+1, char); strcpy(font->cid.csi.registry, opt->csi.registry); font->cid.csi.ordering = NEW(strlen(opt->csi.ordering)+1, char); strcpy(font->cid.csi.ordering, opt->csi.ordering); font->cid.csi.supplement = opt->csi.supplement; } font->resource = pdf_new_dict(); pdf_add_dict(font->resource, pdf_new_name("Type"), pdf_new_name("Font")); pdf_add_dict(font->resource, pdf_new_name("Subtype"), pdf_new_name("CIDFontType2")); font->descriptor = tt_get_fontdesc(sfont, &(opt->embed), opt->stemv, 0, name); if (!font->descriptor) { dpx_warning("Could not obtain necessary font info."); sfnt_close(sfont); ttstub_input_close(handle); return -1; } if (opt->embed) { char *tmp; pdf_font_make_uniqueTag(font->uniqueID); tmp = NEW(strlen(fontname)+8, char); sprintf(tmp, "%s+%s", font->uniqueID, font->fontname); pdf_add_dict(font->descriptor, pdf_new_name("FontName"), pdf_new_name(tmp)); pdf_add_dict(font->resource, pdf_new_name("BaseFont"), pdf_new_name(tmp)); free(tmp); } else { pdf_add_dict(font->descriptor, pdf_new_name("FontName"), pdf_new_name(font->fontname)); pdf_add_dict(font->resource, pdf_new_name("BaseFont"), pdf_new_name(font->fontname)); } sfnt_close(sfont); if (handle) ttstub_input_close(handle); /* * Don't write fontdict here. * /Supplement in /CIDSystemInfo may change. */ return 0; }