/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. Copyright (C) 2008-2018 by Jin-Hwan Cho, Matthias Franz, 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-type1.h" #include #include #include #include #include #include #include "tectonic_bridge_core.h" #include "dpx-cff.h" #include "dpx-cff_dict.h" #include "dpx-cff_limits.h" #include "dpx-cff_types.h" #include "dpx-dpxconf.h" #include "dpx-error.h" #include "dpx-mem.h" #include "dpx-numbers.h" #include "dpx-pdfencoding.h" #include "dpx-pdffont.h" #include "dpx-pdflimits.h" #include "dpx-pdfobj.h" #include "dpx-t1_char.h" #include "dpx-t1_load.h" #define FONT_FLAG_FIXEDPITCH (1 << 0) /* Fixed-width font */ #define FONT_FLAG_SERIF (1 << 1) /* Serif font */ #define FONT_FLAG_SYMBOLIC (1 << 2) /* Symbolic font */ #define FONT_FLAG_SCRIPT (1 << 3) /* Script font */ #define FONT_FLAG_STANDARD (1 << 5) /* Adobe Standard Character Set */ #define FONT_FLAG_ITALIC (1 << 6) /* Italic */ #define FONT_FLAG_ALLCAP (1 << 16) /* All-cap font */ #define FONT_FLAG_SMALLCAP (1 << 17) /* Small-cap font */ #define FONT_FLAG_FORCEBOLD (1 << 18) /* Force bold at small text sizes */ static bool is_basefont (const char *name) { static const char *basefonts[] = { "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique", "Helvetica", "Helvetica-Bold", "Helvetica-Oblique", "Helvetica-BoldOblique", "Symbol", "Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic", "ZapfDingbats" }; int i; for (i = 0; i < 14; i++) { if (streq_ptr(name, basefonts[i])) return true; } return false; } int pdf_font_open_type1 (pdf_font *font, const char *ident, int index, int encoding_id, int embedding) { char fontname[PDF_NAME_LEN_MAX+1]; assert(ident); if (index != 0) { dpx_warning("Ignoring non-zero font index: %s", ident); } if (is_basefont(ident)) { font->fontname = NEW(strlen(ident)+1, char); strcpy(font->fontname, ident); font->subtype = PDF_FONT_FONTTYPE_TYPE1; font->flags |= PDF_FONT_FLAG_NOEMBED; font->flags |= PDF_FONT_FLAG_BASEFONT; } else { rust_input_handle_t handle; handle = ttstub_input_open(ident, TTBC_FILE_FORMAT_TYPE1, 0); /* NOTE: skipping qcheck_filetype() call in dpx_find_type1_file but we * call is_pfb() in just a second anyway. */ if (handle == NULL) return -1; if (!embedding) { dpx_warning("Ignoring no-embed option for Type1 font: %s", ident); font->flags &= ~PDF_FONT_FLAG_NOEMBED; } memset(fontname, 0, PDF_NAME_LEN_MAX+1); if (!is_pfb(handle) || t1_get_fontname(handle, fontname) < 0) _tt_abort("Failed to read Type 1 font \"%s\".", ident); ttstub_input_close(handle); font->fontname = NEW(strlen(fontname)+1, char); strcpy(font->fontname, fontname); font->subtype = PDF_FONT_FONTTYPE_TYPE1; } return 0; } static void get_font_attr (pdf_font *font, cff_font *cffont) { char *fontname; pdf_obj *descriptor; double capheight, ascent, descent; double italicangle, stemv; double defaultwidth, nominalwidth; int flags = 0, gid, i; static const char *L_c[] = { "H", "P", "Pi", "Rho", NULL }; static const char *L_d[] = { "p", "q", "mu", "eta", NULL }; static const char *L_a[] = { "b", "h", "lambda", NULL }; t1_ginfo gm; defaultwidth = 500.0; nominalwidth = 0.0; /* * CapHeight, Ascent, and Descent is meaningfull only for Latin/Greek/Cyrillic. * The BlueValues and OtherBlues also have those information. */ if (cff_dict_known(cffont->topdict, "FontBBox")) { /* Default values */ capheight = ascent = cff_dict_get(cffont->topdict, "FontBBox", 3); descent = cff_dict_get(cffont->topdict, "FontBBox", 1); } else { capheight = 680.0; ascent = 690.0; descent = -190.0; } if (cff_dict_known(cffont->private[0], "StdVW")) { stemv = cff_dict_get(cffont->private[0], "StdVW", 0); } else { /* * We may use the following values for StemV: * Thin - ExtraLight: <= 50 * Light: 71 * Regular(Normal): 88 * Medium: 109 * SemiBold(DemiBold): 135 * Bold - Heavy: >= 166 */ stemv = 88.0; } if (cff_dict_known(cffont->topdict, "ItalicAngle")) { italicangle = cff_dict_get(cffont->topdict, "ItalicAngle", 0); if (italicangle != 0.0) flags |= FONT_FLAG_ITALIC; } else { italicangle = 0.0; } /* * Use "space", "H", "p", and "b" for various values. * Those characters should not "seac". (no accent) */ gid = cff_glyph_lookup(cffont, "space"); if (gid >= 0 && gid < cffont->cstrings->count) { t1char_get_metrics(cffont->cstrings->data + cffont->cstrings->offset[gid] - 1, cffont->cstrings->offset[gid+1] - cffont->cstrings->offset[gid], cffont->subrs[0], &gm); defaultwidth = gm.wx; } for (i = 0; L_c[i] != NULL; i++) { gid = cff_glyph_lookup(cffont, L_c[i]); if (gid >= 0 && gid < cffont->cstrings->count) { t1char_get_metrics(cffont->cstrings->data + cffont->cstrings->offset[gid] - 1, cffont->cstrings->offset[gid+1] - cffont->cstrings->offset[gid], cffont->subrs[0], &gm); capheight = gm.bbox.ury; break; } } for (i = 0; L_d[i] != NULL; i++) { gid = cff_glyph_lookup(cffont, L_d[i]); if (gid >= 0 && gid < cffont->cstrings->count) { t1char_get_metrics(cffont->cstrings->data + cffont->cstrings->offset[gid] - 1, cffont->cstrings->offset[gid+1] - cffont->cstrings->offset[gid], cffont->subrs[0], &gm); descent = gm.bbox.lly; break; } } for (i = 0; L_a[i] != NULL; i++) { gid = cff_glyph_lookup(cffont, L_a[i]); if (gid >= 0 && gid < cffont->cstrings->count) { t1char_get_metrics(cffont->cstrings->data + cffont->cstrings->offset[gid] - 1, cffont->cstrings->offset[gid+1] - cffont->cstrings->offset[gid], cffont->subrs[0], &gm); ascent = gm.bbox.ury; break; } } if (defaultwidth != 0.0) { cff_dict_add(cffont->private[0], "defaultWidthX", 1); cff_dict_set(cffont->private[0], "defaultWidthX", 0, defaultwidth); } if (nominalwidth != 0.0) { cff_dict_add(cffont->private[0], "nominalWidthX", 1); cff_dict_set(cffont->private[0], "nominalWidthX", 0, nominalwidth); } if (cff_dict_known(cffont->private[0], "ForceBold") && cff_dict_get(cffont->private[0], "ForceBold", 0)) { flags |= FONT_FLAG_FORCEBOLD; } if (cff_dict_known(cffont->private[0], "IsFixedPitch") && cff_dict_get(cffont->private[0], "IsFixedPitch", 0)) { flags |= FONT_FLAG_FIXEDPITCH; } fontname = font->fontname; descriptor = pdf_font_get_descriptor(font); if (fontname && !strstr(fontname, "Sans")) { flags |= FONT_FLAG_SERIF; } if (fontname && strstr(fontname, "Caps")) { flags |= FONT_FLAG_SMALLCAP; } flags |= FONT_FLAG_SYMBOLIC; /* FIXME */ pdf_add_dict(descriptor, pdf_new_name("CapHeight"), pdf_new_number(capheight)); pdf_add_dict(descriptor, pdf_new_name("Ascent"), pdf_new_number(ascent)); pdf_add_dict(descriptor, pdf_new_name("Descent"), pdf_new_number(descent)); pdf_add_dict(descriptor, pdf_new_name("ItalicAngle"), pdf_new_number(italicangle)); pdf_add_dict(descriptor, pdf_new_name("StemV"), pdf_new_number(stemv)); pdf_add_dict(descriptor, pdf_new_name("Flags"), pdf_new_number(flags)); } static void add_metrics (pdf_font *font, cff_font *cffont, char **enc_vec, double *widths, int num_glyphs) { pdf_obj *fontdict, *descriptor; pdf_obj *array; int code, firstchar, lastchar; double val; int i; char *usedchars; double scaling = 1.0; double norm_widths[256]; fontdict = pdf_font_get_resource (font); descriptor = pdf_font_get_descriptor(font); usedchars = font->usedchars; /* * The original FontBBox of the font is preserved, instead * of replacing it with tight bounding box calculated from * charstrings, to prevent Acrobat 4 from greeking text as * much as possible. */ if (!cff_dict_known(cffont->topdict, "FontBBox")) { _tt_abort("No FontBBox?"); } /* The widhts array in the font dictionary must be given relative * to the default scaling of 1000:1, not relative to the scaling * given by the font matrix. */ if (cff_dict_known(cffont->topdict, "FontMatrix")) { scaling = 1000*cff_dict_get(cffont->topdict, "FontMatrix", 0); } else { scaling = 1; } array = pdf_new_array(); for (i = 0; i < 4; i++) { val = cff_dict_get(cffont->topdict, "FontBBox", i); pdf_add_array(array, pdf_new_number(ROUND(val, 1.0))); } pdf_add_dict(descriptor, pdf_new_name("FontBBox"), pdf_link_obj(array)); pdf_release_obj(array); array = pdf_new_array(); if (num_glyphs <= 1) { /* This must be an error. */ firstchar = lastchar = 0; pdf_add_array(array, pdf_new_number(0.0)); } else { for (firstchar = 255, lastchar = 0, code = 0; code < 256; code++) { if (usedchars[code]) { if (code < firstchar) firstchar = code; if (code > lastchar) lastchar = code; norm_widths[code] = scaling * widths[cff_glyph_lookup(cffont, enc_vec[code])]; } } if (firstchar > lastchar) { dpx_warning("No glyphs actually used???"); pdf_release_obj(array); return; } pdf_check_tfm_widths(font->ident, norm_widths, firstchar, lastchar, usedchars); for (code = firstchar; code <= lastchar; code++) { if (usedchars[code]) { pdf_add_array(array, pdf_new_number(ROUND(norm_widths[code], 0.1))); } else { pdf_add_array(array, pdf_new_number(0.0)); } } } if (pdf_array_length(array) > 0) { pdf_add_dict(fontdict, pdf_new_name("Widths"), pdf_ref_obj(array)); } pdf_release_obj(array); pdf_add_dict(fontdict, pdf_new_name("FirstChar"), pdf_new_number(firstchar)); pdf_add_dict(fontdict, pdf_new_name("LastChar"), pdf_new_number(lastchar)); return; } static int write_fontfile (pdf_font *font, cff_font *cffont, pdf_obj *pdfcharset) { pdf_obj *descriptor; pdf_obj *fontfile, *stream_dict; cff_index *topdict; int private_size, stream_data_len, charstring_len; int topdict_offset, offset; #define WBUF_SIZE 1024 card8 *stream_data_ptr, wbuf[WBUF_SIZE]; descriptor = pdf_font_get_descriptor(font); topdict = cff_new_index(1); /* * Force existence of Encoding. */ if (!cff_dict_known(cffont->topdict, "CharStrings")) cff_dict_add(cffont->topdict, "CharStrings", 1); if (!cff_dict_known(cffont->topdict, "charset")) cff_dict_add(cffont->topdict, "charset", 1); if (!cff_dict_known(cffont->topdict, "Encoding")) cff_dict_add(cffont->topdict, "Encoding", 1); private_size = cff_dict_pack((cffont->private)[0], wbuf, WBUF_SIZE); /* Private dict is required (but may have size 0) */ if (!cff_dict_known(cffont->topdict, "Private")) cff_dict_add(cffont->topdict, "Private", 2); topdict->offset[1] = cff_dict_pack(cffont->topdict, wbuf, WBUF_SIZE) + 1; /* * Estimate total size of fontfile. */ charstring_len = cff_index_size(cffont->cstrings); stream_data_len = 4; /* header size */ stream_data_len += cff_index_size(cffont->name); stream_data_len += cff_index_size(topdict); stream_data_len += cff_index_size(cffont->string); stream_data_len += cff_index_size(cffont->gsubr); /* We are using format 1 for Encoding and format 0 for charset. * TODO: Should implement cff_xxx_size(). */ stream_data_len += 2 + (cffont->encoding->num_entries)*2 + 1 + (cffont->encoding->num_supps)*3; stream_data_len += 1 + (cffont->charsets->num_entries)*2; stream_data_len += charstring_len; stream_data_len += private_size; /* * Now we create FontFile data. */ stream_data_ptr = NEW(stream_data_len, card8); /* * Data Layout order as described in CFF spec., sec 2 "Data Layout". */ offset = 0; /* Header */ offset += cff_put_header(cffont, stream_data_ptr + offset, stream_data_len - offset); /* Name */ offset += cff_pack_index(cffont->name, stream_data_ptr + offset, stream_data_len - offset); /* Top DICT */ topdict_offset = offset; offset += cff_index_size(topdict); /* Strings */ offset += cff_pack_index(cffont->string, stream_data_ptr + offset, stream_data_len - offset); /* Global Subrs */ offset += cff_pack_index(cffont->gsubr, stream_data_ptr + offset, stream_data_len - offset); /* Encoding */ /* TODO: don't write Encoding entry if the font is always used * with PDF Encoding information. Applies to type1c.c as well. */ cff_dict_set(cffont->topdict, "Encoding", 0, offset); offset += cff_pack_encoding(cffont, stream_data_ptr + offset, stream_data_len - offset); /* charset */ cff_dict_set(cffont->topdict, "charset", 0, offset); offset += cff_pack_charsets(cffont, stream_data_ptr + offset, stream_data_len - offset); /* CharStrings */ cff_dict_set(cffont->topdict, "CharStrings", 0, offset); offset += cff_pack_index(cffont->cstrings, stream_data_ptr + offset, charstring_len); /* Private */ if ((cffont->private)[0] && private_size > 0) { private_size = cff_dict_pack(cffont->private[0], stream_data_ptr + offset, private_size); cff_dict_set(cffont->topdict, "Private", 1, offset); cff_dict_set(cffont->topdict, "Private", 0, private_size); } offset += private_size; /* Finally Top DICT */ topdict->data = NEW(topdict->offset[1] - 1, card8); cff_dict_pack (cffont->topdict, topdict->data, topdict->offset[1] - 1); cff_pack_index(topdict, stream_data_ptr + topdict_offset, cff_index_size(topdict)); cff_release_index(topdict); /* Copyright and Trademark Notice ommited. */ /* Flush Font File */ fontfile = pdf_new_stream(STREAM_COMPRESS); stream_dict = pdf_stream_dict(fontfile); pdf_add_dict(descriptor, pdf_new_name("FontFile3"), pdf_ref_obj (fontfile)); pdf_add_dict(stream_dict, pdf_new_name("Subtype"), pdf_new_name("Type1C")); pdf_add_stream (fontfile, (void *) stream_data_ptr, offset); pdf_release_obj(fontfile); if (pdf_check_version(2, 0) < 0) { pdf_add_dict(descriptor, pdf_new_name("CharSet"), pdf_new_string(pdf_stream_dataptr(pdfcharset), pdf_stream_length(pdfcharset))); } free(stream_data_ptr); return offset; } int pdf_font_load_type1 (pdf_font *font) { pdf_obj *fontdict; pdf_obj *pdfcharset; /* Actually string object */ int encoding_id; char *usedchars, *ident; char *fontname, *uniqueTag; char *fullname; /* With pseudo unique tag */ cff_font *cffont; cff_charsets *charset; char **enc_vec; double defaultwidth, nominalwidth; double *widths; card16 *GIDMap, num_glyphs = 0; int offset; int code; rust_input_handle_t handle; assert(font); if (!font->reference) return 0; fontdict = pdf_font_get_resource(font); encoding_id = font->encoding_id; usedchars = font->usedchars; ident = font->filename; fontname = font->fontname; uniqueTag = pdf_font_get_uniqueTag (font); assert(usedchars); assert(ident); assert(fontname); handle = ttstub_input_open(ident, TTBC_FILE_FORMAT_TYPE1, 0); if (handle == NULL) _tt_abort("Type1: Could not open Type1 font: %s", ident); GIDMap = NULL; num_glyphs = 0; if (encoding_id >= 0) { enc_vec = NULL; } else { enc_vec = NEW(256, char *); for (code = 0; code <= 0xFF; code++) enc_vec[code] = NULL; } cffont = t1_load_font(enc_vec, 0, handle); if (!cffont) _tt_abort("Could not load Type 1 font: %s", ident); ttstub_input_close(handle); fullname = NEW(strlen(fontname) + 8, char); sprintf(fullname, "%6s+%s", uniqueTag, fontname); /* Encoding related things. */ if (encoding_id >= 0) { enc_vec = pdf_encoding_get_encoding(encoding_id); } else { /* Create enc_vec and ToUnicode CMap for built-in encoding. */ pdf_obj *tounicode; if (!pdf_lookup_dict(fontdict, "ToUnicode")) { tounicode = pdf_create_ToUnicode_CMap(fullname, enc_vec, usedchars); if (tounicode) { pdf_add_dict(fontdict, pdf_new_name("ToUnicode"), pdf_ref_obj (tounicode)); pdf_release_obj(tounicode); } } } cff_set_name(cffont, fullname); free(fullname); /* defaultWidthX, CapHeight, etc. */ get_font_attr(font, cffont); if (cff_dict_known(cffont->private[0], "defaultWidthX")) defaultwidth = cff_dict_get(cffont->private[0], "defaultWidthX", 0); else defaultwidth = 0.0; if (cff_dict_known(cffont->private[0], "nominalWidthX")) nominalwidth = cff_dict_get(cffont->private[0], "nominalWidthX", 0); else nominalwidth = 0.0; /* Create CFF encoding, charset, sort glyphs */ #define MAX_GLYPHS 1024 GIDMap = NEW(MAX_GLYPHS, card16); pdfcharset = pdf_new_stream(0); { int prev, duplicate; int gid; char *glyph; s_SID sid; cffont->encoding = NEW(1, cff_encoding); cffont->encoding->format = 1; cffont->encoding->num_entries = 0; cffont->encoding->data.range1 = NEW(256, cff_range1); cffont->encoding->num_supps = 0; cffont->encoding->supp = NEW(256, cff_map); charset = NEW(1, cff_charsets); charset->format = 0; charset->num_entries = 0; charset->data.glyphs = NEW(MAX_GLYPHS, s_SID); gid = cff_glyph_lookup(cffont, ".notdef"); if (gid < 0) _tt_abort("Type 1 font with no \".notdef\" glyph???"); GIDMap[0] = (card16) gid; if (dpx_conf.verbose_level > 2) dpx_message("[glyphs:/.notdef"); num_glyphs = 1; for (prev = -2, code = 0; code <= 0xff; code++) { glyph = enc_vec[code]; if (!usedchars[code]) continue; if (streq_ptr(glyph, ".notdef")) { dpx_warning("Character mapped to .notdef used in font: %s", fontname); usedchars[code] = 0; continue; } gid = cff_glyph_lookup(cffont, glyph); if (gid < 1 || gid >= cffont->cstrings->count) { dpx_warning("Glyph \"%s\" missing in font \"%s\".", glyph, fontname); usedchars[code] = 0; continue; } for (duplicate = 0; duplicate < code; duplicate++) { if (usedchars[duplicate] && enc_vec[duplicate] && streq_ptr(enc_vec[duplicate], glyph)) break; } sid = cff_add_string(cffont, glyph, 1); /* FIXME */ if (duplicate < code) { /* found duplicates */ cffont->encoding->supp[cffont->encoding->num_supps].code = duplicate; cffont->encoding->supp[cffont->encoding->num_supps].glyph = sid; cffont->encoding->num_supps += 1; } else { GIDMap[num_glyphs] = (card16) gid; charset->data.glyphs[charset->num_entries] = sid; charset->num_entries += 1; if (code != prev + 1) { cffont->encoding->num_entries += 1; cffont->encoding->data.range1[cffont->encoding->num_entries-1].first = code; cffont->encoding->data.range1[cffont->encoding->num_entries-1].n_left = 0; } else { cffont->encoding->data.range1[cffont->encoding->num_entries-1].n_left += 1; } prev = code; num_glyphs++; if (dpx_conf.verbose_level > 2) dpx_message("/%s", glyph); /* CharSet is actually string object. */ pdf_add_stream(pdfcharset, "/", 1); pdf_add_stream(pdfcharset, glyph, strlen(glyph)); } } if (cffont->encoding->num_supps > 0) { cffont->encoding->format |= 0x80; } else { cffont->encoding->supp = mfree(cffont->encoding->supp); } } widths = NEW(cffont->cstrings->count, double); /* No more strings will be added. The Type 1 seac operator may add another * glyph but the glyph name of those glyphs are contained in standard * string. The String Index will not be modified after here. BUT: We * cannot update the String Index yet because then we wouldn't be able to * find the GIDs of the base and accent characters (unless they have been * used already). */ { cff_index *cstring; t1_ginfo gm; card16 gid, gid_orig; int dstlen_max, srclen; card8 *srcptr, *dstptr; offset = dstlen_max = 0L; cstring = cff_new_index(cffont->cstrings->count); cstring->data = NULL; cstring->offset[0] = 1; /* The num_glyphs increases if "seac" operators are used. */ for (gid = 0; gid < num_glyphs; gid++) { if (offset + CS_STR_LEN_MAX >= dstlen_max) { dstlen_max += CS_STR_LEN_MAX * 2; cstring->data = RENEW(cstring->data, dstlen_max, card8); } gid_orig = GIDMap[gid]; dstptr = cstring->data + cstring->offset[gid] - 1; srcptr = cffont->cstrings->data + cffont->cstrings->offset[gid_orig] - 1; srclen = cffont->cstrings->offset[gid_orig + 1] - cffont->cstrings->offset[gid_orig]; offset += t1char_convert_charstring(dstptr, CS_STR_LEN_MAX, srcptr, srclen, cffont->subrs[0], defaultwidth, nominalwidth, &gm); cstring->offset[gid + 1] = offset + 1; if (gm.use_seac) { int bchar_gid, achar_gid, i; const char *bchar_name, *achar_name; /* * NOTE: * 1. seac.achar and seac.bchar must be contained in the CFF standard string. * 2. Those characters need not to be encoded. * 3. num_glyphs == charsets->num_entries + 1. */ achar_name = t1_get_standard_glyph(gm.seac.achar); achar_gid = cff_glyph_lookup(cffont, achar_name); bchar_name = t1_get_standard_glyph(gm.seac.bchar); bchar_gid = cff_glyph_lookup(cffont, bchar_name); if (achar_gid < 0) { dpx_warning("Accent char \"%s\" not found. Invalid use of \"seac\" operator.", achar_name); continue; } if (bchar_gid < 0) { dpx_warning("Base char \"%s\" not found. Invalid use of \"seac\" operator.", bchar_name); continue; } for (i = 0; i < num_glyphs; i++) { if (GIDMap[i] == achar_gid) break; } if (i == num_glyphs) { if (dpx_conf.verbose_level > 2) dpx_message("/%s", achar_name); GIDMap[num_glyphs++] = achar_gid; charset->data.glyphs[charset->num_entries] = cff_get_seac_sid(cffont, achar_name); charset->num_entries += 1; /* CharSet is actually string object. */ { pdf_add_stream(pdfcharset, "/", 1); pdf_add_stream(pdfcharset, achar_name, strlen(achar_name)); } } for (i = 0; i < num_glyphs; i++) { if (GIDMap[i] == bchar_gid) break; } if (i == num_glyphs) { if (dpx_conf.verbose_level > 2) dpx_message("/%s", bchar_name); GIDMap[num_glyphs++] = bchar_gid; charset->data.glyphs[charset->num_entries] = cff_get_seac_sid(cffont, bchar_name); charset->num_entries += 1; /* CharSet is actually string object. */ { pdf_add_stream(pdfcharset, "/", 1); pdf_add_stream(pdfcharset, achar_name, strlen(achar_name)); } } } widths[gid] = gm.wx; } cstring->count = num_glyphs; cff_release_index(cffont->subrs[0]); cffont->subrs[0] = NULL; cffont->subrs = mfree(cffont->subrs); cff_release_index(cffont->cstrings); cffont->cstrings = cstring; cff_release_charsets(cffont->charsets); cffont->charsets = charset; } if (dpx_conf.verbose_level > 2) dpx_message("]"); /* Now we can update the String Index */ cff_dict_update (cffont->topdict, cffont); cff_dict_update (cffont->private[0], cffont); cff_update_string(cffont); add_metrics(font, cffont, enc_vec, widths, num_glyphs); offset = write_fontfile(font, cffont, pdfcharset); if (dpx_conf.verbose_level > 1) dpx_message("[%u glyphs][%d bytes]", num_glyphs, offset); pdf_release_obj(pdfcharset); cff_close(cffont); /* Cleanup */ if (encoding_id < 0 && enc_vec) { for (code = 0; code < 256; code++) { enc_vec[code] = mfree(enc_vec[code]); } free(enc_vec); } free(widths); free(GIDMap); return 0; }