#include "mupdf/pdf.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_XFREE86_H
static void pdf_load_font_descriptor(fz_context *ctx, pdf_document *doc, pdf_font_desc *fontdesc, pdf_obj *dict, char *collection, char *basefont, int iscidfont);
static const char *base_font_names[][10] =
{
{ "Courier", "CourierNew", "CourierNewPSMT", NULL },
{ "Courier-Bold", "CourierNew,Bold", "Courier,Bold",
"CourierNewPS-BoldMT", "CourierNew-Bold", NULL },
{ "Courier-Oblique", "CourierNew,Italic", "Courier,Italic",
"CourierNewPS-ItalicMT", "CourierNew-Italic", NULL },
{ "Courier-BoldOblique", "CourierNew,BoldItalic", "Courier,BoldItalic",
"CourierNewPS-BoldItalicMT", "CourierNew-BoldItalic", NULL },
{ "Helvetica", "ArialMT", "Arial", NULL },
{ "Helvetica-Bold", "Arial-BoldMT", "Arial,Bold", "Arial-Bold",
"Helvetica,Bold", NULL },
{ "Helvetica-Oblique", "Arial-ItalicMT", "Arial,Italic", "Arial-Italic",
"Helvetica,Italic", "Helvetica-Italic", NULL },
{ "Helvetica-BoldOblique", "Arial-BoldItalicMT",
"Arial,BoldItalic", "Arial-BoldItalic",
"Helvetica,BoldItalic", "Helvetica-BoldItalic", NULL },
{ "Times-Roman", "TimesNewRomanPSMT", "TimesNewRoman",
"TimesNewRomanPS", NULL },
{ "Times-Bold", "TimesNewRomanPS-BoldMT", "TimesNewRoman,Bold",
"TimesNewRomanPS-Bold", "TimesNewRoman-Bold", NULL },
{ "Times-Italic", "TimesNewRomanPS-ItalicMT", "TimesNewRoman,Italic",
"TimesNewRomanPS-Italic", "TimesNewRoman-Italic", NULL },
{ "Times-BoldItalic", "TimesNewRomanPS-BoldItalicMT",
"TimesNewRoman,BoldItalic", "TimesNewRomanPS-BoldItalic",
"TimesNewRoman-BoldItalic", NULL },
{ "Symbol", "Symbol,Italic", "Symbol,Bold", "Symbol,BoldItalic",
"SymbolMT", "SymbolMT,Italic", "SymbolMT,Bold", "SymbolMT,BoldItalic", NULL },
{ "ZapfDingbats", NULL }
};
static int is_dynalab(char *name)
{
if (strstr(name, "HuaTian"))
return 1;
if (strstr(name, "MingLi"))
return 1;
if ((strstr(name, "DF") == name) || strstr(name, "+DF"))
return 1;
if ((strstr(name, "DLC") == name) || strstr(name, "+DLC"))
return 1;
return 0;
}
static int strcmp_ignore_space(const char *a, const char *b)
{
while (1)
{
while (*a == ' ')
a++;
while (*b == ' ')
b++;
if (*a != *b)
return 1;
if (*a == 0)
return *a != *b;
if (*b == 0)
return *a != *b;
a++;
b++;
}
}
static const char *clean_font_name(const char *fontname)
{
int i, k;
for (i = 0; i < nelem(base_font_names); i++)
for (k = 0; base_font_names[i][k]; k++)
if (!strcmp_ignore_space(base_font_names[i][k], fontname))
return base_font_names[i][0];
return fontname;
}
enum { UNKNOWN, TYPE1, TRUETYPE };
static int ft_kind(FT_Face face)
{
const char *kind = FT_Get_X11_Font_Format(face);
if (!strcmp(kind, "TrueType"))
return TRUETYPE;
if (!strcmp(kind, "Type 1"))
return TYPE1;
if (!strcmp(kind, "CFF"))
return TYPE1;
if (!strcmp(kind, "CID Type 1"))
return TYPE1;
return UNKNOWN;
}
static int ft_is_bold(FT_Face face)
{
return face->style_flags & FT_STYLE_FLAG_BOLD;
}
static int ft_is_italic(FT_Face face)
{
return face->style_flags & FT_STYLE_FLAG_ITALIC;
}
static int ft_char_index(FT_Face face, int cid)
{
int gid = FT_Get_Char_Index(face, cid);
if (gid == 0)
gid = FT_Get_Char_Index(face, 0xf000 + cid);
if (gid == 0 && cid == 0x22ef)
gid = FT_Get_Char_Index(face, 0x2026);
return gid;
}
static int ft_cid_to_gid(pdf_font_desc *fontdesc, int cid)
{
if (fontdesc->to_ttf_cmap)
{
cid = pdf_lookup_cmap(fontdesc->to_ttf_cmap, cid);
return ft_char_index(fontdesc->font->ft_face, cid);
}
if (fontdesc->cid_to_gid && cid < fontdesc->cid_to_gid_len && cid >= 0)
return fontdesc->cid_to_gid[cid];
return cid;
}
int
pdf_font_cid_to_gid(fz_context *ctx, pdf_font_desc *fontdesc, int cid)
{
if (fontdesc->font->ft_face)
return ft_cid_to_gid(fontdesc, cid);
return cid;
}
static int ft_width(fz_context *ctx, pdf_font_desc *fontdesc, int cid)
{
int gid = ft_cid_to_gid(fontdesc, cid);
int fterr;
fterr = FT_Load_Glyph(fontdesc->font->ft_face, gid,
FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
if (fterr)
{
fz_warn(ctx, "freetype load glyph (gid %d): %s", gid, ft_error_string(fterr));
return 0;
}
return ((FT_Face)fontdesc->font->ft_face)->glyph->advance.x;
}
static int lookup_mre_code(char *name)
{
int i;
for (i = 0; i < 256; i++)
if (pdf_mac_roman[i] && !strcmp(name, pdf_mac_roman[i]))
return i;
return -1;
}
static void
pdf_load_builtin_font(fz_context *ctx, pdf_font_desc *fontdesc, char *fontname, int has_descriptor)
{
FT_Face face;
const char *clean_name = clean_font_name(fontname);
fontdesc->font = fz_load_system_font(ctx, fontname, 0, 0, !has_descriptor);
if (!fontdesc->font)
{
unsigned char *data;
unsigned int len;
data = pdf_lookup_builtin_font(ctx, clean_name, &len);
if (!data)
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find builtin font: '%s'", fontname);
fontdesc->font = fz_new_font_from_memory(ctx, fontname, data, len, 0, 1);
}
if (!strcmp(clean_name, "Symbol") || !strcmp(clean_name, "ZapfDingbats"))
fontdesc->flags |= PDF_FD_SYMBOLIC;
face = fontdesc->font->ft_face;
fontdesc->ascent = 1000.0f * face->ascender / face->units_per_EM;
fontdesc->descent = 1000.0f * face->descender / face->units_per_EM;
}
static void
pdf_load_substitute_font(fz_context *ctx, pdf_font_desc *fontdesc, char *fontname, int mono, int serif, int bold, int italic)
{
fontdesc->font = fz_load_system_font(ctx, fontname, bold, italic, 0);
if (!fontdesc->font)
{
unsigned char *data;
unsigned int len;
data = pdf_lookup_substitute_font(ctx, mono, serif, bold, italic, &len);
if (!data)
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find substitute font");
fontdesc->font = fz_new_font_from_memory(ctx, fontname, data, len, 0, 1);
fontdesc->font->ft_bold = bold && !ft_is_bold(fontdesc->font->ft_face);
fontdesc->font->ft_italic = italic && !ft_is_italic(fontdesc->font->ft_face);
}
fontdesc->font->ft_substitute = 1;
}
static void
pdf_load_substitute_cjk_font(fz_context *ctx, pdf_font_desc *fontdesc, char *fontname, int ros, int serif)
{
fontdesc->font = fz_load_system_cjk_font(ctx, fontname, ros, serif);
if (!fontdesc->font)
{
unsigned char *data;
unsigned int len;
int index;
data = pdf_lookup_substitute_cjk_font(ctx, ros, serif, fontdesc->wmode, &len, &index);
if (!data)
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find builtin CJK font");
fontdesc->font = fz_new_font_from_memory(ctx, fontname, data, len, index, 0);
}
fontdesc->font->ft_substitute = 1;
}
static void
pdf_load_system_font(fz_context *ctx, pdf_font_desc *fontdesc, char *fontname, char *collection)
{
int bold = 0;
int italic = 0;
int serif = 0;
int mono = 0;
if (strstr(fontname, "Bold"))
bold = 1;
if (strstr(fontname, "Italic"))
italic = 1;
if (strstr(fontname, "Oblique"))
italic = 1;
if (fontdesc->flags & PDF_FD_FIXED_PITCH)
mono = 1;
if (fontdesc->flags & PDF_FD_SERIF)
serif = 1;
if (fontdesc->flags & PDF_FD_ITALIC)
italic = 1;
if (fontdesc->flags & PDF_FD_FORCE_BOLD)
bold = 1;
if (collection)
{
if (!strcmp(collection, "Adobe-CNS1"))
pdf_load_substitute_cjk_font(ctx, fontdesc, fontname, FZ_ADOBE_CNS_1, serif);
else if (!strcmp(collection, "Adobe-GB1"))
pdf_load_substitute_cjk_font(ctx, fontdesc, fontname, FZ_ADOBE_GB_1, serif);
else if (!strcmp(collection, "Adobe-Japan1"))
pdf_load_substitute_cjk_font(ctx, fontdesc, fontname, FZ_ADOBE_JAPAN_1, serif);
else if (!strcmp(collection, "Adobe-Korea1"))
pdf_load_substitute_cjk_font(ctx, fontdesc, fontname, FZ_ADOBE_KOREA_1, serif);
else
{
if (strcmp(collection, "Adobe-Identity") != 0)
fz_warn(ctx, "unknown cid collection: %s", collection);
pdf_load_substitute_font(ctx, fontdesc, fontname, mono, serif, bold, italic);
}
}
else
{
pdf_load_substitute_font(ctx, fontdesc, fontname, mono, serif, bold, italic);
}
}
static void
pdf_load_embedded_font(fz_context *ctx, pdf_document *doc, pdf_font_desc *fontdesc, char *fontname, pdf_obj *stmref)
{
fz_buffer *buf;
fz_try(ctx)
{
buf = pdf_load_stream(ctx, doc, pdf_to_num(ctx, stmref), pdf_to_gen(ctx, stmref));
}
fz_catch(ctx)
{
fz_rethrow_message(ctx, "cannot load font stream (%d %d R)", pdf_to_num(ctx, stmref), pdf_to_gen(ctx, stmref));
}
fz_try(ctx)
{
fontdesc->font = fz_new_font_from_buffer(ctx, fontname, buf, 0, 1);
}
fz_always(ctx)
{
fz_drop_buffer(ctx, buf);
}
fz_catch(ctx)
{
fz_rethrow_message(ctx, "cannot load embedded font (%d %d R)", pdf_to_num(ctx, stmref), pdf_to_gen(ctx, stmref));
}
fontdesc->size += buf->len;
fontdesc->is_embedded = 1;
}
pdf_font_desc *
pdf_keep_font(fz_context *ctx, pdf_font_desc *fontdesc)
{
return (pdf_font_desc *)fz_keep_storable(ctx, &fontdesc->storable);
}
void
pdf_drop_font(fz_context *ctx, pdf_font_desc *fontdesc)
{
fz_drop_storable(ctx, &fontdesc->storable);
}
static void
pdf_drop_font_imp(fz_context *ctx, fz_storable *fontdesc_)
{
pdf_font_desc *fontdesc = (pdf_font_desc *)fontdesc_;
if (fontdesc->font)
fz_drop_font(ctx, fontdesc->font);
if (fontdesc->encoding)
pdf_drop_cmap(ctx, fontdesc->encoding);
if (fontdesc->to_ttf_cmap)
pdf_drop_cmap(ctx, fontdesc->to_ttf_cmap);
if (fontdesc->to_unicode)
pdf_drop_cmap(ctx, fontdesc->to_unicode);
fz_free(ctx, fontdesc->cid_to_gid);
fz_free(ctx, fontdesc->cid_to_ucs);
fz_free(ctx, fontdesc->hmtx);
fz_free(ctx, fontdesc->vmtx);
fz_free(ctx, fontdesc);
}
pdf_font_desc *
pdf_new_font_desc(fz_context *ctx)
{
pdf_font_desc *fontdesc;
fontdesc = fz_malloc_struct(ctx, pdf_font_desc);
FZ_INIT_STORABLE(fontdesc, 1, pdf_drop_font_imp);
fontdesc->size = sizeof(pdf_font_desc);
fontdesc->font = NULL;
fontdesc->flags = 0;
fontdesc->italic_angle = 0;
fontdesc->ascent = 0;
fontdesc->descent = 0;
fontdesc->cap_height = 0;
fontdesc->x_height = 0;
fontdesc->missing_width = 0;
fontdesc->encoding = NULL;
fontdesc->to_ttf_cmap = NULL;
fontdesc->cid_to_gid_len = 0;
fontdesc->cid_to_gid = NULL;
fontdesc->to_unicode = NULL;
fontdesc->cid_to_ucs_len = 0;
fontdesc->cid_to_ucs = NULL;
fontdesc->wmode = 0;
fontdesc->hmtx_cap = 0;
fontdesc->vmtx_cap = 0;
fontdesc->hmtx_len = 0;
fontdesc->vmtx_len = 0;
fontdesc->hmtx = NULL;
fontdesc->vmtx = NULL;
fontdesc->dhmtx.lo = 0x0000;
fontdesc->dhmtx.hi = 0xFFFF;
fontdesc->dhmtx.w = 1000;
fontdesc->dvmtx.lo = 0x0000;
fontdesc->dvmtx.hi = 0xFFFF;
fontdesc->dvmtx.x = 0;
fontdesc->dvmtx.y = 880;
fontdesc->dvmtx.w = -1000;
fontdesc->is_embedded = 0;
return fontdesc;
}
static pdf_font_desc *
pdf_load_simple_font_by_name(fz_context *ctx, pdf_document *doc, pdf_obj *dict, char *basefont)
{
pdf_obj *descriptor;
pdf_obj *encoding;
pdf_obj *widths;
unsigned short *etable = NULL;
pdf_font_desc *fontdesc = NULL;
pdf_obj *subtype;
FT_Face face;
FT_CharMap cmap;
int symbolic;
int kind;
char *estrings[256];
char ebuffer[256][32];
int i, k, n;
int fterr;
int has_lock = 0;
fz_var(fontdesc);
fz_var(etable);
fz_var(has_lock);
fz_try(ctx)
{
fontdesc = pdf_new_font_desc(ctx);
descriptor = pdf_dict_get(ctx, dict, PDF_NAME_FontDescriptor);
if (descriptor)
pdf_load_font_descriptor(ctx, doc, fontdesc, descriptor, NULL, basefont, 0);
else
pdf_load_builtin_font(ctx, fontdesc, basefont, 0);
if (descriptor && pdf_is_string(ctx, pdf_dict_get(ctx, descriptor, PDF_NAME_FontName)) &&
!pdf_dict_get(ctx, dict, PDF_NAME_ToUnicode) &&
pdf_name_eq(ctx, pdf_dict_get(ctx, dict, PDF_NAME_Encoding), PDF_NAME_WinAnsiEncoding) &&
pdf_to_int(ctx, pdf_dict_get(ctx, descriptor, PDF_NAME_Flags)) == 4)
{
char *cp936fonts[] = {
"\xCB\xCE\xCC\xE5", "SimSun,Regular",
"\xBA\xDA\xCC\xE5", "SimHei,Regular",
"\xBF\xAC\xCC\xE5_GB2312", "SimKai,Regular",
"\xB7\xC2\xCB\xCE_GB2312", "SimFang,Regular",
"\xC1\xA5\xCA\xE9", "SimLi,Regular",
NULL
};
for (i = 0; cp936fonts[i]; i += 2)
if (!strcmp(basefont, cp936fonts[i]))
break;
if (cp936fonts[i])
{
fz_warn(ctx, "workaround for S22PDF lying about chinese font encodings");
pdf_drop_font(ctx, fontdesc);
fontdesc = NULL;
fontdesc = pdf_new_font_desc(ctx);
pdf_load_font_descriptor(ctx, doc, fontdesc, descriptor, "Adobe-GB1", cp936fonts[i+1], 0);
fontdesc->encoding = pdf_load_system_cmap(ctx, "GBK-EUC-H");
fontdesc->to_unicode = pdf_load_system_cmap(ctx, "Adobe-GB1-UCS2");
fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-GB1-UCS2");
face = fontdesc->font->ft_face;
goto skip_encoding;
}
}
face = fontdesc->font->ft_face;
kind = ft_kind(face);
symbolic = fontdesc->flags & 4;
if (face->num_charmaps > 0)
cmap = face->charmaps[0];
else
cmap = NULL;
for (i = 0; i < face->num_charmaps; i++)
{
FT_CharMap test = face->charmaps[i];
if (kind == TYPE1)
{
if (test->platform_id == 7)
cmap = test;
}
if (kind == TRUETYPE)
{
if (test->platform_id == 1 && test->encoding_id == 0)
cmap = test;
if (test->platform_id == 3 && test->encoding_id == 1)
cmap = test;
if (symbolic && test->platform_id == 3 && test->encoding_id == 0)
cmap = test;
}
}
if (cmap)
{
fterr = FT_Set_Charmap(face, cmap);
if (fterr)
fz_warn(ctx, "freetype could not set cmap: %s", ft_error_string(fterr));
}
else
fz_warn(ctx, "freetype could not find any cmaps");
etable = fz_malloc_array(ctx, 256, sizeof(unsigned short));
fontdesc->size += 256 * sizeof(unsigned short);
for (i = 0; i < 256; i++)
{
estrings[i] = NULL;
etable[i] = 0;
}
encoding = pdf_dict_get(ctx, dict, PDF_NAME_Encoding);
if (encoding)
{
if (pdf_is_name(ctx, encoding))
pdf_load_encoding(estrings, pdf_to_name(ctx, encoding));
if (pdf_is_dict(ctx, encoding))
{
pdf_obj *base, *diff, *item;
base = pdf_dict_get(ctx, encoding, PDF_NAME_BaseEncoding);
if (pdf_is_name(ctx, base))
pdf_load_encoding(estrings, pdf_to_name(ctx, base));
else if (!fontdesc->is_embedded && !symbolic)
pdf_load_encoding(estrings, "StandardEncoding");
diff = pdf_dict_get(ctx, encoding, PDF_NAME_Differences);
if (pdf_is_array(ctx, diff))
{
n = pdf_array_len(ctx, diff);
k = 0;
for (i = 0; i < n; i++)
{
item = pdf_array_get(ctx, diff, i);
if (pdf_is_int(ctx, item))
k = pdf_to_int(ctx, item);
if (pdf_is_name(ctx, item) && k >= 0 && k < nelem(estrings))
estrings[k++] = pdf_to_name(ctx, item);
}
}
}
}
else if (!fontdesc->is_embedded && !symbolic)
pdf_load_encoding(estrings, "StandardEncoding");
for (i = 0; i < 256; i++)
etable[i] = ft_char_index(face, i);
fz_lock(ctx, FZ_LOCK_FREETYPE);
has_lock = 1;
subtype = pdf_dict_get(ctx, dict, PDF_NAME_Subtype);
if (pdf_name_eq(ctx, subtype, PDF_NAME_Type1))
kind = TYPE1;
else if (pdf_name_eq(ctx, subtype, PDF_NAME_MMType1))
kind = TYPE1;
else if (pdf_name_eq(ctx, subtype, PDF_NAME_TrueType))
kind = TRUETYPE;
else if (pdf_name_eq(ctx, subtype, PDF_NAME_CIDFontType0))
kind = TYPE1;
else if (pdf_name_eq(ctx, subtype, PDF_NAME_CIDFontType2))
kind = TRUETYPE;
if (kind == TYPE1)
{
for (i = 0; i < 256; i++)
{
if (estrings[i])
{
etable[i] = FT_Get_Name_Index(face, estrings[i]);
if (etable[i] == 0)
{
int aglcode = pdf_lookup_agl(estrings[i]);
const char **dupnames = pdf_lookup_agl_duplicates(aglcode);
while (*dupnames)
{
etable[i] = FT_Get_Name_Index(face, (char*)*dupnames);
if (etable[i])
break;
dupnames++;
}
if (etable[i] == 0)
{
char buf[10];
sprintf(buf, "uni%04X", aglcode);
etable[i] = FT_Get_Name_Index(face, buf);
}
}
}
}
}
if (kind == TRUETYPE)
{
if (!symbolic && face->charmap && face->charmap->platform_id == 3)
{
for (i = 0; i < 256; i++)
{
if (estrings[i])
{
int aglcode = pdf_lookup_agl(estrings[i]);
if (!aglcode)
etable[i] = FT_Get_Name_Index(face, estrings[i]);
else
etable[i] = ft_char_index(face, aglcode);
}
}
}
else if (!symbolic && face->charmap && face->charmap->platform_id == 1)
{
for (i = 0; i < 256; i++)
{
if (estrings[i])
{
k = lookup_mre_code(estrings[i]);
if (k <= 0)
etable[i] = FT_Get_Name_Index(face, estrings[i]);
else
etable[i] = ft_char_index(face, k);
}
}
}
else if (!face->charmap || face->charmap->encoding != FT_ENCODING_MS_SYMBOL)
{
for (i = 0; i < 256; i++)
{
if (estrings[i])
{
etable[i] = FT_Get_Name_Index(face, estrings[i]);
if (etable[i] == 0)
etable[i] = ft_char_index(face, i);
}
}
}
}
for (i = 0; i < 256; i++)
{
if (etable[i] && !estrings[i])
{
if (FT_HAS_GLYPH_NAMES(face))
{
fterr = FT_Get_Glyph_Name(face, etable[i], ebuffer[i], 32);
if (fterr)
fz_warn(ctx, "freetype get glyph name (gid %d): %s", etable[i], ft_error_string(fterr));
if (ebuffer[i][0])
estrings[i] = ebuffer[i];
}
else
{
estrings[i] = (char*) pdf_win_ansi[i];
}
}
}
if (kind == TYPE1 && symbolic)
{
for (i = 0; i < 256; i++)
if (etable[i] && estrings[i] && !pdf_lookup_agl(estrings[i]))
estrings[i] = (char*) pdf_standard[i];
}
fz_unlock(ctx, FZ_LOCK_FREETYPE);
has_lock = 0;
fontdesc->encoding = pdf_new_identity_cmap(ctx, 0, 1);
fontdesc->size += pdf_cmap_size(ctx, fontdesc->encoding);
fontdesc->cid_to_gid_len = 256;
fontdesc->cid_to_gid = etable;
fz_try(ctx)
{
pdf_load_to_unicode(ctx, doc, fontdesc, estrings, NULL, pdf_dict_get(ctx, dict, PDF_NAME_ToUnicode));
}
fz_catch(ctx)
{
fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
fz_warn(ctx, "cannot load ToUnicode CMap");
}
skip_encoding:
pdf_set_default_hmtx(ctx, fontdesc, fontdesc->missing_width);
widths = pdf_dict_get(ctx, dict, PDF_NAME_Widths);
if (widths)
{
int first, last;
first = pdf_to_int(ctx, pdf_dict_get(ctx, dict, PDF_NAME_FirstChar));
last = pdf_to_int(ctx, pdf_dict_get(ctx, dict, PDF_NAME_LastChar));
if (first < 0 || last > 255 || first > last)
first = last = 0;
for (i = 0; i < last - first + 1; i++)
{
int wid = pdf_to_int(ctx, pdf_array_get(ctx, widths, i));
pdf_add_hmtx(ctx, fontdesc, i + first, i + first, wid);
}
}
else
{
fz_lock(ctx, FZ_LOCK_FREETYPE);
has_lock = 1;
fterr = FT_Set_Char_Size(face, 1000, 1000, 72, 72);
if (fterr)
fz_warn(ctx, "freetype set character size: %s", ft_error_string(fterr));
for (i = 0; i < 256; i++)
{
pdf_add_hmtx(ctx, fontdesc, i, i, ft_width(ctx, fontdesc, i));
}
fz_unlock(ctx, FZ_LOCK_FREETYPE);
has_lock = 0;
}
pdf_end_hmtx(ctx, fontdesc);
}
fz_catch(ctx)
{
if (has_lock)
fz_unlock(ctx, FZ_LOCK_FREETYPE);
if (fontdesc && etable != fontdesc->cid_to_gid)
fz_free(ctx, etable);
pdf_drop_font(ctx, fontdesc);
fz_rethrow_message(ctx, "cannot load simple font (%d %d R)", pdf_to_num(ctx, dict), pdf_to_gen(ctx, dict));
}
return fontdesc;
}
static pdf_font_desc *
pdf_load_simple_font(fz_context *ctx, pdf_document *doc, pdf_obj *dict)
{
char *basefont = pdf_to_name(ctx, pdf_dict_get(ctx, dict, PDF_NAME_BaseFont));
return pdf_load_simple_font_by_name(ctx, doc, dict, basefont);
}
static int
hail_mary_make_hash_key(fz_context *ctx, fz_store_hash *hash, void *key_)
{
hash->u.i.i0 = 0;
hash->u.i.i1 = 0;
hash->u.i.ptr = NULL;
return 1;
}
static void *
hail_mary_keep_key(fz_context *ctx, void *key)
{
return key;
}
static void
hail_mary_drop_key(fz_context *ctx, void *key)
{
}
static int
hail_mary_cmp_key(fz_context *ctx, void *k0, void *k1)
{
return k0 == k1;
}
#ifndef NDEBUG
static void
hail_mary_debug_key(fz_context *ctx, FILE *out, void *key_)
{
fprintf(out, "hail mary ");
}
#endif
static fz_store_type hail_mary_store_type =
{
hail_mary_make_hash_key,
hail_mary_keep_key,
hail_mary_drop_key,
hail_mary_cmp_key,
#ifndef NDEBUG
hail_mary_debug_key
#endif
};
pdf_font_desc *
pdf_load_hail_mary_font(fz_context *ctx, pdf_document *doc)
{
pdf_font_desc *fontdesc;
pdf_font_desc *existing;
if ((fontdesc = fz_find_item(ctx, pdf_drop_font_imp, &hail_mary_store_type, &hail_mary_store_type)) != NULL)
{
return fontdesc;
}
fontdesc = pdf_load_simple_font_by_name(ctx, doc, NULL, "Helvetica");
existing = fz_store_item(ctx, &hail_mary_store_type, fontdesc, fontdesc->size, &hail_mary_store_type);
assert(existing == NULL);
return fontdesc;
}
static pdf_font_desc *
load_cid_font(fz_context *ctx, pdf_document *doc, pdf_obj *dict, pdf_obj *encoding, pdf_obj *to_unicode)
{
pdf_obj *widths;
pdf_obj *descriptor;
pdf_font_desc *fontdesc = NULL;
pdf_cmap *cmap;
FT_Face face;
char collection[256];
char *basefont;
int i, k, fterr;
pdf_obj *cidtogidmap;
pdf_obj *obj;
int dw;
fz_var(fontdesc);
fz_try(ctx)
{
basefont = pdf_to_name(ctx, pdf_dict_get(ctx, dict, PDF_NAME_BaseFont));
{
pdf_obj *cidinfo;
char tmpstr[64];
int tmplen;
cidinfo = pdf_dict_get(ctx, dict, PDF_NAME_CIDSystemInfo);
if (!cidinfo)
fz_throw(ctx, FZ_ERROR_GENERIC, "cid font is missing info");
obj = pdf_dict_get(ctx, cidinfo, PDF_NAME_Registry);
tmplen = fz_mini(sizeof tmpstr - 1, pdf_to_str_len(ctx, obj));
memcpy(tmpstr, pdf_to_str_buf(ctx, obj), tmplen);
tmpstr[tmplen] = '\0';
fz_strlcpy(collection, tmpstr, sizeof collection);
fz_strlcat(collection, "-", sizeof collection);
obj = pdf_dict_get(ctx, cidinfo, PDF_NAME_Ordering);
tmplen = fz_mini(sizeof tmpstr - 1, pdf_to_str_len(ctx, obj));
memcpy(tmpstr, pdf_to_str_buf(ctx, obj), tmplen);
tmpstr[tmplen] = '\0';
fz_strlcat(collection, tmpstr, sizeof collection);
}
if (pdf_is_name(ctx, encoding))
{
if (pdf_name_eq(ctx, encoding, PDF_NAME_Identity_H))
cmap = pdf_new_identity_cmap(ctx, 0, 2);
else if (pdf_name_eq(ctx, encoding, PDF_NAME_Identity_V))
cmap = pdf_new_identity_cmap(ctx, 1, 2);
else
cmap = pdf_load_system_cmap(ctx, pdf_to_name(ctx, encoding));
}
else if (pdf_is_indirect(ctx, encoding))
{
cmap = pdf_load_embedded_cmap(ctx, doc, encoding);
}
else
{
fz_throw(ctx, FZ_ERROR_GENERIC, "syntaxerror: font missing encoding");
}
fontdesc = pdf_new_font_desc(ctx);
fontdesc->encoding = cmap;
fontdesc->size += pdf_cmap_size(ctx, fontdesc->encoding);
pdf_set_font_wmode(ctx, fontdesc, pdf_cmap_wmode(ctx, fontdesc->encoding));
descriptor = pdf_dict_get(ctx, dict, PDF_NAME_FontDescriptor);
if (!descriptor)
fz_throw(ctx, FZ_ERROR_GENERIC, "syntaxerror: missing font descriptor");
pdf_load_font_descriptor(ctx, doc, fontdesc, descriptor, collection, basefont, 1);
face = fontdesc->font->ft_face;
cidtogidmap = pdf_dict_get(ctx, dict, PDF_NAME_CIDToGIDMap);
if (pdf_is_indirect(ctx, cidtogidmap))
{
fz_buffer *buf;
buf = pdf_load_stream(ctx, doc, pdf_to_num(ctx, cidtogidmap), pdf_to_gen(ctx, cidtogidmap));
fontdesc->cid_to_gid_len = (buf->len) / 2;
fontdesc->cid_to_gid = fz_malloc_array(ctx, fontdesc->cid_to_gid_len, sizeof(unsigned short));
fontdesc->size += fontdesc->cid_to_gid_len * sizeof(unsigned short);
for (i = 0; i < fontdesc->cid_to_gid_len; i++)
fontdesc->cid_to_gid[i] = (buf->data[i * 2] << 8) + buf->data[i * 2 + 1];
fz_drop_buffer(ctx, buf);
}
else if (fontdesc->font->ft_substitute)
{
fterr = FT_Select_Charmap(face, ft_encoding_unicode);
if (fterr)
{
fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror: no unicode cmap when emulating CID font: %s", ft_error_string(fterr));
}
if (!strcmp(collection, "Adobe-CNS1"))
fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-CNS1-UCS2");
else if (!strcmp(collection, "Adobe-GB1"))
fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-GB1-UCS2");
else if (!strcmp(collection, "Adobe-Japan1"))
fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-Japan1-UCS2");
else if (!strcmp(collection, "Adobe-Japan2"))
fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-Japan2-UCS2");
else if (!strcmp(collection, "Adobe-Korea1"))
fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-Korea1-UCS2");
}
pdf_load_to_unicode(ctx, doc, fontdesc, NULL, collection, to_unicode);
if (strstr(fontdesc->encoding->cmap_name, "Identity-") && fontdesc->font->ft_substitute)
{
fz_warn(ctx, "non-embedded font using identity encoding: %s", basefont);
if (fontdesc->to_unicode && !fontdesc->to_ttf_cmap)
fontdesc->to_ttf_cmap = pdf_keep_cmap(ctx, fontdesc->to_unicode);
}
dw = 1000;
obj = pdf_dict_get(ctx, dict, PDF_NAME_DW);
if (obj)
dw = pdf_to_int(ctx, obj);
pdf_set_default_hmtx(ctx, fontdesc, dw);
widths = pdf_dict_get(ctx, dict, PDF_NAME_W);
if (widths)
{
int c0, c1, w, n, m;
n = pdf_array_len(ctx, widths);
for (i = 0; i < n; )
{
c0 = pdf_to_int(ctx, pdf_array_get(ctx, widths, i));
obj = pdf_array_get(ctx, widths, i + 1);
if (pdf_is_array(ctx, obj))
{
m = pdf_array_len(ctx, obj);
for (k = 0; k < m; k++)
{
w = pdf_to_int(ctx, pdf_array_get(ctx, obj, k));
pdf_add_hmtx(ctx, fontdesc, c0 + k, c0 + k, w);
}
i += 2;
}
else
{
c1 = pdf_to_int(ctx, obj);
w = pdf_to_int(ctx, pdf_array_get(ctx, widths, i + 2));
pdf_add_hmtx(ctx, fontdesc, c0, c1, w);
i += 3;
}
}
}
pdf_end_hmtx(ctx, fontdesc);
if (pdf_cmap_wmode(ctx, fontdesc->encoding) == 1)
{
int dw2y = 880;
int dw2w = -1000;
obj = pdf_dict_get(ctx, dict, PDF_NAME_DW2);
if (obj)
{
dw2y = pdf_to_int(ctx, pdf_array_get(ctx, obj, 0));
dw2w = pdf_to_int(ctx, pdf_array_get(ctx, obj, 1));
}
pdf_set_default_vmtx(ctx, fontdesc, dw2y, dw2w);
widths = pdf_dict_get(ctx, dict, PDF_NAME_W2);
if (widths)
{
int c0, c1, w, x, y, n;
n = pdf_array_len(ctx, widths);
for (i = 0; i < n; )
{
c0 = pdf_to_int(ctx, pdf_array_get(ctx, widths, i));
obj = pdf_array_get(ctx, widths, i + 1);
if (pdf_is_array(ctx, obj))
{
int m = pdf_array_len(ctx, obj);
for (k = 0; k * 3 < m; k ++)
{
w = pdf_to_int(ctx, pdf_array_get(ctx, obj, k * 3 + 0));
x = pdf_to_int(ctx, pdf_array_get(ctx, obj, k * 3 + 1));
y = pdf_to_int(ctx, pdf_array_get(ctx, obj, k * 3 + 2));
pdf_add_vmtx(ctx, fontdesc, c0 + k, c0 + k, x, y, w);
}
i += 2;
}
else
{
c1 = pdf_to_int(ctx, obj);
w = pdf_to_int(ctx, pdf_array_get(ctx, widths, i + 2));
x = pdf_to_int(ctx, pdf_array_get(ctx, widths, i + 3));
y = pdf_to_int(ctx, pdf_array_get(ctx, widths, i + 4));
pdf_add_vmtx(ctx, fontdesc, c0, c1, x, y, w);
i += 5;
}
}
}
pdf_end_vmtx(ctx, fontdesc);
}
}
fz_catch(ctx)
{
pdf_drop_font(ctx, fontdesc);
fz_rethrow_message(ctx, "cannot load cid font (%d %d R)", pdf_to_num(ctx, dict), pdf_to_gen(ctx, dict));
}
return fontdesc;
}
static pdf_font_desc *
pdf_load_type0_font(fz_context *ctx, pdf_document *doc, pdf_obj *dict)
{
pdf_obj *dfonts;
pdf_obj *dfont;
pdf_obj *subtype;
pdf_obj *encoding;
pdf_obj *to_unicode;
dfonts = pdf_dict_get(ctx, dict, PDF_NAME_DescendantFonts);
if (!dfonts)
fz_throw(ctx, FZ_ERROR_GENERIC, "cid font is missing descendant fonts");
dfont = pdf_array_get(ctx, dfonts, 0);
subtype = pdf_dict_get(ctx, dfont, PDF_NAME_Subtype);
encoding = pdf_dict_get(ctx, dict, PDF_NAME_Encoding);
to_unicode = pdf_dict_get(ctx, dict, PDF_NAME_ToUnicode);
if (pdf_is_name(ctx, subtype) && pdf_name_eq(ctx, subtype, PDF_NAME_CIDFontType0))
return load_cid_font(ctx, doc, dfont, encoding, to_unicode);
if (pdf_is_name(ctx, subtype) && pdf_name_eq(ctx, subtype, PDF_NAME_CIDFontType2))
return load_cid_font(ctx, doc, dfont, encoding, to_unicode);
fz_throw(ctx, FZ_ERROR_GENERIC, "syntaxerror: unknown cid font type");
}
static void
pdf_load_font_descriptor(fz_context *ctx, pdf_document *doc, pdf_font_desc *fontdesc, pdf_obj *dict, char *collection, char *basefont, int iscidfont)
{
pdf_obj *obj1, *obj2, *obj3, *obj;
char *fontname;
FT_Face face;
fontname = basefont;
fontdesc->flags = pdf_to_int(ctx, pdf_dict_get(ctx, dict, PDF_NAME_Flags));
fontdesc->italic_angle = pdf_to_real(ctx, pdf_dict_get(ctx, dict, PDF_NAME_ItalicAngle));
fontdesc->ascent = pdf_to_real(ctx, pdf_dict_get(ctx, dict, PDF_NAME_Ascent));
fontdesc->descent = pdf_to_real(ctx, pdf_dict_get(ctx, dict, PDF_NAME_Descent));
fontdesc->cap_height = pdf_to_real(ctx, pdf_dict_get(ctx, dict, PDF_NAME_CapHeight));
fontdesc->x_height = pdf_to_real(ctx, pdf_dict_get(ctx, dict, PDF_NAME_XHeight));
fontdesc->missing_width = pdf_to_real(ctx, pdf_dict_get(ctx, dict, PDF_NAME_MissingWidth));
obj1 = pdf_dict_get(ctx, dict, PDF_NAME_FontFile);
obj2 = pdf_dict_get(ctx, dict, PDF_NAME_FontFile2);
obj3 = pdf_dict_get(ctx, dict, PDF_NAME_FontFile3);
obj = obj1 ? obj1 : obj2 ? obj2 : obj3;
if (pdf_is_indirect(ctx, obj))
{
fz_try(ctx)
{
pdf_load_embedded_font(ctx, doc, fontdesc, fontname, obj);
}
fz_catch(ctx)
{
fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
fz_warn(ctx, "ignored error when loading embedded font; attempting to load system font");
if (!iscidfont && fontname != clean_font_name(fontname))
pdf_load_builtin_font(ctx, fontdesc, fontname, 1);
else
pdf_load_system_font(ctx, fontdesc, fontname, collection);
}
}
else
{
if (!iscidfont && fontname != clean_font_name(fontname))
pdf_load_builtin_font(ctx, fontdesc, fontname, 1);
else
pdf_load_system_font(ctx, fontdesc, fontname, collection);
}
face = fontdesc->font->ft_face;
if (ft_kind(face) == TRUETYPE)
{
if (FT_IS_TRICKY(face) || is_dynalab(fontdesc->font->name))
fontdesc->font->ft_hint = 1;
if (fontdesc->ascent == 0.0f)
fontdesc->ascent = 1000.0f * face->ascender / face->units_per_EM;
if (fontdesc->descent == 0.0f)
fontdesc->descent = 1000.0f * face->descender / face->units_per_EM;
}
}
static void
pdf_make_width_table(fz_context *ctx, pdf_font_desc *fontdesc)
{
fz_font *font = fontdesc->font;
int i, k, n, cid, gid;
n = 0;
for (i = 0; i < fontdesc->hmtx_len; i++)
{
for (k = fontdesc->hmtx[i].lo; k <= fontdesc->hmtx[i].hi; k++)
{
cid = pdf_lookup_cmap(fontdesc->encoding, k);
gid = pdf_font_cid_to_gid(ctx, fontdesc, cid);
if (gid > n)
n = gid;
}
};
font->width_count = n + 1;
font->width_table = fz_malloc_array(ctx, font->width_count, sizeof(int));
memset(font->width_table, 0, font->width_count * sizeof(int));
fontdesc->size += font->width_count * sizeof(int);
for (i = 0; i < fontdesc->hmtx_len; i++)
{
for (k = fontdesc->hmtx[i].lo; k <= fontdesc->hmtx[i].hi; k++)
{
cid = pdf_lookup_cmap(fontdesc->encoding, k);
gid = pdf_font_cid_to_gid(ctx, fontdesc, cid);
if (gid >= 0 && gid < font->width_count)
font->width_table[gid] = fz_maxi(fontdesc->hmtx[i].w, font->width_table[gid]);
}
}
}
pdf_font_desc *
pdf_load_font(fz_context *ctx, pdf_document *doc, pdf_obj *rdb, pdf_obj *dict, int nested_depth)
{
pdf_obj *subtype;
pdf_obj *dfonts;
pdf_obj *charprocs;
pdf_font_desc *fontdesc;
int type3 = 0;
if ((fontdesc = pdf_find_item(ctx, pdf_drop_font_imp, dict)) != NULL)
{
return fontdesc;
}
subtype = pdf_dict_get(ctx, dict, PDF_NAME_Subtype);
dfonts = pdf_dict_get(ctx, dict, PDF_NAME_DescendantFonts);
charprocs = pdf_dict_get(ctx, dict, PDF_NAME_CharProcs);
if (pdf_name_eq(ctx, subtype, PDF_NAME_Type0))
fontdesc = pdf_load_type0_font(ctx, doc, dict);
else if (pdf_name_eq(ctx, subtype, PDF_NAME_Type1))
fontdesc = pdf_load_simple_font(ctx, doc, dict);
else if (pdf_name_eq(ctx, subtype, PDF_NAME_MMType1))
fontdesc = pdf_load_simple_font(ctx, doc, dict);
else if (pdf_name_eq(ctx, subtype, PDF_NAME_TrueType))
fontdesc = pdf_load_simple_font(ctx, doc, dict);
else if (pdf_name_eq(ctx, subtype, PDF_NAME_Type3))
{
fontdesc = pdf_load_type3_font(ctx, doc, rdb, dict);
type3 = 1;
}
else if (charprocs)
{
fz_warn(ctx, "unknown font format, guessing type3.");
fontdesc = pdf_load_type3_font(ctx, doc, rdb, dict);
type3 = 1;
}
else if (dfonts)
{
fz_warn(ctx, "unknown font format, guessing type0.");
fontdesc = pdf_load_type0_font(ctx, doc, dict);
}
else
{
fz_warn(ctx, "unknown font format, guessing type1 or truetype.");
fontdesc = pdf_load_simple_font(ctx, doc, dict);
}
if (fontdesc->font->ft_substitute && !fontdesc->to_ttf_cmap)
pdf_make_width_table(ctx, fontdesc);
pdf_store_item(ctx, dict, fontdesc, fontdesc->size);
if (type3)
pdf_load_type3_glyphs(ctx, doc, fontdesc, nested_depth);
return fontdesc;
}
#ifndef NDEBUG
void
pdf_print_font(fz_context *ctx, pdf_font_desc *fontdesc)
{
int i;
printf("fontdesc {\n");
if (fontdesc->font->ft_face)
printf("\tfreetype font\n");
if (fontdesc->font->t3procs)
printf("\ttype3 font\n");
printf("\twmode %d\n", fontdesc->wmode);
printf("\tDW %d\n", fontdesc->dhmtx.w);
printf("\tW {\n");
for (i = 0; i < fontdesc->hmtx_len; i++)
printf("\t\t<%04x> <%04x> %d\n",
fontdesc->hmtx[i].lo, fontdesc->hmtx[i].hi, fontdesc->hmtx[i].w);
printf("\t}\n");
if (fontdesc->wmode)
{
printf("\tDW2 [%d %d]\n", fontdesc->dvmtx.y, fontdesc->dvmtx.w);
printf("\tW2 {\n");
for (i = 0; i < fontdesc->vmtx_len; i++)
printf("\t\t<%04x> <%04x> %d %d %d\n", fontdesc->vmtx[i].lo, fontdesc->vmtx[i].hi,
fontdesc->vmtx[i].x, fontdesc->vmtx[i].y, fontdesc->vmtx[i].w);
printf("\t}\n");
}
}
#endif
fz_rect *pdf_measure_text(fz_context *ctx, pdf_font_desc *fontdesc, unsigned char *buf, int len, fz_rect *acc)
{
int i;
int w = 0;
for (i = 0; i < len; i++)
w += pdf_lookup_hmtx(ctx, fontdesc, buf[i]).w;
acc->x0 = 0;
acc->x1 = w / 1000.0f;
acc->y0 = fontdesc->descent / 1000.0f;
acc->y1 = fontdesc->ascent / 1000.0f;
return acc;
}
float pdf_text_stride(fz_context *ctx, pdf_font_desc *fontdesc, float fontsize, unsigned char *buf, int len, float room, int *count)
{
pdf_hmtx h;
int i = 0;
float x = 0.0;
while(i < len)
{
float span;
h = pdf_lookup_hmtx(ctx, fontdesc, buf[i]);
span = h.w * fontsize / 1000.0;
if (x + span > room)
break;
x += span;
i ++;
}
if (count)
*count = i;
return x;
}