#include "libtexpdf.h"
#define TYPE0FONT_DEBUG_STR "Type0"
#define TYPE0FONT_DEBUG 3
static int __verbose = 0;
static pdf_obj *pdf_read_ToUnicode_file (const char *cmap_name);
void
Type0Font_set_verbose(void)
{
__verbose++;
}
static char *
new_used_chars2(void)
{
char *used_chars;
used_chars = NEW(8192, char);
memset(used_chars, 0, 8192);
return used_chars;
}
#define FLAG_NONE 0
#define FLAG_USED_CHARS_SHARED (1 << 0)
struct Type0Font {
char *fontname;
char *encoding;
char *used_chars;
CIDFont *descendant;
int flags;
int wmode;
int cmap_id;
pdf_obj *indirect;
pdf_obj *fontdict;
pdf_obj *descriptor;
};
static void
Type0Font_init_font_struct (Type0Font *font)
{
ASSERT(font);
font->fontname = NULL;
font->fontdict = NULL;
font->indirect = NULL;
font->descriptor = NULL;
font->encoding = NULL;
font->used_chars = NULL;
font->descendant = NULL;
font->wmode = -1;
font->cmap_id = -1;
font->flags = FLAG_NONE;
return;
}
static void
Type0Font_clean (Type0Font *font)
{
if (font) {
if (font->fontdict)
ERROR("%s: Object not flushed.", TYPE0FONT_DEBUG_STR);
if (font->indirect)
ERROR("%s: Object not flushed.", TYPE0FONT_DEBUG_STR);
if (font->descriptor)
ERROR("%s: FontDescriptor unexpected for Type0 font.", TYPE0FONT_DEBUG_STR);
if (!(font->flags & FLAG_USED_CHARS_SHARED) && font->used_chars)
RELEASE(font->used_chars);
if (font->encoding)
RELEASE(font->encoding);
if (font->fontname)
RELEASE(font->fontname);
font->fontdict = NULL;
font->indirect = NULL;
font->descriptor = NULL;
font->used_chars = NULL;
font->encoding = NULL;
font->fontname = NULL;
}
}
#include "tt_cmap.h"
static pdf_obj *
Type0Font_create_ToUnicode_stream(Type0Font *font) {
CIDFont *cidfont = font->descendant;
return otf_create_ToUnicode_stream(CIDFont_get_ident(cidfont),
CIDFont_get_opt_index(cidfont),
Type0Font_get_usedchars(font),
font->cmap_id);
}
static pdf_obj *
Type0Font_try_load_ToUnicode_stream(Type0Font *font, char *cmap_base) {
char *cmap_name = NEW(strlen(cmap_base) + strlen("-UTF-16"), char);
pdf_obj *tounicode;
sprintf(cmap_name, "%s-UTF16", cmap_base);
tounicode = pdf_read_ToUnicode_file(cmap_name);
if (!tounicode) {
sprintf(cmap_name, "%s-UCS2", cmap_base);
tounicode = pdf_read_ToUnicode_file(cmap_name);
}
RELEASE(cmap_name);
if (!tounicode)
tounicode = Type0Font_create_ToUnicode_stream(font);
return tounicode;
}
static void
add_ToUnicode (Type0Font *font)
{
pdf_obj *tounicode;
CIDFont *cidfont;
CIDSysInfo *csi;
char *fontname;
cidfont = font->descendant;
if (!cidfont) {
ERROR("%s: No descendant CID-keyed font.", TYPE0FONT_DEBUG_STR);
return;
}
if (CIDFont_is_ACCFont(cidfont)) {
return;
} else if (CIDFont_is_UCSFont(cidfont)) {
tounicode = pdf_read_ToUnicode_file("Adobe-Identity-UCS2");
if (!tounicode) {
tounicode = texpdf_new_name("Identity-H");
}
texpdf_add_dict(font->fontdict, texpdf_new_name("ToUnicode"), tounicode);
return;
}
tounicode = NULL;
csi = CIDFont_get_CIDSysInfo(cidfont);
fontname = CIDFont_get_fontname(cidfont);
if (CIDFont_get_embedding(cidfont)) {
fontname += 7;
}
if (!strcmp(csi->registry, "Adobe") &&
!strcmp(csi->ordering, "Identity")) {
switch (CIDFont_get_subtype(cidfont)) {
case CIDFONT_TYPE2:
tounicode = Type0Font_create_ToUnicode_stream(font);
break;
default:
if (CIDFont_get_flag(cidfont, CIDFONT_FLAG_TYPE1C)) {
tounicode = Type0Font_create_ToUnicode_stream(font);
} else if (CIDFont_get_flag(cidfont, CIDFONT_FLAG_TYPE1)) {
return;
} else {
tounicode = Type0Font_try_load_ToUnicode_stream(font, fontname);
}
break;
}
} else {
char *cmap_base = NEW(strlen(csi->registry) + strlen(csi->ordering) + 2, char);
sprintf(cmap_base, "%s-%s", csi->registry, csi->ordering);
tounicode = Type0Font_try_load_ToUnicode_stream(font, cmap_base);
RELEASE(cmap_base);
}
if (tounicode) {
texpdf_add_dict(font->fontdict,
texpdf_new_name("ToUnicode"), tounicode);
} else {
WARN("Failed to load ToUnicode CMap for font \"%s\"", fontname);
}
return;
}
void
Type0Font_set_ToUnicode (Type0Font *font, pdf_obj *cmap_ref)
{
ASSERT(font);
texpdf_add_dict(font->fontdict,
texpdf_new_name("ToUnicode"), cmap_ref);
}
static void
Type0Font_dofont (Type0Font *font)
{
if (!font || !font->indirect)
return;
if (!texpdf_lookup_dict(font->fontdict, "ToUnicode")) {
add_ToUnicode(font);
}
}
static void
Type0Font_flush (Type0Font *font)
{
if (font) {
if (font->fontdict)
texpdf_release_obj(font->fontdict);
font->fontdict = NULL;
if (font->indirect)
texpdf_release_obj(font->indirect);
font->indirect = NULL;
if (font->descriptor)
ERROR("%s: FontDescriptor unexpected for Type0 font.", TYPE0FONT_DEBUG_STR);
font->descriptor = NULL;
}
}
int
Type0Font_get_wmode (Type0Font *font)
{
ASSERT(font);
return font->wmode;
}
#if 0#endif
char *
Type0Font_get_usedchars (Type0Font *font)
{
ASSERT(font);
return font->used_chars;
}
pdf_obj *
Type0Font_get_resource (Type0Font *font)
{
ASSERT(font);
if (!font->indirect) {
pdf_obj *array;
array = texpdf_new_array();
texpdf_add_array(array, CIDFont_get_resource(font->descendant));
texpdf_add_dict(font->fontdict, texpdf_new_name("DescendantFonts"), array);
font->indirect = texpdf_ref_obj(font->fontdict);
}
return texpdf_link_obj(font->indirect);
}
#define CHECK_ID(n) do {\
if ((n) < 0 || (n) >= __cache.count)\
ERROR("%s: Invalid ID %d", TYPE0FONT_DEBUG_STR, (n));\
} while (0)
#define CACHE_ALLOC_SIZE 16u
static struct font_cache {
int count;
int capacity;
Type0Font *fonts;
} __cache = {
0, 0, NULL
};
void
Type0Font_cache_init (void)
{
if (__cache.fonts)
ERROR("%s: Already initialized.", TYPE0FONT_DEBUG_STR);
__cache.count = 0;
__cache.capacity = 0;
__cache.fonts = NULL;
}
Type0Font *
Type0Font_cache_get (int id)
{
CHECK_ID(id);
return &__cache.fonts[id];
}
int
Type0Font_cache_find (const char *map_name, int cmap_id, fontmap_opt *fmap_opt)
{
int font_id = -1;
Type0Font *font;
CIDFont *cidfont;
CMap *cmap;
CIDSysInfo *csi;
char *fontname = NULL;
int cid_id = -1, parent_id = -1, wmode = 0;
int pdf_ver;
pdf_ver = texpdf_get_version();
if (!map_name || cmap_id < 0 || pdf_ver < 2)
return -1;
cmap = texpdf_CMap_cache_get(cmap_id);
csi = (CMap_is_Identity(cmap)) ? NULL : CMap_get_CIDSysInfo(cmap) ;
cid_id = CIDFont_cache_find(map_name, csi, fmap_opt);
if (cid_id < 0)
return -1;
cidfont = CIDFont_cache_get(cid_id);
wmode = CMap_get_wmode(cmap);
parent_id = CIDFont_get_parent_id(cidfont, wmode);
if (parent_id >= 0)
return parent_id;
if (__cache.count >= __cache.capacity) {
__cache.capacity += CACHE_ALLOC_SIZE;
__cache.fonts = RENEW(__cache.fonts, __cache.capacity, struct Type0Font);
}
font_id = __cache.count;
font = &__cache.fonts[font_id];
Type0Font_init_font_struct(font);
if (wmode) {
font->encoding = NEW(strlen("Identity-V")+1, char);
strcpy(font->encoding, "Identity-V");
} else {
font->encoding = NEW(strlen("Identity-H")+1, char);
strcpy(font->encoding, "Identity-H");
}
font->wmode = wmode;
font->cmap_id = cmap_id;
font->fontdict = texpdf_new_dict();
texpdf_add_dict(font->fontdict, texpdf_new_name ("Type"), texpdf_new_name ("Font"));
texpdf_add_dict(font->fontdict, texpdf_new_name ("Subtype"), texpdf_new_name ("Type0"));
font->descendant = cidfont;
CIDFont_attach_parent(cidfont, font_id, wmode);
fontname = CIDFont_get_fontname(cidfont);
if (__verbose) {
if (CIDFont_get_embedding(cidfont) && strlen(fontname) > 7)
MESG("(CID:%s)", fontname+7);
else
MESG("(CID:%s)", fontname);
}
font->used_chars = NULL;
font->flags = FLAG_NONE;
switch (CIDFont_get_subtype(cidfont)) {
case CIDFONT_TYPE0:
font->fontname = NEW(strlen(fontname)+strlen(font->encoding)+2, char);
sprintf(font->fontname, "%s-%s", fontname, font->encoding);
texpdf_add_dict(font->fontdict,
texpdf_new_name("BaseFont"), texpdf_new_name(font->fontname));
if ((parent_id = CIDFont_get_parent_id(cidfont, wmode ? 0 : 1)) < 0) {
font->used_chars = new_used_chars2();
} else {
font->used_chars = Type0Font_get_usedchars(Type0Font_cache_get(parent_id));
font->flags |= FLAG_USED_CHARS_SHARED;
}
break;
case CIDFONT_TYPE2:
texpdf_add_dict(font->fontdict,
texpdf_new_name("BaseFont"), texpdf_new_name(fontname));
font->used_chars = new_used_chars2();
break;
default:
ERROR("Unrecognized CIDFont Type");
break;
}
texpdf_add_dict(font->fontdict,
texpdf_new_name("Encoding"), texpdf_new_name(font->encoding));
__cache.count++;
return font_id;
}
void
Type0Font_cache_close (void)
{
int font_id;
if (__cache.fonts) {
for (font_id = 0; font_id < __cache.count; font_id++)
Type0Font_dofont(&__cache.fonts[font_id]);
}
CIDFont_cache_close();
if (__cache.fonts) {
for (font_id = 0; font_id < __cache.count; font_id++) {
Type0Font_flush(&__cache.fonts[font_id]);
Type0Font_clean(&__cache.fonts[font_id]);
}
RELEASE(__cache.fonts);
}
__cache.fonts = NULL;
__cache.count = 0;
__cache.capacity = 0;
}
#ifndef WITHOUT_COMPAT
#include "cmap_read.h"
#include "cmap_write.h"
#include "pdfresource.h"
#include "pdfencoding.h"
static pdf_obj *
create_dummy_CMap (void)
{
pdf_obj *stream;
char buf[32];
int i, n;
#define CMAP_PART0 "\
%!PS-Adobe-3.0 Resource-CMap\n\
%%DocumentNeededResources: ProcSet (CIDInit)\n\
%%IncludeResource: ProcSet (CIDInit)\n\
%%BeginResource: CMap (Adobe-Identity-UCS2)\n\
%%Title: (Adobe-Identity-UCS2 Adobe UCS2 0)\n\
%%Version: 1.0\n\
%%Copyright:\n\
%% ---\n\
%%EndComments\n\n\
"
#define CMAP_PART1 "\
/CIDInit /ProcSet findresource begin\n\
\n\
12 dict begin\n\nbegincmap\n\n\
/CIDSystemInfo 3 dict dup begin\n\
/Registry (Adobe) def\n\
/Ordering (UCS2) def\n\
/Supplement 0 def\n\
end def\n\n\
/CMapName /Adobe-Identity-UCS2 def\n\
/CMapVersion 1.0 def\n\
/CMapType 2 def\n\n\
2 begincodespacerange\n\
<0000> <FFFF>\n\
endcodespacerange\n\
"
#define CMAP_PART3 "\
endcmap\n\n\
CMapName currentdict /CMap defineresource pop\n\n\
end\nend\n\n\
%%EndResource\n\
%%EOF\n\
"
stream = texpdf_new_stream(STREAM_COMPRESS);
texpdf_add_stream(stream, CMAP_PART0, strlen(CMAP_PART0));
texpdf_add_stream(stream, CMAP_PART1, strlen(CMAP_PART1));
texpdf_add_stream(stream, "\n100 beginbfrange\n", strlen("\n100 beginbfrange\n"));
for (i = 0; i < 0x64; i++) {
n = sprintf(buf,
"<%02X00> <%02XFF> <%02X00>\n", i, i, i);
texpdf_add_stream(stream, buf, n);
}
texpdf_add_stream(stream, "endbfrange\n\n", strlen("endbfrange\n\n"));
texpdf_add_stream(stream, "\n100 beginbfrange\n", strlen("\n100 beginbfrange\n"));
for (i = 0x64; i < 0xc8; i++) {
n = sprintf(buf,
"<%02X00> <%02XFF> <%02X00>\n", i, i, i);
texpdf_add_stream(stream, buf, n);
}
texpdf_add_stream(stream, "endbfrange\n\n", strlen("endbfrange\n\n"));
texpdf_add_stream(stream, "\n48 beginbfrange\n", strlen("\n48 beginbfrange\n"));
for (i = 0xc8; i <= 0xd7; i++) {
n = sprintf(buf,
"<%02X00> <%02XFF> <%02X00>\n", i, i, i);
texpdf_add_stream(stream, buf, n);
}
for (i = 0xe0; i <= 0xff; i++) {
n = sprintf(buf,
"<%02X00> <%02XFF> <%02X00>\n", i, i, i);
texpdf_add_stream(stream, buf, n);
}
texpdf_add_stream(stream, "endbfrange\n\n", strlen("endbfrange\n\n"));
texpdf_add_stream(stream, CMAP_PART3, strlen(CMAP_PART3));
return stream;
}
static pdf_obj *
pdf_read_ToUnicode_file (const char *cmap_name)
{
pdf_obj *stream;
long res_id = -1;
ASSERT(cmap_name);
res_id = pdf_findresource("CMap", cmap_name);
if (res_id < 0) {
if (!strcmp(cmap_name, "Adobe-Identity-UCS2"))
stream = create_dummy_CMap();
else {
stream = pdf_load_ToUnicode_stream(cmap_name);
}
if (stream) {
res_id = pdf_defineresource("CMap",
cmap_name,
stream, PDF_RES_FLUSH_IMMEDIATE);
}
}
return (res_id < 0 ? NULL : texpdf_get_resource_reference(res_id));
}
#endif