#include "libtexpdf.h"
#include <stdio.h>
#include <math.h>
#define CS_TYPE2_DEBUG_STR "Type2 Charstring Parser"
#define CS_TYPE2_DEBUG 5
#define CS_BUFFER_ERROR -3
#define CS_STACK_ERROR -2
#define CS_PARSE_ERROR -1
#define CS_PARSE_OK 0
#define CS_PARSE_END 1
#define CS_SUBR_RETURN 2
#define CS_CHAR_END 3
static int status = CS_PARSE_ERROR;
#define DST_NEED(a,b) {if ((a) < (b)) { status = CS_BUFFER_ERROR ; return ; }}
#define SRC_NEED(a,b) {if ((a) < (b)) { status = CS_PARSE_ERROR ; return ; }}
#define NEED(a,b) {if ((a) < (b)) { status = CS_STACK_ERROR ; return ; }}
static int num_stems = 0;
static int phase = 0;
static int nest = 0;
static int have_width = 0;
static double width = 0.0;
#if 0#endif
static int stack_top = 0;
static double arg_stack[CS_ARG_STACK_MAX];
static double trn_array[CS_TRANS_ARRAY_MAX];
#define cs_hstem 1
#define cs_vstem 3
#define cs_vmoveto 4
#define cs_rlineto 5
#define cs_hlineto 6
#define cs_vlineto 7
#define cs_rrcurveto 8
#define cs_callsubr 10
#define cs_return 11
#define cs_escape 12
#define cs_endchar 14
#define cs_hstemhm 18
#define cs_hintmask 19
#define cs_cntrmask 20
#define cs_rmoveto 21
#define cs_hmoveto 22
#define cs_vstemhm 23
#define cs_rcurveline 24
#define cs_rlinecurve 25
#define cs_vvcurveto 26
#define cs_hhcurveto 27
#define cs_callgsubr 29
#define cs_vhcurveto 30
#define cs_hvcurveto 31
#define cs_dotsection 0
#define cs_and 3
#define cs_or 4
#define cs_not 5
#define cs_abs 9
#define cs_add 10
#define cs_sub 11
#define cs_div 12
#define cs_neg 14
#define cs_eq 15
#define cs_drop 18
#define cs_put 20
#define cs_get 21
#define cs_ifelse 22
#define cs_random 23
#define cs_mul 24
#define cs_sqrt 26
#define cs_dup 27
#define cs_exch 28
#define cs_index 29
#define cs_roll 30
#define cs_hflex 34
#define cs_flex 35
#define cs_hflex1 36
#define cs_flex1 37
static void
clear_stack (card8 **dest, card8 *limit)
{
int i;
for (i = 0; i < stack_top; i++) {
double value;
long ivalue;
value = arg_stack[i];
ivalue = (long) floor(value+0.5);
if (value >= 0x8000L || value <= (-0x8000L - 1)) {
ERROR("%s: Argument value too large. (This is bug)", CS_TYPE2_DEBUG_STR);
} else if (fabs(value - ivalue) > 3.0e-5) {
DST_NEED(limit, *dest + 5);
*(*dest)++ = 255;
ivalue = (long) floor(value);
*(*dest)++ = (ivalue >> 8) & 0xff;
*(*dest)++ = ivalue & 0xff;
ivalue = (long)((value - ivalue) * 0x10000l);
*(*dest)++ = (ivalue >> 8) & 0xff;
*(*dest)++ = ivalue & 0xff;
} else if (ivalue >= -107 && ivalue <= 107) {
DST_NEED(limit, *dest + 1);
*(*dest)++ = ivalue + 139;
} else if (ivalue >= 108 && ivalue <= 1131) {
DST_NEED(limit, *dest + 2);
ivalue = 0xf700u + ivalue - 108;
*(*dest)++ = (ivalue >> 8) & 0xff;
*(*dest)++ = ivalue & 0xff;
} else if (ivalue >= -1131 && ivalue <= -108) {
DST_NEED(limit, *dest + 2);
ivalue = 0xfb00u - ivalue - 108;
*(*dest)++ = (ivalue >> 8) & 0xff;
*(*dest)++ = ivalue & 0xff;
} else if (ivalue >= -32768 && ivalue <= 32767) {
DST_NEED(limit, *dest + 3);
*(*dest)++ = 28;
*(*dest)++ = (ivalue >> 8) & 0xff;
*(*dest)++ = (ivalue) & 0xff;
} else {
ERROR("%s: Unexpected error.", CS_TYPE2_DEBUG_STR);
}
}
stack_top = 0;
return;
}
static void
do_operator1 (card8 **dest, card8 *limit, card8 **data, card8 *endptr)
{
card8 op = **data;
*data += 1;
switch (op) {
case cs_hstemhm:
case cs_vstemhm:
case cs_hstem:
case cs_vstem:
if (phase == 0 && (stack_top % 2)) {
have_width = 1;
width = arg_stack[0];
}
num_stems += stack_top/2;
clear_stack(dest, limit);
DST_NEED(limit, *dest + 1);
*(*dest)++ = op;
phase = 1;
break;
case cs_hintmask:
case cs_cntrmask:
if (phase < 2) {
if (phase == 0 && (stack_top % 2)) {
have_width = 1;
width = arg_stack[0];
}
num_stems += stack_top/2;
}
clear_stack(dest, limit);
DST_NEED(limit, *dest + 1);
*(*dest)++ = op;
if (num_stems > 0) {
int masklen = (num_stems + 7) / 8;
DST_NEED(limit, *dest + masklen);
SRC_NEED(endptr, *data + masklen);
memmove(*dest, *data, masklen);
*data += masklen;
*dest += masklen;
}
phase = 2;
break;
case cs_rmoveto:
if (phase == 0 && (stack_top % 2)) {
have_width = 1;
width = arg_stack[0];
}
clear_stack(dest, limit);
DST_NEED(limit, *dest + 1);
*(*dest)++ = op;
phase = 2;
break;
case cs_hmoveto:
case cs_vmoveto:
if (phase == 0 && (stack_top % 2) == 0) {
have_width = 1;
width = arg_stack[0];
}
clear_stack(dest, limit);
DST_NEED(limit, *dest + 1);
*(*dest)++ = op;
phase = 2;
break;
case cs_endchar:
if (stack_top == 1) {
have_width = 1;
width = arg_stack[0];
clear_stack(dest, limit);
} else if (stack_top == 4 || stack_top == 5) {
WARN("\"seac\" character deprecated in Type 2 charstring.");
status = CS_PARSE_ERROR;
return;
} else if (stack_top > 0) {
WARN("%s: Operand stack not empty.", CS_TYPE2_DEBUG_STR);
}
DST_NEED(limit, *dest + 1);
*(*dest)++ = op;
status = CS_CHAR_END;
break;
case cs_rlineto:
case cs_hlineto:
case cs_vlineto:
case cs_rrcurveto:
case cs_rcurveline:
case cs_rlinecurve:
case cs_vvcurveto:
case cs_hhcurveto:
case cs_vhcurveto:
case cs_hvcurveto:
if (phase < 2) {
WARN("%s: Broken Type 2 charstring.", CS_TYPE2_DEBUG_STR);
status = CS_PARSE_ERROR;
return;
}
clear_stack(dest, limit);
DST_NEED(limit, *dest + 1);
*(*dest)++ = op;
break;
case cs_return:
case cs_callgsubr:
case cs_callsubr:
ERROR("%s: Unexpected call(g)subr/return", CS_TYPE2_DEBUG_STR);
break;
default:
WARN("%s: Unknown charstring operator: 0x%02x", CS_TYPE2_DEBUG_STR, op);
status = CS_PARSE_ERROR;
break;
}
return;
}
static void
do_operator2 (card8 **dest, card8 *limit, card8 **data, card8 *endptr)
{
card8 op;
*data += 1;
SRC_NEED(endptr, *data + 1);
op = **data;
*data += 1;
switch(op) {
case cs_dotsection:
WARN("Operator \"dotsection\" deprecated in Type 2 charstring.");
status = CS_PARSE_ERROR;
return;
case cs_hflex:
case cs_flex:
case cs_hflex1:
case cs_flex1:
if (phase < 2) {
WARN("%s: Broken Type 2 charstring.", CS_TYPE2_DEBUG_STR);
status = CS_PARSE_ERROR;
return;
}
clear_stack(dest, limit);
DST_NEED(limit, *dest + 2);
*(*dest)++ = cs_escape;
*(*dest)++ = op;
break;
case cs_and:
NEED(stack_top, 2);
stack_top--;
if (arg_stack[stack_top] && arg_stack[stack_top-1]) {
arg_stack[stack_top-1] = 1.0;
} else {
arg_stack[stack_top-1] = 0.0;
}
break;
case cs_or:
NEED(stack_top, 2);
stack_top--;
if (arg_stack[stack_top] || arg_stack[stack_top-1]) {
arg_stack[stack_top-1] = 1.0;
} else {
arg_stack[stack_top-1] = 0.0;
}
break;
case cs_not:
NEED(stack_top, 1);
if (arg_stack[stack_top-1]) {
arg_stack[stack_top-1] = 0.0;
} else {
arg_stack[stack_top-1] = 1.0;
}
break;
case cs_abs:
NEED(stack_top, 1);
arg_stack[stack_top-1] = fabs(arg_stack[stack_top-1]);
break;
case cs_add:
NEED(stack_top, 2);
arg_stack[stack_top-2] += arg_stack[stack_top-1];
stack_top--;
break;
case cs_sub:
NEED(stack_top, 2);
arg_stack[stack_top-2] -= arg_stack[stack_top-1];
stack_top--;
break;
case cs_div:
NEED(stack_top, 2);
arg_stack[stack_top-2] /= arg_stack[stack_top-1];
stack_top--;
break;
case cs_neg:
NEED(stack_top, 1);
arg_stack[stack_top-1] *= -1.0;
break;
case cs_eq:
NEED(stack_top, 2);
stack_top--;
if (arg_stack[stack_top] == arg_stack[stack_top-1]) {
arg_stack[stack_top-1] = 1.0;
} else {
arg_stack[stack_top-1] = 0.0;
}
break;
case cs_drop:
NEED(stack_top, 1);
stack_top--;
break;
case cs_put:
NEED(stack_top, 2);
{
int idx = (int)arg_stack[--stack_top];
NEED(CS_TRANS_ARRAY_MAX, idx);
trn_array[idx] = arg_stack[--stack_top];
}
break;
case cs_get:
NEED(stack_top, 1);
{
int idx = (int)arg_stack[stack_top-1];
NEED(CS_TRANS_ARRAY_MAX, idx);
arg_stack[stack_top-1] = trn_array[idx];
}
break;
case cs_ifelse:
NEED(stack_top, 4);
stack_top -= 3;
if (arg_stack[stack_top+1] > arg_stack[stack_top+2]) {
arg_stack[stack_top-1] = arg_stack[stack_top];
}
break;
case cs_mul:
NEED(stack_top, 2);
arg_stack[stack_top-2] = arg_stack[stack_top-2] * arg_stack[stack_top-1];
stack_top--;
break;
case cs_sqrt:
NEED(stack_top, 1);
arg_stack[stack_top-1] = sqrt(arg_stack[stack_top-1]);
break;
case cs_dup:
NEED(stack_top, 1);
NEED(CS_ARG_STACK_MAX, stack_top+1);
arg_stack[stack_top] = arg_stack[stack_top-1];
stack_top++;
break;
case cs_exch:
NEED(stack_top, 2);
{
double save = arg_stack[stack_top-2];
arg_stack[stack_top-2] = arg_stack[stack_top-1];
arg_stack[stack_top-1] = save;
}
break;
case cs_index:
NEED(stack_top, 2);
{
int idx = (int)arg_stack[stack_top-1];
if (idx < 0) {
arg_stack[stack_top-1] = arg_stack[stack_top-2];
} else {
NEED(stack_top, idx+2);
arg_stack[stack_top-1] = arg_stack[stack_top-idx-2];
}
}
break;
case cs_roll:
NEED(stack_top, 2);
{
int N, J;
J = (int)arg_stack[--stack_top];
N = (int)arg_stack[--stack_top];
NEED(stack_top, N);
if (J > 0) {
J = J % N;
while (J-- > 0) {
double save = arg_stack[stack_top-1];
int i = stack_top - 1;
while (i > stack_top-N) {
arg_stack[i] = arg_stack[i-1];
i--;
}
arg_stack[i] = save;
}
} else {
J = (-J) % N;
while (J-- > 0) {
double save = arg_stack[stack_top-N];
int i = stack_top - N;
while (i < stack_top-1) {
arg_stack[i] = arg_stack[i+1];
i++;
}
arg_stack[i] = save;
}
}
}
break;
case cs_random:
WARN("%s: Charstring operator \"random\" found.", CS_TYPE2_DEBUG_STR);
NEED(CS_ARG_STACK_MAX, stack_top+1);
arg_stack[stack_top++] = 1.0;
break;
default:
WARN("%s: Unknown charstring operator: 0x0c%02x", CS_TYPE2_DEBUG_STR, op);
status = CS_PARSE_ERROR;
break;
}
return;
}
static void
get_integer (card8 **data, card8 *endptr)
{
long result = 0;
card8 b0 = **data, b1, b2;
*data += 1;
if (b0 == 28) {
SRC_NEED(endptr, *data + 2);
b1 = **data;
b2 = *(*data+1);
result = b1*256+b2;
if (result > 0x7fff)
result -= 0x10000L;
*data += 2;
} else if (b0 >= 32 && b0 <= 246) {
result = b0 - 139;
} else if (b0 >= 247 && b0 <= 250) {
SRC_NEED(endptr, *data + 1);
b1 = **data;
result = (b0-247)*256+b1+108;
*data += 1;
} else if (b0 >= 251 && b0 <= 254) {
SRC_NEED(endptr, *data + 1);
b1 = **data;
result = -(b0-251)*256-b1-108;
*data += 1;
} else {
status = CS_PARSE_ERROR;
return;
}
NEED(CS_ARG_STACK_MAX, stack_top+1);
arg_stack[stack_top++] = (double) result;
return;
}
static void
get_fixed (card8 **data, card8 *endptr)
{
long ivalue;
double rvalue;
*data += 1;
SRC_NEED(endptr, *data + 4);
ivalue = *(*data) * 0x100 + *(*data+1);
rvalue = (ivalue > 0x7fffL) ? (ivalue - 0x10000L) : ivalue;
ivalue = *(*data+2) * 0x100 + *(*data+3);
rvalue += ((double) ivalue) / 0x10000L;
NEED(CS_ARG_STACK_MAX, stack_top+1);
arg_stack[stack_top++] = rvalue;
*data += 4;
return;
}
static void
get_subr (card8 **subr, long *len, cff_index *subr_idx, long id)
{
card16 count;
if (subr_idx == NULL)
ERROR("%s: Subroutine called but no subroutine found.", CS_TYPE2_DEBUG_STR);
count = subr_idx->count;
if (count < 1240) {
id += 107;
} else if (count < 33900) {
id += 1131;
} else {
id += 32768;
}
if (id > count)
ERROR("%s: Invalid Subr index: %ld (max=%u)", CS_TYPE2_DEBUG_STR, id, count);
*len = (subr_idx->offset)[id + 1] - (subr_idx->offset)[id];
*subr = subr_idx->data + (subr_idx->offset)[id] - 1;
return;
}
static void
do_charstring (card8 **dest, card8 *limit,
card8 **data, card8 *endptr,
cff_index *gsubr_idx, cff_index *subr_idx)
{
card8 b0 = 0, *subr;
long len;
if (nest > CS_SUBR_NEST_MAX)
ERROR("%s: Subroutine nested too deeply.", CS_TYPE2_DEBUG_STR);
nest++;
while (*data < endptr && status == CS_PARSE_OK) {
b0 = **data;
if (b0 == 255) {
get_fixed(data, endptr);
} else if (b0 == cs_return) {
status = CS_SUBR_RETURN;
} else if (b0 == cs_callgsubr) {
if (stack_top < 1) {
status = CS_STACK_ERROR;
} else {
stack_top--;
get_subr(&subr, &len, gsubr_idx, (long) arg_stack[stack_top]);
if (*dest + len > limit)
ERROR("%s: Possible buffer overflow.", CS_TYPE2_DEBUG_STR);
do_charstring(dest, limit, &subr, subr + len,
gsubr_idx, subr_idx);
*data += 1;
}
} else if (b0 == cs_callsubr) {
if (stack_top < 1) {
status = CS_STACK_ERROR;
} else {
stack_top--;
get_subr(&subr, &len, subr_idx, (long) arg_stack[stack_top]);
if (limit < *dest + len)
ERROR("%s: Possible buffer overflow.", CS_TYPE2_DEBUG_STR);
do_charstring(dest, limit, &subr, subr + len,
gsubr_idx, subr_idx);
*data += 1;
}
} else if (b0 == cs_escape) {
do_operator2(dest, limit, data, endptr);
} else if (b0 < 32 && b0 != 28) {
do_operator1(dest, limit, data, endptr);
} else if ((b0 <= 22 && b0 >= 27) || b0 == 31) {
status = CS_PARSE_ERROR;
} else {
get_integer(data, endptr);
}
}
if (status == CS_SUBR_RETURN) {
status = CS_PARSE_OK;
} else if (status == CS_CHAR_END && *data < endptr) {
WARN("%s: Garbage after endchar.", CS_TYPE2_DEBUG_STR);
} else if (status < CS_PARSE_OK) {
ERROR("%s: Parsing charstring failed: (status=%d, stack=%d)",
CS_TYPE2_DEBUG_STR, status, stack_top);
}
nest--;
return;
}
static void
cs_parse_init (void)
{
status = CS_PARSE_OK;
nest = 0;
phase = 0;
num_stems = 0;
stack_top = 0;
}
long
cs_copy_charstring (card8 *dst, long dstlen,
card8 *src, long srclen,
cff_index *gsubr, cff_index *subr,
double default_width, double nominal_width, cs_ginfo *ginfo)
{
card8 *save = dst;
cs_parse_init();
width = 0.0;
have_width = 0;
do_charstring(&dst, dst + dstlen, &src, src + srclen, gsubr, subr);
if (ginfo) {
ginfo->flags = 0;
if (have_width) {
ginfo->wx = nominal_width + width;
} else {
ginfo->wx = default_width;
}
}
return (long)(dst - save);
}