/* 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. */ /* * References: * * PostScript Language Reference Manual, 3rd. ed. (Adobe Systems Inc.) * 5.11.4 CMap Dictionaries * 5.11.5 FMapType 9 Composite Fonts * Building CMap Files for CID-Keyed Fonts, Adobe Technical Note #5099 * CID-Keyed Font Technology Overview, Adobe Technical Note #5092 * Adobe CMap and CIDFont Files Specification, Adobe Technical Specification #5014 * * Undefined Character Handling: * PLRM 3rd. ed., sec. 5.11.5., "Handling Undefined Characters" * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "system.h" #include "mem.h" #include "error.h" #include "dpxutil.h" #include "pdfobj.h" #include "pdfresource.h" #include "cmap_p.h" #include "cmap.h" #include "cmap_write.h" struct sbuf { char *buf; char *curptr; char *limptr; }; static int write_map (mapDef *mtab, int count, unsigned char *codestr, int depth, struct sbuf *wbuf, pdf_obj *stream); #if 0 /* Not completed yet */ /* used_slot... * This is for collecting only used CIDs. * add_inverse_map never supports code-to-code of * cid-to-code mapping. */ static int add_inverse_map (CMap *icmap, mapDef *mtab, unsigned char *codestr, int depth, unsigned char *used_slot); static int add_map (CMap *cmap, mapDef *mtab, unsigned char *codestr, int depth); static CMap *invert_cmap (CMap *cmap, unsigned char *used_slot); static CMap *flatten_cmap (CMap *cmap); #endif /* 0 */ static int block_count (mapDef *mtab, int c) { int count = 0, n; n = mtab[c].len - 1; c += 1; for (; c < 256; c++) { if (LOOKUP_CONTINUE(mtab[c].flag) || !MAP_DEFINED(mtab[c].flag) || (MAP_TYPE(mtab[c].flag) != MAP_IS_CID && MAP_TYPE(mtab[c].flag) != MAP_IS_CODE) || mtab[c-1].len != mtab[c].len) break; else if (!memcmp(mtab[c-1].code, mtab[c].code, n) && mtab[c-1].code[n] < 255 && mtab[c-1].code[n] + 1 == mtab[c].code[n]) count++; else { break; } } return count; } static int sputx (unsigned char c, char **s, char *end) { char hi = (c >> 4), lo = c & 0x0f; if (*s + 2 > end) ERROR("Buffer overflow."); **s = (hi < 10) ? hi + '0' : hi + '7'; *(*s+1) = (lo < 10) ? lo + '0' : lo + '7'; *s += 2; return 2; } static int write_map (mapDef *mtab, int count, unsigned char *codestr, int depth, struct sbuf *wbuf, pdf_obj *stream) { int c, i, block_length; mapDef *mtab1; /* Must be greater than 1 */ #define BLOCK_LEN_MIN 2 struct { int start, count; } blocks[256/BLOCK_LEN_MIN+1]; int num_blocks = 0; for (c = 0; c < 256; c++) { codestr[depth] = (unsigned char) (c & 0xff); if (LOOKUP_CONTINUE(mtab[c].flag)) { mtab1 = mtab[c].next; count = write_map(mtab1, count, codestr, depth + 1, wbuf, stream); } else { if (MAP_DEFINED(mtab[c].flag)) { switch (MAP_TYPE(mtab[c].flag)) { case MAP_IS_CID: case MAP_IS_CODE: block_length = block_count(mtab, c); if (block_length >= BLOCK_LEN_MIN) { blocks[num_blocks].start = c; blocks[num_blocks].count = block_length; num_blocks++; c += block_length; } else { *(wbuf->curptr)++ = '<'; for (i = 0; i <= depth; i++) sputx(codestr[i], &(wbuf->curptr), wbuf->limptr); *(wbuf->curptr)++ = '>'; *(wbuf->curptr)++ = ' '; *(wbuf->curptr)++ = '<'; for (i = 0; i < mtab[c].len; i++) sputx(mtab[c].code[i], &(wbuf->curptr), wbuf->limptr); *(wbuf->curptr)++ = '>'; *(wbuf->curptr)++ = '\n'; count++; } break; case MAP_IS_NAME: ERROR("%s: Unexpected error...", CMAP_DEBUG_STR); break; case MAP_IS_NOTDEF: break; default: ERROR("%s: Unknown mapping type: %d", CMAP_DEBUG_STR, MAP_TYPE(mtab[c].flag)); } } } /* Flush if necessary */ if (count >= 100 || wbuf->curptr >= wbuf->limptr ) { char fmt_buf[32]; if (count > 100) ERROR("Unexpected error....: %d", count); sprintf(fmt_buf, "%d beginbfchar\n", count); pdf_add_stream(stream, fmt_buf, strlen(fmt_buf)); pdf_add_stream(stream, wbuf->buf, (int) (wbuf->curptr - wbuf->buf)); wbuf->curptr = wbuf->buf; pdf_add_stream(stream, "endbfchar\n", strlen("endbfchar\n")); count = 0; } } if (num_blocks > 0) { char fmt_buf[32]; if (count > 0) { sprintf(fmt_buf, "%d beginbfchar\n", count); pdf_add_stream(stream, fmt_buf, strlen(fmt_buf)); pdf_add_stream(stream, wbuf->buf, (int) (wbuf->curptr - wbuf->buf)); wbuf->curptr = wbuf->buf; pdf_add_stream(stream, "endbfchar\n", strlen("endbfchar\n")); count = 0; } sprintf(fmt_buf, "%d beginbfrange\n", num_blocks); pdf_add_stream(stream, fmt_buf, strlen(fmt_buf)); for (i = 0; i < num_blocks; i++) { int j; c = blocks[i].start; *(wbuf->curptr)++ = '<'; for (j = 0; j < depth; j++) sputx(codestr[j], &(wbuf->curptr), wbuf->limptr); sputx((unsigned char)c, &(wbuf->curptr), wbuf->limptr); *(wbuf->curptr)++ = '>'; *(wbuf->curptr)++ = ' '; *(wbuf->curptr)++ = '<'; for (j = 0; j < depth; j++) sputx(codestr[j], &(wbuf->curptr), wbuf->limptr); sputx((unsigned char)(c + blocks[i].count), &(wbuf->curptr), wbuf->limptr); *(wbuf->curptr)++ = '>'; *(wbuf->curptr)++ = ' '; *(wbuf->curptr)++ = '<'; for (j = 0; j < mtab[c].len; j++) sputx(mtab[c].code[j], &(wbuf->curptr), wbuf->limptr); *(wbuf->curptr)++ = '>'; *(wbuf->curptr)++ = '\n'; } pdf_add_stream(stream, wbuf->buf, (int) (wbuf->curptr - wbuf->buf)); wbuf->curptr = wbuf->buf; pdf_add_stream(stream, "endbfrange\n", strlen("endbfrange\n")); } return count; } /* duplicated from pdfobj.c */ static void write_string (char **outptr, char *endptr, const char *strptr) { const char *s; char *p; int i, length; s = strptr; length = strptr ? strlen(strptr) : 0; p = *outptr; *p++ = '('; for (i = 0; i < length; i++) { unsigned char ch; ch = s[i]; if (ch < 32 || ch > 126) { p += sprintf(p, "\\%03o", ch); } else { switch (ch) { case '(': case ')': case '\\': *p++ = '\\'; *p++ = ch; break; default: *p++ = ch; break; } } } *p++ = ')'; *outptr = p; } static void write_name (char **outptr, char *endptr, const char *name) { const char *s; char *p; int i, length; s = name; length = name ? strlen(name) : 0; p = *outptr; #ifndef is_delim /* Avoid '{' and '}' for PostScript compatibility? */ #define is_delim(c) ((c) == '(' || (c) == ')' || \ (c) == '/' || \ (c) == '<' || (c) == '>' || \ (c) == '[' || (c) == ']' || \ (c) == '{' || (c) == '}' || \ (c) == '%') #endif *p++ = '/'; for (i = 0; i < length; i++) { if (s[i] < '!' || s[i] > '~' || s[i] == '#' || is_delim(s[i])) { /* ^ "space" is here. */ *p++ = '#'; sputx(s[i], &p, endptr); } else { *p++ = s[i]; } } *outptr = p; } #define CMAP_BEGIN "\ /CIDInit /ProcSet findresource begin\n\ 12 dict begin\n\ begincmap\n\ " #define CMAP_END "\ endcmap\n\ CMapName currentdict /CMap defineresource pop\n\ end\n\ end\n\ " pdf_obj * CMap_create_stream (CMap *cmap) { pdf_obj *stream; pdf_obj *stream_dict; CIDSysInfo *csi; struct sbuf wbuf; struct rangeDef *ranges; unsigned char *codestr; int i, j, count = 0; if (!cmap || !CMap_is_valid(cmap)) { WARN("Invalid CMap"); return NULL; } if (cmap->type == CMAP_TYPE_IDENTITY) return NULL; stream = pdf_new_stream(STREAM_COMPRESS); stream_dict = pdf_stream_dict(stream); csi = CMap_get_CIDSysInfo(cmap); if (!csi) { csi = (cmap->type != CMAP_TYPE_TO_UNICODE) ? &CSI_IDENTITY : &CSI_UNICODE; } if (cmap->type != CMAP_TYPE_TO_UNICODE) { pdf_obj *csi_dict; csi_dict = pdf_new_dict(); pdf_add_dict(csi_dict, pdf_new_name("Registry"), pdf_new_string(csi->registry, strlen(csi->registry))); pdf_add_dict(csi_dict, pdf_new_name("Ordering"), pdf_new_string(csi->ordering, strlen(csi->ordering))); pdf_add_dict(csi_dict, pdf_new_name("Supplement"), pdf_new_number(csi->supplement)); pdf_add_dict(stream_dict, pdf_new_name("Type"), pdf_new_name("CMap")); pdf_add_dict(stream_dict, pdf_new_name("CMapName"), pdf_new_name(cmap->name)); pdf_add_dict(stream_dict, pdf_new_name("CIDSystemInfo"), csi_dict); if (cmap->wmode != 0) pdf_add_dict(stream_dict, pdf_new_name("WMode"), pdf_new_number(cmap->wmode)); } /* TODO: * Predefined CMaps need not to be embedded. */ if (cmap->useCMap) { ERROR("UseCMap found (not supported yet)..."); if (CMap_is_Identity(cmap->useCMap)) { /* not sure */ if (CMap_get_wmode(cmap) == 1) { pdf_add_dict(stream_dict, pdf_new_name("UseCMap"), pdf_new_name("Identity-V")); } else { pdf_add_dict(stream_dict, pdf_new_name("UseCMap"), pdf_new_name("Identity-H")); } } else { int res_id; pdf_obj *ucmap_ref; res_id = pdf_findresource("CMap", CMap_get_name(cmap->useCMap)); if (res_id >= 0) { ucmap_ref = pdf_get_resource_reference(res_id); } else { pdf_obj *ucmap_obj; ucmap_obj = CMap_create_stream(cmap->useCMap); if (!ucmap_obj) { ERROR("Uh ah. I cannot continue..."); } res_id = pdf_defineresource("CMap", CMap_get_name(cmap->useCMap), ucmap_obj, PDF_RES_FLUSH_IMMEDIATE); ucmap_ref = pdf_get_resource_reference(res_id); } pdf_add_dict(stream_dict, pdf_new_name("UseCMap"), ucmap_ref); } } #define WBUF_SIZE 40960 wbuf.buf = NEW(WBUF_SIZE, char); codestr = NEW(cmap->profile.maxBytesIn, unsigned char); memset(codestr, 0, cmap->profile.maxBytesIn); wbuf.curptr = wbuf.buf; wbuf.limptr = wbuf.buf + WBUF_SIZE - 2 * (cmap->profile.maxBytesIn + cmap->profile.maxBytesOut) + 16; /* Start CMap */ pdf_add_stream(stream, (const void *) CMAP_BEGIN, strlen(CMAP_BEGIN)); wbuf.curptr += sprintf(wbuf.curptr, "/CMapName "); write_name(&wbuf.curptr, wbuf.limptr, cmap->name); wbuf.curptr += sprintf(wbuf.curptr, " def\n"); wbuf.curptr += sprintf(wbuf.curptr, "/CMapType %d def\n" , cmap->type); if (cmap->wmode != 0 && cmap->type != CMAP_TYPE_TO_UNICODE) wbuf.curptr += sprintf(wbuf.curptr, "/WMode %d def\n", cmap->wmode); #define CMAP_CSI_FMT "/CIDSystemInfo <<\n\ /Registry (%s)\n\ /Ordering (%s)\n\ /Supplement %d\n\ >> def\n" wbuf.curptr += sprintf(wbuf.curptr, "/CIDSystemInfo <<\n"); wbuf.curptr += sprintf(wbuf.curptr, " /Registry "); write_string(&wbuf.curptr, wbuf.limptr, csi->registry); wbuf.curptr += sprintf(wbuf.curptr, "\n"); wbuf.curptr += sprintf(wbuf.curptr, " /Ordering "); write_string(&wbuf.curptr, wbuf.limptr, csi->ordering); wbuf.curptr += sprintf(wbuf.curptr, "\n"); wbuf.curptr += sprintf(wbuf.curptr, " /Supplement %d\n>> def\n", csi->supplement); pdf_add_stream(stream, wbuf.buf, (int)(wbuf.curptr - wbuf.buf)); wbuf.curptr = wbuf.buf; /* codespacerange */ ranges = cmap->codespace.ranges; wbuf.curptr += sprintf(wbuf.curptr, "%d begincodespacerange\n", cmap->codespace.num); for (i = 0; i < cmap->codespace.num; i++) { *(wbuf.curptr)++ = '<'; for (j = 0; j < ranges[i].dim; j++) { sputx(ranges[i].codeLo[j], &(wbuf.curptr), wbuf.limptr); } *(wbuf.curptr)++ = '>'; *(wbuf.curptr)++ = ' '; *(wbuf.curptr)++ = '<'; for (j = 0; j < ranges[i].dim; j++) { sputx(ranges[i].codeHi[j], &(wbuf.curptr), wbuf.limptr); } *(wbuf.curptr)++ = '>'; *(wbuf.curptr)++ = '\n'; } pdf_add_stream(stream, wbuf.buf, (int)(wbuf.curptr - wbuf.buf)); wbuf.curptr = wbuf.buf; pdf_add_stream(stream, "endcodespacerange\n", strlen("endcodespacerange\n")); /* CMap body */ if (cmap->mapTbl) { count = write_map(cmap->mapTbl, 0, codestr, 0, &wbuf, stream); /* Top node */ if (count > 0) { /* Flush */ char fmt_buf[32]; if (count > 100) ERROR("Unexpected error....: %d", count); sprintf(fmt_buf, "%d beginbfchar\n", count); pdf_add_stream(stream, fmt_buf, strlen(fmt_buf)); pdf_add_stream(stream, wbuf.buf, (int) (wbuf.curptr - wbuf.buf)); pdf_add_stream(stream, "endbfchar\n", strlen("endbfchar\n")); count = 0; wbuf.curptr = wbuf.buf; } } /* End CMap */ pdf_add_stream(stream, CMAP_END, strlen(CMAP_END)); RELEASE(codestr); RELEASE(wbuf.buf); return stream; } #if 0 /* Not completed yet */ static int add_inverse_map (CMap *icmap, mapDef *mtab, unsigned char *codestr, int depth, unsigned char *used_slot) { CID cid; int c; mapDef *mtab1; for (c = 0; c < 256; c++) { codestr[depth] = (unsigned char) (c & 0xff); if (LOOKUP_CONTINUE(mtab[c].flag)) { mtab1 = mtab[c].next; add_inverse_map(icmap, mtab1, codestr, depth + 1, used_slot); } else { if (MAP_DEFINED(mtab[c].flag)) { switch (MAP_TYPE(mtab[c].flag)) { /* We should restrict it to to-CID mapping. * However... */ case MAP_IS_CID: case MAP_IS_CODE: if (mtab[c].len == 2) { cid = (mtab[c].code[0] << 8)|mtab[c].code[1]; #ifndef is_used_char2 #define is_used_char2(b,c) (((b)[(c)/8]) & (1 << (7-((c)%8)))) #endif if (!used_slot || is_used_char2(used_slot, cid)) { CMap_add_bfchar(icmap, mtab[c].code, mtab[c].len, codestr, depth + 1); } } break; case MAP_IS_NAME: ERROR("%s: Unexpected error...", CMAP_DEBUG_STR); break; case MAP_IS_NOTDEF: break; default: ERROR("%s: Unknown mapping type: %d", CMAP_DEBUG_STR, MAP_TYPE(mtab[c].flag)); } } } } return 0; } static int add_map (CMap *cmap, mapDef *mtab, unsigned char *codestr, int depth) { int c; mapDef *mtab1; for (c = 0; c < 256; c++) { codestr[depth] = (unsigned char) (c & 0xff); if (LOOKUP_CONTINUE(mtab[c].flag)) { mtab1 = mtab[c].next; add_map(cmap, mtab1, codestr, depth + 1); } else { if (MAP_DEFINED(mtab[c].flag)) { switch (MAP_TYPE(mtab[c].flag)) { case MAP_IS_CID: case MAP_IS_CODE: CMap_add_bfchar(cmap, codestr, depth + 1, mtab[c].code, mtab[c].len); break; case MAP_IS_NAME: ERROR("%s: Unexpected error...", CMAP_DEBUG_STR); break; case MAP_IS_NOTDEF: break; default: ERROR("%s: Unknown mapping type: %d", CMAP_DEBUG_STR, MAP_TYPE(mtab[c].flag)); } } } } return 0; } static CMap * invert_cmap (CMap *cmap, unsigned char *used_slot) { CMap *icmap; unsigned char *codestr; unsigned char range_min[2] = {0x00, 0x00}; /* CID */ unsigned char range_max[2] = {0xff, 0xfe}; /* CID */ cmap = flatten_cmap(cmap); ASSERT(cmap); icmap = CMap_new(); CMap_set_type(icmap, CMAP_TYPE_CID_TO_CODE); CMap_add_codespacerange(icmap, range_min, range_max, 2); if (cmap->mapTbl) { codestr = NEW(cmap->profile.maxBytesIn, unsigned char); memset(codestr, 0, cmap->profile.maxBytesIn); add_inverse_map(icmap, cmap->mapTbl, codestr, 0, used_slot); /* top node */ RELEASE(codestr); } CMap_release(cmap); return icmap; } static CMap * flatten_cmap (CMap *cmap) { CMap *fcmap; rangeDef *range; int i; ASSERT(cmap); fcmap = CMap_new(); for (i = 0; i < cmap->codespace.num; i++) { range = &(cmap->codespace.ranges[i]); CMap_add_codespacerange(fcmap, range->codeLo, range->codeHi, range->dim); } CMap_set_CIDSysInfo(fcmap, cmap->CSI); CMap_set_type (fcmap, cmap->type ); CMap_set_wmode(fcmap, cmap->wmode); fcmap->flags = cmap->flags; fcmap->version = cmap->version; while (cmap) { if (cmap->mapTbl) { unsigned char *codestr; codestr = NEW(cmap->profile.maxBytesIn, unsigned char); memset(codestr, 0, cmap->profile.maxBytesIn); add_map(fcmap, cmap->mapTbl, codestr, 0); /* top node */ RELEASE(codestr); } cmap = cmap->useCMap; } return fcmap; } pdf_obj * CMap_ToCode_stream (CMap *cmap, const char *cmap_name, CIDSysInfo *csi, int cmap_type, unsigned char *used_slot, int flags) { pdf_obj *stream = NULL; CMap *icmap; ASSERT(cmap && cmap_name); if (cmap->type != CMAP_TYPE_CODE_TO_CID) return NULL; icmap = invert_cmap(cmap, used_slot); if (icmap) { CMap_set_name(icmap, cmap_name); if (csi) CMap_set_CIDSysInfo(icmap, csi); else { CMap_set_CIDSysInfo(icmap, &(CSI_IDENTITY)); } CMap_set_type(icmap, cmap_type); stream = CMap_create_stream(icmap, flags); CMap_release(icmap); } return stream; } #endif /* 0 */