#include "libtexpdf.h"
#include <string.h>
#include "mem.h"
#include "error.h"
#include "dpxutil.h"
#include "pst.h"
#include "cmap_p.h"
#include "cmap.h"
#include "cmap_read.h"
static int __verbose = 0;
#define CMAP_PARSE_DEBUG_STR "CMap_parse:"
#define CMAP_PARSE_DEBUG 3
#ifdef HAVE_DPXFILE
#include "dpxfile.h"
#else
#include "mfileio.h"
typedef struct {
unsigned char *cursor;
unsigned char *endptr;
unsigned char *buf;
size_t max;
FILE *fp;
size_t unread;
} ifreader;
static ifreader *ifreader_create (FILE *fp, size_t remain, size_t bufsize);
static size_t ifreader_read (ifreader *reader, size_t size);
static void ifreader_destroy (ifreader *reader);
static ifreader *
ifreader_create (FILE *fp, size_t size, size_t bufsize)
{
ifreader *reader;
reader = NEW(1, ifreader);
reader->buf = NEW(bufsize+1, unsigned char);
reader->max = bufsize;
reader->fp = fp;
reader->unread = size;
reader->cursor = reader->endptr = reader->buf;
*reader->endptr = 0;
return reader;
}
static void
ifreader_destroy (ifreader *reader)
{
ASSERT(reader);
if (reader->buf)
RELEASE(reader->buf);
RELEASE(reader);
}
static size_t
ifreader_read (ifreader *reader, size_t size)
{
size_t bytesread = 0, bytesrem = 0;
ASSERT(reader);
bytesrem = (size_t) reader->endptr - (size_t) reader->cursor;
if (size > reader->max) {
if (__verbose)
MESG("\nExtending buffer (%ld bytes)...\n", size);
reader->buf = RENEW(reader->buf, size+1, unsigned char);
reader->max = size;
}
if (reader->unread > 0 && bytesrem < size) {
bytesread = MIN(reader->max - bytesrem, reader->unread);
memmove(reader->buf, reader->cursor, bytesrem);
reader->cursor = reader->buf;
reader->endptr = reader->buf + bytesrem;
if (fread(reader->endptr, 1, bytesread, reader->fp) != bytesread)
ERROR("Reading file failed.");
reader->endptr += bytesread;
reader->unread -= bytesread;
if (__verbose)
MESG("Reading more %ld bytes (%ld bytes remains in buffer)...\n", bytesread, bytesrem);
}
*reader->endptr = 0;
return bytesread + bytesrem;
}
#define ifreader_need(f,s) ifreader_read((f),(s))
#endif
static int check_next_token (ifreader *input, const char *key);
static int get_coderange (ifreader *input, unsigned char *codeLo, unsigned char *codeHi, int *dim, int maxlen);
static int handle_codearray (CMap *cmap, ifreader *input, unsigned char *codeLo, int dim, int count);
static int do_codespacerange (CMap *cmap, ifreader *input, int count);
static int do_notdefrange (CMap *cmap, ifreader *input, int count);
static int do_bfrange (CMap *cmap, ifreader *input, int count);
static int do_cidrange (CMap *cmap, ifreader *input, int count);
static int do_notdefchar (CMap *cmap, ifreader *input, int count);
static int do_bfchar (CMap *cmap, ifreader *input, int count);
static int do_cidchar (CMap *cmap, ifreader *input, int count);
#define TOKEN_LEN_MAX 127
static int
check_next_token (ifreader *input, const char *key)
{
int cmp;
pst_obj *token;
char *str;
if (ifreader_need(input, strlen(key)) == 0)
return -1;
if ((token = pst_get_token(&(input->cursor), input->endptr)) == NULL)
return -1;
str = (char *) pst_getSV(token);
cmp = strcmp(str, key) ? -1 : 0;
if (str)
RELEASE(str);
pst_release_obj(token);
return cmp;
}
static int
get_coderange (ifreader *input,
unsigned char *codeLo, unsigned char *codeHi, int *dim, int maxlen)
{
pst_obj *tok1, *tok2;
int dim1, dim2;
if ((tok1 = pst_get_token(&(input->cursor), input->endptr)) == NULL)
return -1;
if ((tok2 = pst_get_token(&(input->cursor), input->endptr)) == NULL) {
pst_release_obj(tok1);
return -1;
}
if (!PST_STRINGTYPE(tok1) || !PST_STRINGTYPE(tok2)) {
pst_release_obj(tok1);
pst_release_obj(tok2);
return -1;
}
dim1 = pst_length_of(tok1);
dim2 = pst_length_of(tok2);
if (dim1 != dim2 || dim1 > maxlen) {
pst_release_obj(tok1);
pst_release_obj(tok2);
return -1;
}
memcpy(codeLo, pst_data_ptr(tok1), dim1);
memcpy(codeHi, pst_data_ptr(tok2), dim2);
pst_release_obj(tok1);
pst_release_obj(tok2);
*dim = dim1;
return 0;
}
static int
do_codespacerange (CMap *cmap, ifreader *input, int count)
{
unsigned char codeLo[TOKEN_LEN_MAX], codeHi[TOKEN_LEN_MAX];
int dim;
while (count-- > 0) {
if (get_coderange(input, codeLo, codeHi, &dim, TOKEN_LEN_MAX) < 0)
return -1;
CMap_add_codespacerange(cmap, codeLo, codeHi, dim);
}
return check_next_token(input, "endcodespacerange");
}
static int
handle_codearray (CMap *cmap, ifreader *input, unsigned char *codeLo, int dim, int count)
{
pst_obj *tok = NULL;
if (dim < 1)
ERROR("Invalid code range.");
while (count-- > 0) {
if ((tok = pst_get_token(&(input->cursor), input->endptr)) == NULL)
return -1;
else if (PST_STRINGTYPE(tok)) {
CMap_add_bfchar(cmap, codeLo, dim, (unsigned char *) pst_data_ptr(tok), (int) pst_length_of(tok));
} else if (PST_MARKTYPE(tok) || !PST_NAMETYPE(tok))
ERROR("%s: Invalid CMap mapping record.", CMAP_PARSE_DEBUG_STR);
else
ERROR("%s: Mapping to charName not supported.", CMAP_PARSE_DEBUG_STR);
pst_release_obj(tok);
codeLo[dim-1] += 1;
}
return check_next_token(input, "]");
}
static int
do_notdefrange (CMap *cmap, ifreader *input, int count)
{
pst_obj *tok;
unsigned char codeLo[TOKEN_LEN_MAX], codeHi[TOKEN_LEN_MAX];
long dstCID;
int dim;
while (count-- > 0) {
if (ifreader_need(input, TOKEN_LEN_MAX*3) == 0)
return -1;
if (get_coderange(input, codeLo, codeHi, &dim, TOKEN_LEN_MAX) < 0 ||
(tok = pst_get_token(&(input->cursor), input->endptr)) == NULL)
return -1;
if (PST_INTEGERTYPE(tok)) {
dstCID = pst_getIV(tok);
if (dstCID >= 0 && dstCID <= CID_MAX)
CMap_add_notdefrange(cmap, codeLo, codeHi, dim, (CID) dstCID);
} else
WARN("%s: Invalid CMap mapping record. (ignored)", CMAP_PARSE_DEBUG_STR);
pst_release_obj(tok);
}
return check_next_token(input, "endnotdefrange");
}
static int
do_bfrange (CMap *cmap, ifreader *input, int count)
{
pst_obj *tok;
unsigned char codeLo[TOKEN_LEN_MAX], codeHi[TOKEN_LEN_MAX];
int srcdim;
while (count-- > 0) {
if (ifreader_need(input, TOKEN_LEN_MAX*3) == 0)
return -1;
if (get_coderange(input, codeLo, codeHi, &srcdim, TOKEN_LEN_MAX) < 0 ||
(tok = pst_get_token(&(input->cursor), input->endptr)) == NULL)
return -1;
if (PST_STRINGTYPE(tok)) {
CMap_add_bfrange(cmap, codeLo, codeHi, srcdim,
(unsigned char *) pst_data_ptr(tok), (int) pst_length_of(tok));
} else if (PST_MARKTYPE(tok)) {
if (handle_codearray(cmap, input, codeLo, srcdim,
codeHi[srcdim-1] - codeLo[srcdim-1] + 1) < 0) {
pst_release_obj(tok);
return -1;
}
} else
WARN("%s: Invalid CMap mapping record. (ignored)", CMAP_PARSE_DEBUG_STR);
pst_release_obj(tok);
}
return check_next_token(input, "endbfrange");
}
static int
do_cidrange (CMap *cmap, ifreader *input, int count)
{
pst_obj *tok;
unsigned char codeLo[TOKEN_LEN_MAX], codeHi[TOKEN_LEN_MAX];
long dstCID;
int dim;
while (count-- > 0) {
if (ifreader_need(input, TOKEN_LEN_MAX*3) == 0)
return -1;
if (get_coderange(input, codeLo, codeHi, &dim, TOKEN_LEN_MAX) < 0 ||
(tok = pst_get_token(&(input->cursor), input->endptr)) == NULL)
return -1;
if (PST_INTEGERTYPE(tok)) {
dstCID = pst_getIV(tok);
if (dstCID >= 0 && dstCID <= CID_MAX)
CMap_add_cidrange(cmap, codeLo, codeHi, dim, (CID) dstCID);
} else
WARN("%s: Invalid CMap mapping record. (ignored)", CMAP_PARSE_DEBUG_STR);
pst_release_obj(tok);
}
return check_next_token(input, "endcidrange");
}
static int
do_notdefchar (CMap *cmap, ifreader *input, int count)
{
pst_obj *tok1, *tok2;
long dstCID;
while (count-- > 0) {
if (ifreader_need(input, TOKEN_LEN_MAX*2) == 0)
return -1;
if ((tok1 = pst_get_token(&(input->cursor), input->endptr)) == NULL)
return -1;
if ((tok2 = pst_get_token(&(input->cursor), input->endptr)) == NULL) {
pst_release_obj(tok1);
return -1;
}
if (PST_STRINGTYPE(tok1) && PST_INTEGERTYPE(tok2)) {
dstCID = pst_getIV(tok2);
if (dstCID >= 0 && dstCID <= CID_MAX)
CMap_add_notdefchar(cmap, pst_data_ptr(tok1), pst_length_of(tok1), (CID) dstCID);
} else
WARN("%s: Invalid CMap mapping record. (ignored)", CMAP_PARSE_DEBUG_STR);
pst_release_obj(tok1);
pst_release_obj(tok2);
}
return check_next_token(input, "endnotdefchar");
}
static int
do_bfchar (CMap *cmap, ifreader *input, int count)
{
pst_obj *tok1, *tok2;
while (count-- > 0) {
if (ifreader_need(input, TOKEN_LEN_MAX*2) == 0)
return -1;
if ((tok1 = pst_get_token(&(input->cursor), input->endptr)) == NULL)
return -1;
if ((tok2 = pst_get_token(&(input->cursor), input->endptr)) == NULL) {
pst_release_obj(tok1);
return -1;
}
if (PST_STRINGTYPE(tok1) && PST_STRINGTYPE(tok2)) {
CMap_add_bfchar(cmap,
(unsigned char *) pst_data_ptr(tok1), (int) pst_length_of(tok1),
(unsigned char *) pst_data_ptr(tok2), (int) pst_length_of(tok2));
} else if (PST_NAMETYPE(tok2))
ERROR("%s: Mapping to charName not supported.", CMAP_PARSE_DEBUG_STR);
else
WARN("%s: Invalid CMap mapping record. (ignored)", CMAP_PARSE_DEBUG_STR);
pst_release_obj(tok1);
pst_release_obj(tok2);
}
return check_next_token(input, "endbfchar");
}
static int
do_cidchar (CMap *cmap, ifreader *input, int count)
{
pst_obj *tok1, *tok2;
long dstCID;
while (count-- > 0) {
if (ifreader_need(input, TOKEN_LEN_MAX*2) == 0)
return -1;
if ((tok1 = pst_get_token(&(input->cursor), input->endptr)) == NULL)
return -1;
if ((tok2 = pst_get_token(&(input->cursor), input->endptr)) == NULL) {
pst_release_obj(tok1);
return -1;
}
if (PST_STRINGTYPE(tok1) && PST_INTEGERTYPE(tok2)) {
dstCID = pst_getIV(tok2);
if (dstCID >= 0 && dstCID <= CID_MAX)
CMap_add_cidchar(cmap, pst_data_ptr(tok1), pst_length_of(tok1), (CID) dstCID);
} else
WARN("%s: Invalid CMap mapping record. (ignored)", CMAP_PARSE_DEBUG_STR);
pst_release_obj(tok1);
pst_release_obj(tok2);
}
return check_next_token(input, "endcidchar");
}
#define MATCH_NAME(t,n) (PST_NAMETYPE((t)) && !memcmp(pst_data_ptr((t)),(n),strlen((n))))
#define MATCH_OP(t,n) (PST_UNKNOWNTYPE((t)) && !memcmp(pst_data_ptr((t)),(n),strlen((n))))
static int
do_cidsysteminfo (CMap *cmap, ifreader *input)
{
pst_obj *tok1, *tok2;
CIDSysInfo csi = {NULL, NULL, -1};
int simpledict = 0;
int error = 0;
ifreader_need(input, TOKEN_LEN_MAX*2);
while ((tok1 = pst_get_token(&(input->cursor), input->endptr)) != NULL) {
if (PST_MARKTYPE(tok1)) {
simpledict = 1;
pst_release_obj(tok1);
break;
} else if (MATCH_OP(tok1, "begin")) {
simpledict = 0;
pst_release_obj(tok1);
break;
} else {
pst_release_obj(tok1);
}
}
tok1 = tok2 = NULL;
while (!error &&
(tok1 = pst_get_token(&(input->cursor), input->endptr)) != NULL) {
if (MATCH_OP(tok1, ">>") && simpledict) {
pst_release_obj(tok1);
break;
} else if (MATCH_OP(tok1, "end") && !simpledict) {
pst_release_obj(tok1);
break;
} else if (MATCH_NAME(tok1, "Registry") &&
(tok2 = pst_get_token(&(input->cursor), input->endptr)) != NULL) {
if (!PST_STRINGTYPE(tok2))
error = -1;
else if (!simpledict &&
check_next_token(input, "def"))
error = -1;
if (!error)
csi.registry = (char *) pst_getSV(tok2);
} else if (MATCH_NAME(tok1, "Ordering") &&
(tok2 = pst_get_token(&(input->cursor), input->endptr)) != NULL) {
if (!PST_STRINGTYPE(tok2))
error = -1;
else if (!simpledict &&
check_next_token(input, "def"))
error = -1;
if (!error)
csi.ordering = (char *) pst_getSV(tok2);
} else if (MATCH_NAME(tok1, "Supplement") &&
(tok2 = pst_get_token(&(input->cursor), input->endptr)) != NULL) {
if (!PST_INTEGERTYPE(tok2))
error = -1;
else if (!simpledict &&
check_next_token(input, "def"))
error = -1;
if (!error)
csi.supplement = pst_getIV(tok2);
}
if (tok2)
pst_release_obj(tok2);
if (tok1)
pst_release_obj(tok1);
tok1 = tok2 = NULL;
}
if (!error &&
check_next_token(input, "def"))
error = -1;
if (!error &&
csi.registry && csi.ordering &&
csi.supplement >= 0) {
CMap_set_CIDSysInfo(cmap, &csi);
}
if (csi.registry)
RELEASE(csi.registry);
if (csi.ordering)
RELEASE(csi.ordering);
return error;
}
#define INPUT_BUF_SIZE 4096
#define CMAP_SIG_MAX 64
int
CMap_parse_check_sig (FILE *fp)
{
int result = -1;
char sig[CMAP_SIG_MAX+1];
if (!fp)
return -1;
rewind(fp);
if (fread(sig, sizeof(char), CMAP_SIG_MAX, fp) != CMAP_SIG_MAX)
result = -1;
else {
sig[CMAP_SIG_MAX] = 0;
if (strncmp(sig, "%!PS", 4))
result = -1;
else if (strstr(sig+4, "Resource-CMap"))
result = 0;
}
rewind(fp);
return result;
}
int
CMap_parse (CMap *cmap, FILE *fp)
{
pst_obj *tok1, *tok2;
ifreader *input;
int status = 0, tmpint = -1;
ASSERT(cmap && fp);
input = ifreader_create(fp, file_size(fp), INPUT_BUF_SIZE-1);
while (status >= 0) {
tok1 = tok2 = NULL;
ifreader_read(input, INPUT_BUF_SIZE/2);
tok1 = pst_get_token(&(input->cursor), input->endptr);
if (tok1 == NULL)
break;
else if (MATCH_NAME(tok1, "CMapName")) {
if ((tok2 = pst_get_token(&(input->cursor), input->endptr)) == NULL ||
!(PST_NAMETYPE(tok2) || PST_STRINGTYPE(tok2)) ||
check_next_token(input, "def") < 0)
status = -1;
else
CMap_set_name(cmap, pst_data_ptr(tok2));
} else if (MATCH_NAME(tok1, "CMapType")) {
if ((tok2 = pst_get_token(&(input->cursor), input->endptr)) == NULL ||
!PST_INTEGERTYPE(tok2) ||
check_next_token(input, "def") < 0)
status = -1;
else
CMap_set_type(cmap, pst_getIV(tok2));
} else if (MATCH_NAME(tok1, "WMode")) {
if ((tok2 = pst_get_token(&(input->cursor), input->endptr)) == NULL ||
!PST_INTEGERTYPE(tok2) ||
check_next_token(input, "def") < 0)
status = -1;
else
CMap_set_wmode(cmap, pst_getIV(tok2));
} else if (MATCH_NAME(tok1, "CIDSystemInfo")) {
status = do_cidsysteminfo(cmap, input);
} else if (MATCH_NAME(tok1, "Version") ||
MATCH_NAME(tok1, "UIDOffset") ||
MATCH_NAME(tok1, "XUID")) {
} else if (PST_NAMETYPE(tok1)) {
if ((tok2 = pst_get_token(&(input->cursor), input->endptr)) != NULL &&
MATCH_OP(tok2, "usecmap")) {
int id;
CMap *ucmap;
id = texpdf_CMap_cache_find(pst_data_ptr(tok1));
if (id < 0)
status = -1;
else {
ucmap = texpdf_CMap_cache_get(id);
CMap_set_usecmap(cmap, ucmap);
}
}
} else if (MATCH_OP(tok1, "begincodespacerange")) {
status = do_codespacerange(cmap, input, tmpint);
} else if (MATCH_OP(tok1, "beginnotdefrange")) {
status = do_notdefrange(cmap, input, tmpint);
} else if (MATCH_OP(tok1, "beginnotdefchar")) {
status = do_notdefchar(cmap, input, tmpint);
} else if (MATCH_OP(tok1, "beginbfrange")) {
status = do_bfrange(cmap, input, tmpint);
} else if (MATCH_OP(tok1, "beginbfchar")) {
status = do_bfchar(cmap, input, tmpint);
} else if (MATCH_OP(tok1, "begincidrange")) {
status = do_cidrange(cmap, input, tmpint);
} else if (MATCH_OP(tok1, "begincidchar")) {
status = do_cidchar(cmap, input, tmpint);
} else if (PST_INTEGERTYPE(tok1)) {
tmpint = pst_getIV(tok1);
}
if (tok1)
pst_release_obj(tok1);
if (tok2)
pst_release_obj(tok2);
}
ifreader_destroy(input);
return (status < 0) ? -1 : CMap_is_valid(cmap);
}