#include "libtexpdf.h"
#define FONT_FLAG_FIXEDPITCH (1 << 0)
#define FONT_FLAG_SERIF (1 << 1)
#define FONT_FLAG_SYMBOLIC (1 << 2)
#define FONT_FLAG_SCRIPT (1 << 3)
#define FONT_FLAG_STANDARD (1 << 5)
#define FONT_FLAG_ITALIC (1 << 6)
#define FONT_FLAG_ALLCAP (1 << 16)
#define FONT_FLAG_SMALLCAP (1 << 17)
#define FONT_FLAG_FORCEBOLD (1 << 18)
static int
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 (!strcmp(name, basefonts[i]))
return 1;
}
return 0;
}
int
pdf_font_open_type1 (pdf_font *font)
{
char *ident;
FILE *fp;
char fontname[PDF_NAME_LEN_MAX+1];
ASSERT(font);
ident = pdf_font_get_ident(font);
if (is_basefont(ident)) {
pdf_font_set_fontname(font, ident);
pdf_font_set_subtype (font, PDF_FONT_FONTTYPE_TYPE1);
pdf_font_set_flags (font,
(PDF_FONT_FLAG_NOEMBED|PDF_FONT_FLAG_BASEFONT));
} else {
fp = DPXFOPEN(ident, DPX_RES_TYPE_T1FONT);
if (!fp)
return -1;
memset(fontname, 0, PDF_NAME_LEN_MAX+1);
if (!is_pfb(fp) || t1_get_fontname(fp, fontname) < 0) {
ERROR("Failed to read Type 1 font \"%s\".", ident);
}
DPXFCLOSE(fp);
pdf_font_set_fontname(font, fontname);
pdf_font_set_subtype (font, 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;
long 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;
if (cff_dict_known(cffont->topdict, "FontBBox")) {
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 {
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;
}
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 = pdf_font_get_fontname (font);
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;
texpdf_add_dict(descriptor,
texpdf_new_name("CapHeight"), texpdf_new_number(capheight));
texpdf_add_dict(descriptor,
texpdf_new_name("Ascent"), texpdf_new_number(ascent));
texpdf_add_dict(descriptor,
texpdf_new_name("Descent"), texpdf_new_number(descent));
texpdf_add_dict(descriptor,
texpdf_new_name("ItalicAngle"), texpdf_new_number(italicangle));
texpdf_add_dict(descriptor,
texpdf_new_name("StemV"), texpdf_new_number(stemv));
texpdf_add_dict(descriptor,
texpdf_new_name("Flags"), texpdf_new_number(flags));
}
static void
add_metrics (pdf_font *font, cff_font *cffont, char **enc_vec, double *widths, long num_glyphs)
{
pdf_obj *fontdict, *descriptor;
pdf_obj *tmp_array;
int code, firstchar, lastchar;
double val;
int i;
int tfm_id;
char *usedchars;
double scaling;
fontdict = pdf_font_get_resource (font);
descriptor = pdf_font_get_descriptor(font);
usedchars = pdf_font_get_usedchars (font);
if (!cff_dict_known(cffont->topdict, "FontBBox")) {
ERROR("No FontBBox?");
}
if (cff_dict_known(cffont->topdict, "FontMatrix"))
scaling = 1000*cff_dict_get(cffont->topdict, "FontMatrix", 0);
else
scaling = 1;
tmp_array = texpdf_new_array();
for (i = 0; i < 4; i++) {
val = cff_dict_get(cffont->topdict, "FontBBox", i);
texpdf_add_array(tmp_array, texpdf_new_number(ROUND(val, 1.0)));
}
texpdf_add_dict(descriptor, texpdf_new_name("FontBBox"), tmp_array);
tmp_array = texpdf_new_array();
if (num_glyphs <= 1) {
firstchar = lastchar = 0;
texpdf_add_array(tmp_array, texpdf_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;
}
}
if (firstchar > lastchar) {
WARN("No glyphs actually used???");
texpdf_release_obj(tmp_array);
return;
}
tfm_id = texpdf_tfm_open(pdf_font_get_tfm_path(font), pdf_font_get_mapname(font), 0);
for (code = firstchar; code <= lastchar; code++) {
if (usedchars[code]) {
double width;
if (tfm_id < 0)
width = scaling * widths[cff_glyph_lookup(cffont, enc_vec[code])];
else
width = 1000. * texpdf_tfm_get_width(tfm_id, code);
texpdf_add_array(tmp_array,
texpdf_new_number(ROUND(width, 0.1)));
} else {
texpdf_add_array(tmp_array, texpdf_new_number(0.0));
}
}
}
if (texpdf_array_length(tmp_array) > 0) {
texpdf_add_dict(fontdict,
texpdf_new_name("Widths"), texpdf_ref_obj(tmp_array));
}
texpdf_release_obj(tmp_array);
texpdf_add_dict(fontdict,
texpdf_new_name("FirstChar"), texpdf_new_number(firstchar));
texpdf_add_dict(fontdict,
texpdf_new_name("LastChar"), texpdf_new_number(lastchar));
return;
}
static long
write_fontfile (pdf_font *font, cff_font *cffont)
{
pdf_obj *descriptor;
pdf_obj *fontfile, *stream_dict;
cff_index *topdict;
long private_size, stream_data_len, charstring_len;
long 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);
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);
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;
charstring_len = cff_index_size(cffont->cstrings);
stream_data_len = 4;
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);
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;
stream_data_ptr = NEW(stream_data_len, card8);
offset = 0;
offset += cff_put_header(cffont,
stream_data_ptr + offset, stream_data_len - offset);
offset += cff_pack_index(cffont->name,
stream_data_ptr + offset, stream_data_len - offset);
topdict_offset = offset;
offset += cff_index_size(topdict);
offset += cff_pack_index(cffont->string,
stream_data_ptr + offset, stream_data_len - offset);
offset += cff_pack_index(cffont->gsubr,
stream_data_ptr + offset, stream_data_len - offset);
cff_dict_set(cffont->topdict, "Encoding", 0, offset);
offset += cff_pack_encoding(cffont,
stream_data_ptr + offset, stream_data_len - offset);
cff_dict_set(cffont->topdict, "charset", 0, offset);
offset += cff_pack_charsets(cffont,
stream_data_ptr + offset, stream_data_len - offset);
cff_dict_set(cffont->topdict, "CharStrings", 0, offset);
offset += cff_pack_index(cffont->cstrings,
stream_data_ptr + offset, charstring_len);
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;
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);
fontfile = texpdf_new_stream(STREAM_COMPRESS);
stream_dict = texpdf_stream_dict(fontfile);
texpdf_add_dict(descriptor,
texpdf_new_name("FontFile3"), texpdf_ref_obj (fontfile));
texpdf_add_dict(stream_dict,
texpdf_new_name("Subtype"), texpdf_new_name("Type1C"));
texpdf_add_stream (fontfile, (void *) stream_data_ptr, offset);
texpdf_release_obj(fontfile);
RELEASE(stream_data_ptr);
return offset;
}
int
pdf_font_load_type1 (pdf_font *font)
{
pdf_obj *fontdict;
int encoding_id;
char *usedchars, *ident;
char *fontname, *uniqueTag;
char *fullname;
cff_font *cffont;
cff_charsets *charset;
char **enc_vec;
double defaultwidth, nominalwidth;
double *widths;
card16 *GIDMap, num_glyphs = 0;
FILE *fp;
long offset;
int code, verbose;
ASSERT(font);
if (!pdf_font_is_in_use(font)) {
return 0;
}
verbose = pdf_font_get_verbose();
encoding_id = pdf_font_get_encoding (font);
fontdict = pdf_font_get_resource (font);
pdf_font_get_descriptor(font);
usedchars = pdf_font_get_usedchars (font);
ident = pdf_font_get_ident (font);
fontname = pdf_font_get_fontname (font);
uniqueTag = pdf_font_get_uniqueTag (font);
if (!usedchars || !ident || !fontname) {
ERROR("Type1: Unexpected error.");
}
fp = DPXFOPEN(ident, DPX_RES_TYPE_T1FONT);
if (!fp) {
ERROR("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, fp);
if (!cffont) {
ERROR("Could not load Type 1 font: %s", ident);
}
DPXFCLOSE(fp);
fullname = NEW(strlen(fontname) + 8, char);
sprintf(fullname, "%6s+%s", uniqueTag, fontname);
if (encoding_id >= 0) {
enc_vec = pdf_encoding_get_encoding(encoding_id);
} else {
pdf_obj *tounicode;
if (!texpdf_lookup_dict(fontdict, "ToUnicode")) {
tounicode = pdf_create_ToUnicode_CMap(fullname,
enc_vec, usedchars);
if (tounicode) {
texpdf_add_dict(fontdict,
texpdf_new_name("ToUnicode"),
texpdf_ref_obj (tounicode));
texpdf_release_obj(tounicode);
}
}
}
cff_set_name(cffont, fullname);
RELEASE(fullname);
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;
}
#define MAX_GLYPHS 1024
GIDMap = NEW(MAX_GLYPHS, card16);
{
int prev, duplicate;
long 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)
ERROR("Type 1 font with no \".notdef\" glyph???");
GIDMap[0] = (card16) gid;
if (verbose > 2)
MESG("[glyphs:/.notdef");
num_glyphs = 1;
for (prev = -2, code = 0; code <= 0xff; code++) {
glyph = enc_vec[code];
if (!usedchars[code])
continue;
if (glyph && !strcmp(glyph, ".notdef")) {
WARN("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) {
WARN("Glyph \"%s\" missing in font \"%s\".", glyph, fontname);
usedchars[code] = 0;
continue;
}
for (duplicate = 0; duplicate < code; duplicate++) {
if (usedchars[duplicate] &&
enc_vec[duplicate] && !strcmp(enc_vec[duplicate], glyph))
break;
}
sid = cff_add_string(cffont, glyph, 1);
if (duplicate < code) {
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 (verbose > 2) {
MESG("/%s", glyph);
}
}
}
if (cffont->encoding->num_supps > 0) {
cffont->encoding->format |= 0x80;
} else {
RELEASE(cffont->encoding->supp);
cffont->encoding->supp = NULL;
}
}
widths = NEW(cffont->cstrings->count, double);
{
cff_index *cstring;
t1_ginfo gm;
card16 gid, gid_orig;
long dstlen_max, srclen;
card8 *srcptr, *dstptr;
offset = dstlen_max = 0L;
cstring = cff_new_index(cffont->cstrings->count);
cstring->data = NULL;
cstring->offset[0] = 1;
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) {
long bchar_gid, achar_gid, i;
const char *bchar_name, *achar_name;
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) {
WARN("Accent char \"%s\" not found. Invalid use of \"seac\" operator.",
achar_name);
continue;
}
if (bchar_gid < 0) {
WARN("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 (verbose > 2)
MESG("/%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;
}
for (i = 0; i < num_glyphs; i++) {
if (GIDMap[i] == bchar_gid)
break;
}
if (i == num_glyphs) {
if (verbose > 2)
MESG("/%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;
}
}
widths[gid] = gm.wx;
}
cstring->count = num_glyphs;
cff_release_index(cffont->subrs[0]);
cffont->subrs[0] = NULL;
RELEASE(cffont->subrs);
cffont->subrs = NULL;
cff_release_index(cffont->cstrings);
cffont->cstrings = cstring;
cff_release_charsets(cffont->charsets);
cffont->charsets = charset;
}
if (verbose > 2)
MESG("]");
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);
if (verbose > 1)
MESG("[%u glyphs][%ld bytes]", num_glyphs, offset);
cff_close(cffont);
if (encoding_id < 0 && enc_vec) {
for (code = 0; code < 256; code++) {
if (enc_vec[code])
RELEASE(enc_vec[code]);
enc_vec[code] = NULL;
}
RELEASE(enc_vec);
}
if (widths)
RELEASE(widths);
if (GIDMap)
RELEASE(GIDMap);
return 0;
}