summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mupdf.h2
-rw-r--r--include/mupdf/function.h53
-rw-r--r--mupdf/function.c1613
-rw-r--r--mupdf/lex.c51
4 files changed, 1718 insertions, 1 deletions
diff --git a/include/mupdf.h b/include/mupdf.h
index a1633541..cc455f89 100644
--- a/include/mupdf.h
+++ b/include/mupdf.h
@@ -12,4 +12,4 @@
#include "mupdf/rsrc.h"
#include "mupdf/content.h"
#include "mupdf/page.h"
-
+#include "mupdf/function.h"
diff --git a/include/mupdf/function.h b/include/mupdf/function.h
new file mode 100644
index 00000000..483931b5
--- /dev/null
+++ b/include/mupdf/function.h
@@ -0,0 +1,53 @@
+typedef struct pdf_function_s pdf_function;
+typedef struct psobj_s psobj;
+
+struct pdf_function_s
+{
+ unsigned short type; /* 0=sample 2=exponential 3=stitching 4=postscript */
+ int m; /* number of input values */
+ int n; /* number of output values */
+ float *domain; /* even index : min value, odd index : max value */
+ float *range; /* even index : min value, odd index : max value */
+ union
+ {
+ struct {
+ unsigned short bps;
+ unsigned short order;
+ int *size; /* the num of samples in each input dimension */
+ float *encode;
+ float *decode;
+ int *samples;
+ } sa;
+ struct {
+ float n;
+ float *c0;
+ float *c1;
+ } e;
+ struct {
+ int k;
+ pdf_function **funcs;
+ float *bounds;
+ float *encode;
+ } st;
+ struct {
+ psobj *code;
+ int cap;
+ } p;
+ }u;
+};
+
+struct psobj_s
+{
+ unsigned short type;
+ union {
+ int booln; // boolean (stack only)
+ int intg; // integer (stack and code)
+ float real; // real (stack and code)
+ int op; // operator (code only)
+ int blk; // if/ifelse block pointer (code only)
+ };
+};
+
+fz_error *pdf_loadfunction(pdf_function **func, pdf_xref *xref, fz_obj *obj);
+fz_error *pdf_execfunction(pdf_function *func, float *in, int inlen, float *out, int outlen);
+void pdf_freefunc(pdf_function *func); \ No newline at end of file
diff --git a/mupdf/function.c b/mupdf/function.c
new file mode 100644
index 00000000..8d262b00
--- /dev/null
+++ b/mupdf/function.c
@@ -0,0 +1,1613 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+#define ARRAY_SIZE(a) sizeof(a) / sizeof(a[0])
+
+#define MIN_MAX(a,min,max) \
+ if(a < (min)) a = (min);\
+else if(a > (max)) a = (max);
+
+#define INTERPOLATE(x,xmin,xmax,ymin,ymax) \
+(ymin) + ((x)-(xmin)) * ((ymax)-(ymin)) / ((xmax) - (xmin));
+
+#define SAFE_PUSHINT(st,a)\
+ {err = pushint(st,a);\
+ if(err) goto cleanup;}
+
+#define SAFE_PUSHREAL(st,a)\
+ {err = pushreal(st,a);\
+ if(err) goto cleanup;}
+
+#define SAFE_PUSHBOOL(st,a)\
+ {err = pushbool(st,a);\
+ if(err) goto cleanup;}
+
+#define SAFE_POPINT(st,a)\
+ {err = popint(st,a);\
+ if(err) goto cleanup;}
+
+#define SAFE_POPNUM(st,a)\
+ {err = popnum(st,a);\
+ if(err) goto cleanup;}
+
+#define SAFE_POPBOOL(st,a)\
+ {err = popbool(st,a);\
+ if(err) goto cleanup;}
+
+#define SAFE_POP(st)\
+ {err = pop(st);\
+ if(err) goto cleanup;}
+
+#define SAFE_INDEX(st,i)\
+ {err = index(st,i);\
+ if(err) goto cleanup;}
+
+#define SAFE_COPY(st,n)\
+ {err = copy(st,n);\
+ if(err) goto cleanup;}
+
+#define RADIAN 57.2957795
+
+enum pdf_funckind_e
+{
+ PDF_FUNC_SAMPLE = 0,
+ PDF_FUNC_EXPONENTIAL = 2,
+ PDF_FUNC_STITCHING = 3,
+ PDF_FUNC_POSTSCRIPT = 4
+};
+
+enum psop_e {
+ psOpAbs,
+ psOpAdd,
+ psOpAnd,
+ psOpAtan,
+ psOpBitshift,
+ psOpCeiling,
+ psOpCopy,
+ psOpCos,
+ psOpCvi,
+ psOpCvr,
+ psOpDiv,
+ psOpDup,
+ psOpEq,
+ psOpExch,
+ psOpExp,
+ psOpFalse,
+ psOpFloor,
+ psOpGe,
+ psOpGt,
+ psOpIdiv,
+ psOpIndex,
+ psOpLe,
+ psOpLn,
+ psOpLog,
+ psOpLt,
+ psOpMod,
+ psOpMul,
+ psOpNe,
+ psOpNeg,
+ psOpNot,
+ psOpOr,
+ psOpPop,
+ psOpRoll,
+ psOpRound,
+ psOpSin,
+ psOpSqrt,
+ psOpSub,
+ psOpTrue,
+ psOpTruncate,
+ psOpXor,
+ psOpIf,
+ psOpIfelse,
+ psOpReturn
+};
+
+// Note: 'if' and 'ifelse' are parsed separately.
+// The rest are listed here in alphabetical order.
+// The index in this table is equivalent to the entry in PSOp.
+char *psOpNames[] = {
+ "abs",
+ "add",
+ "and",
+ "atan",
+ "bitshift",
+ "ceiling",
+ "copy",
+ "cos",
+ "cvi",
+ "cvr",
+ "div",
+ "dup",
+ "eq",
+ "exch",
+ "exp",
+ "false",
+ "floor",
+ "ge",
+ "gt",
+ "idiv",
+ "index",
+ "le",
+ "ln",
+ "log",
+ "lt",
+ "mod",
+ "mul",
+ "ne",
+ "neg",
+ "not",
+ "or",
+ "pop",
+ "roll",
+ "round",
+ "sin",
+ "sqrt",
+ "sub",
+ "true",
+ "truncate",
+ "xor"
+};
+
+enum psobjtype_e {
+ psBool,
+ psInt,
+ psReal,
+ psOperator,
+ psBlock
+};
+
+static int bps_supported[] = { 1,2,4,8,12,16,24,32 };
+
+/************************************************************************/
+/* Start Stack Impl */
+/************************************************************************/
+#define PSSTACKSIZE 100
+#define fz_stackoverflow fz_throw("syntaxerror : stackoverflow")
+#define fz_stackunderflow fz_throw("syntaxerror : stackunderflow")
+#define fz_stacktypemismatch fz_throw("syntaxerror : type mismatching")
+
+typedef struct psstack_s psstack;
+
+struct psstack_s
+{
+ psobj stack[PSSTACKSIZE];
+ int sp;
+};
+
+static void
+initstack(psstack *st)
+{
+ memset(st->stack,0,sizeof(st->stack));
+ st->sp = PSSTACKSIZE;
+}
+
+static int
+checkoverflow(psstack *st, int n)
+{
+ return st->sp >= n;
+}
+
+static int
+checkunderflow(psstack *st)
+{
+ return st->sp != PSSTACKSIZE;
+}
+
+static int
+checktype(psstack *st, unsigned short t1, unsigned short t2)
+{
+ return (st->stack[st->sp].type == t1 ||
+ st->stack[st->sp].type == t2);
+}
+
+static fz_error *
+pushbool(psstack *st, int booln)
+{
+ if (checkoverflow(st, 1)) {
+ st->stack[--st->sp].type = psBool;
+ st->stack[st->sp].booln = booln;
+ }
+ else
+ return fz_stackoverflow;
+
+ return nil;
+}
+
+static fz_error *
+pushint(psstack *st, int intg)
+{
+ if (checkoverflow(st, 1)) {
+ st->stack[--st->sp].type = psInt;
+ st->stack[st->sp].intg = intg;
+ }
+ else
+ return fz_stackoverflow;
+
+ return nil;
+}
+
+static fz_error *
+pushreal(psstack *st, float real)
+{
+ if (checkoverflow(st, 1)) {
+ st->stack[--st->sp].type = psReal;
+ st->stack[st->sp].real = real;
+ }
+ else
+ return fz_stackoverflow;
+
+ return nil;
+}
+
+static fz_error *
+popbool(psstack *st, int *booln)
+{
+ if (checkunderflow(st) && checktype(st, psBool, psBool)) {
+ *booln = st->stack[st->sp++].booln;
+ }
+ else if(checkunderflow(st))
+ return fz_stackunderflow;
+ else
+ return fz_stacktypemismatch;
+
+ return nil;
+}
+
+static fz_error *
+popint(psstack *st, int *intg)
+{
+ if (checkunderflow(st) && checktype(st, psInt, psInt)) {
+ *intg = st->stack[st->sp++].intg;
+ }
+ else if(checkunderflow(st))
+ return fz_stackunderflow;
+ else
+ return fz_stacktypemismatch;
+
+ return nil;
+}
+
+static fz_error *
+popnum(psstack *st, float *real)
+{
+ if (checkunderflow(st) && checktype(st, psInt, psReal)) {
+ float ret;
+ ret = (st->stack[st->sp].type == psInt) ?
+ (float)st->stack[st->sp].intg : st->stack[st->sp].real;
+ ++st->sp;
+ *real = ret;
+ }
+ else if(checkunderflow(st))
+ return fz_stackunderflow;
+ else
+ return fz_stacktypemismatch;
+
+ return nil;
+}
+
+static int
+empty(psstack *st)
+{
+ return st->sp == PSSTACKSIZE;
+}
+
+static int
+topisint(psstack *st)
+{
+ return st->sp < PSSTACKSIZE && st->stack[st->sp].type == psInt;
+}
+
+static int
+toptwoareints(psstack *st)
+{
+ return st->sp < PSSTACKSIZE - 1 &&
+ st->stack[st->sp].type == psInt &&
+ st->stack[st->sp+1].type == psInt;
+}
+
+static int
+topisreal(psstack *st)
+{
+ return st->sp < PSSTACKSIZE && st->stack[st->sp].type == psReal;
+}
+
+static int
+toptwoarenums(psstack *st)
+{
+ return st->sp < PSSTACKSIZE - 1 &&
+ (st->stack[st->sp].type == psInt || st->stack[st->sp].type == psReal) &&
+ (st->stack[st->sp+1].type == psInt || st->stack[st->sp+1].type == psReal);
+}
+
+static fz_error *
+copy(psstack *st, int n)
+{
+ int i;
+
+ if (!checkoverflow(st,n))
+ return fz_stackoverflow;
+
+ for (i = st->sp + n - 1; i <= st->sp; ++i) {
+ st->stack[i - n] = st->stack[i];
+ }
+ st->sp -= n;
+
+ return nil;
+}
+
+static void
+roll(psstack *st, int n, int j)
+{
+ psobj obj;
+ int i, k;
+
+ if (j >= 0) {
+ j %= n;
+ } else {
+ j = -j % n;
+ if (j != 0) {
+ j = n - j;
+ }
+ }
+ if (n <= 0 || j == 0) {
+ return;
+ }
+ for (i = 0; i < j; ++i) {
+ obj = st->stack[st->sp];
+ for (k = st->sp; k < st->sp + n - 1; ++k) {
+ st->stack[k] = st->stack[k+1];
+ }
+ st->stack[st->sp + n - 1] = obj;
+ }
+}
+
+static fz_error *
+index(psstack *st, int i)
+{
+ if (!checkoverflow(st, 1)) {
+ return fz_stackoverflow;
+ }
+ --st->sp;
+ st->stack[st->sp] = st->stack[st->sp + 1 + i];
+ return nil;
+}
+
+static fz_error *
+pop(psstack *st)
+{
+ if (!checkoverflow(st, 1)) {
+ return fz_stackoverflow;
+ }
+ ++st->sp;
+ return nil;
+}
+
+/************************************************************************/
+/* End Stack Impl */
+/************************************************************************/
+
+/* From Numerial recipes */
+static fz_error *
+spline(float x[], float y[], int n, float yp1, float ypn, float y2[])
+{
+ int i,k;
+ float p,qn,sig,un,*u;
+
+ u = fz_malloc(n*sizeof(float));
+ if(!u)
+ return fz_outofmem;
+
+ if (yp1 > 0.99e30)
+ y2[0]=u[0]=0.0;
+ else {
+ y2[0] = -0.5;
+ u[0]=(3.0/(x[1]-x[0]))*((y[1]-y[0])/(x[1]-x[0])-yp1);
+ }
+ for (i=1;i<n-1;i++) {
+ sig=(x[i]-x[i-1])/(x[i+1]-x[i-1]);
+ p=sig*y2[i-1]+2.0;
+ y2[i]=(sig-1.0)/p;
+ u[i]=(y[i+1]-y[i])/(x[i+1]-x[i]) - (y[i]-y[i-1])/(x[i]-x[i-1]);
+ u[i]=(6.0*u[i]/(x[i+1]-x[i-1])-sig*u[i-1])/p;
+ }
+ if (ypn > 0.99e30)
+ qn=un=0.0;
+ else {
+ qn=0.5;
+ un=(3.0/(x[n-1]-x[n-2]))*(ypn-(y[n-1]-y[n-2])/(x[n-1]-x[n-2]));
+ }
+ y2[n-1]=(un-qn*u[n-2])/(qn*y2[n-2]+1.0);
+ for (k=n-2;k>=0;k--)
+ y2[k]=y2[k]*y2[k+1]+u[k];
+
+ fz_free(u);
+ return nil;
+}
+
+static fz_error *
+splint(float xa[], float ya[], float y2a[], int n, float x, float *y)
+{
+ int k;
+ int klo=0;
+ int khi=n-1;
+ float h,b,a;
+
+ while (khi-klo > 1) {
+ k=(khi+klo) >> 1;
+ if (xa[k] > x) khi=k;
+ else klo=k;
+ }
+ h=xa[khi]-xa[klo];
+ if (h == 0.0) return fz_throw("Bad xa input to routine splint");
+ a=(xa[khi]-x)/h;
+ b=(x-xa[klo])/h;
+ *y=a*ya[klo]+b*ya[khi]+((a*a*a-a)*y2a[klo]
+ +(b*b*b-b)*y2a[khi])*(h*h)/6.0;
+
+ return nil;
+}
+
+static fz_error *
+loadsamplefunc(pdf_function *func, pdf_xref *xref, fz_obj *dict, int oid, int gid)
+{
+ fz_error *err = nil;
+ fz_obj *tmpobj;
+ int i;
+ int bps;
+ int samplecount, bytetoread;
+ int *size;
+ unsigned char *streamsamples = nil;
+ int *samples = nil;
+ float *encode;
+ float *decode;
+
+ /* required */
+ tmpobj = fz_dictgets(dict,"Size");
+ if(!fz_isarray(tmpobj) || fz_arraylen(tmpobj) != func->m)
+ goto cleanup;
+
+ size = fz_malloc(func->m * sizeof(int));
+ if(!size) return fz_outofmem;
+
+ for(i = 0; i < func->m; ++i)
+ size[i] = fz_toint(fz_arrayget(tmpobj,i));
+ func->u.sa.size = size;
+
+ /* required */
+ tmpobj = fz_dictgets(dict,"BitsPerSample");
+ if(!fz_isint(tmpobj))
+ goto cleanup;
+ func->u.sa.bps = bps = fz_toint(tmpobj);
+
+ for(i = 0; i < ARRAY_SIZE(bps_supported); ++i)
+ if(bps == bps_supported[i]) break;
+ if(i == ARRAY_SIZE(bps_supported))
+ goto cleanup;
+
+ /* optional */
+ tmpobj = fz_dictgets(dict, "Order");
+ if(!fz_isint(tmpobj))
+ func->u.sa.order = 1; /* default : linear interpolation */
+ else
+ func->u.sa.order = fz_toint(tmpobj);
+ if(func->u.sa.order != 1 && func->u.sa.order != 3)
+ goto cleanup;
+
+ if(func->u.sa.order == 3) {
+ for(i = 0; i < func->m; ++i)
+ if(size[i] < 4) {
+ func->u.sa.order = 1;
+ break;
+ }
+ }
+
+ /* optional */
+ tmpobj = fz_dictgets(dict, "Encode");
+ encode = fz_malloc(func->m*2 * sizeof(float));
+ if(!encode) return fz_outofmem;
+ if(fz_isarray(tmpobj)) {
+ if(fz_arraylen(tmpobj) != func->m*2)
+ goto cleanup;
+
+ for(i = 0; i < func->m; ++i) {
+ encode[i*2] = fz_toreal(fz_arrayget(tmpobj, i*2));
+ encode[i*2+1] = fz_toreal(fz_arrayget(tmpobj, i*2+1));
+ }
+ func->u.sa.encode = encode;
+ }
+ else {
+ for(i = 0; i < func->m; ++i) {
+ encode[i*2] = 0;
+ encode[i*2+1] = size[i] - 1;
+ }
+ }
+
+ /* optional */
+ tmpobj = fz_dictgets(dict, "Decode");
+ decode = fz_malloc(func->n*2 * sizeof(float));
+ if(!decode) return fz_outofmem;
+ if(fz_isarray(tmpobj)) {
+ if(fz_arraylen(tmpobj) != func->n*2)
+ goto cleanup;
+
+ for(i = 0; i < func->n; ++i) {
+ decode[i*2] = fz_toreal(fz_arrayget(tmpobj, i*2));
+ decode[i*2+1] = fz_toreal(fz_arrayget(tmpobj, i*2+1));
+ }
+ func->u.sa.decode = decode;
+ }
+ else {
+ for(i = 0; i < func->n; ++i) {
+ decode[i*2] = func->range[i*2];
+ encode[i*2+1] = func->range[i*2+1];
+ }
+ }
+
+ /* read samples from stream */
+ err = pdf_openstream(xref, oid, gid);
+ if (err) goto cleanup;
+
+ for(i = 0, samplecount = 1; i < func->m; ++i)
+ samplecount *= size[i];
+
+ bytetoread = (samplecount*bps + 7)/8;
+ streamsamples = fz_malloc(bytetoread);
+ samples = fz_malloc(samplecount * sizeof(int));
+ func->u.sa.samples = samples;
+ if(!streamsamples || !samples) { err = fz_outofmem; goto cleanup2; }
+
+ /* read samples */
+ {
+ int pos;
+ unsigned int bitMask = (1 << bps) - 1;
+ unsigned int buf = 0;
+ int bits = 0;
+ int s;
+
+ int readbyte =
+ fz_read(xref->stream, streamsamples, bytetoread);
+
+ if(readbyte != bytetoread)
+ {
+ err = fz_throw("syntaxerror : ");
+ goto cleanup2;
+ }
+
+ for (i = 0, pos = 0; i < samplecount; ++i) {
+ if (bps == 8) {
+ s = streamsamples[pos++];
+ } else if (samplecount == 16) {
+ s = streamsamples[pos++];
+ s = (s << 8) + streamsamples[pos++];
+ } else if (samplecount == 32) {
+ s = streamsamples[pos++];
+ s = (s << 8) + streamsamples[pos++];
+ s = (s << 8) + streamsamples[pos++];
+ s = (s << 8) + streamsamples[pos++];
+ } else {
+ while (bits < bps) {
+ buf = (buf << 8) | (streamsamples[pos++] & 0xff);
+ bits += 8;
+ }
+ s = (buf >> (bits - bps)) & bitMask;
+ bits -= bps;
+ }
+ samples[i] = s;
+ }
+ }
+
+cleanup2:
+ if(streamsamples)
+ fz_free(streamsamples);
+
+ pdf_closestream(xref);
+ return err;
+
+cleanup:
+ if(err) return err;
+ return fz_throw("syntaxerror : ");
+}
+
+static fz_error *
+execsamplefunc(pdf_function *func, float *in, float *out)
+{
+ int i , j, k, idx;
+ int e[2][func->m];
+ float efrac[func->m];
+ float s0[1 << func->m], s1[1 << func->m];
+ float x;
+ float *domain = func->domain;
+ float *encode = func->u.sa.encode;
+ float *range = func->range;
+ float *decode = func->u.sa.decode;
+ int *size = func->u.sa.size;
+
+ if(func->type != PDF_FUNC_SAMPLE)
+ goto cleanup;
+
+ switch(func->u.sa.order) {
+ case 3:
+ //cubic spline interpolation
+ case 1:
+ for (i = 0; i < func->m; ++i) {
+ x = in[i];
+ MIN_MAX(x,domain[i*2],domain[i*2+1]);
+
+ if(domain[i*2+1] != domain[i*2])
+ x = ((x - domain[i*2]) / (domain[i*2+1] - domain[i*2])) *
+ (encode[i*2+1] - encode[i*2]) + encode[i*2];
+
+ MIN_MAX(x,0,size[i] - 1);
+
+ e[0][i] = (int)floor(x);
+ e[1][i] = (int)ceil(x);
+ efrac[i] = x - e[0][i];
+ }
+
+ // for each output, do m-linear interpolation
+ for (i = 0; i < func->n; ++i) {
+ // pull 2^m values out of the sample array
+ for (j = 0; j < (1 << func->m); ++j) {
+ idx = 0;
+ for (k = func->m - 1; k >= 0; --k) {
+ idx = idx * func->u.sa.size[k] + e[(j >> k) & 1][k];
+ }
+ idx = idx * func->n + i;
+ s0[j] = func->u.sa.samples[idx];
+ }
+
+ // do m sets of interpolations
+ for (j = 0; j < func->m; ++j) {
+ for (k = 0; k < (1 << (func->m - j)); k += 2) {
+ s1[k >> 1] = (1 - efrac[j]) * s0[k] + efrac[j] * s0[k+1];
+ }
+ memcpy(s0, s1, (1 << (func->m - j - 1)) * sizeof(float));
+ }
+
+ // map output value to range
+ out[i] = s0[0] * (decode[i*2+1] - decode[i*2]) + decode[i*2];
+ MIN_MAX(out[i],range[i*2],range[i*2+1]);
+ }
+ break;
+ }
+ return nil;
+
+cleanup:
+ return fz_throw("syntaxerror : ");
+}
+
+static fz_error *
+loadexponentialfunc(pdf_function *func, fz_obj *dict)
+{
+ fz_error *err = nil;
+ fz_obj *tmpobj;
+ int i;
+ float *c0, *c1;
+
+ /* single input */
+ if(func->m != 1)
+ goto cleanup;
+
+ /* required */
+ tmpobj = fz_dictgets(dict,"N");
+ if(!fz_isint(tmpobj) && !fz_isreal(tmpobj))
+ goto cleanup;
+ func->u.e.n = fz_toreal(tmpobj);
+
+ /* optional */
+ tmpobj = fz_dictgets(dict,"C0");
+ if(fz_isarray(tmpobj)) {
+ if(func->range && fz_arraylen(tmpobj) != func->n)
+ goto cleanup;
+
+ func->n = fz_arraylen(tmpobj);
+ func->u.e.c0 = c0 = fz_malloc(func->n * sizeof(float));
+ if(!c0) { err = fz_outofmem; goto cleanup; }
+
+ fz_obj *objnum;
+ for(i = 0; i < func->n; ++i) {
+ objnum = fz_arrayget(tmpobj,i);
+ if(!fz_isint(objnum) && !fz_isreal(objnum))
+ goto cleanup;
+
+ c0[i] = fz_toreal(objnum);
+ }
+ }
+ else {
+ if(func->range && func->n != 1)
+ goto cleanup;
+
+ func->n = 1;
+ func->u.e.c0 = c0 = fz_malloc(func->n * sizeof(float));
+ if(!c0) { err = fz_outofmem; goto cleanup; }
+
+ c0[0] = 0;
+ }
+
+ /* optional */
+ tmpobj = fz_dictgets(dict,"C1");
+ func->u.e.c1 = c1 = fz_malloc(func->n * sizeof(float));
+ if(!c1) { err = fz_outofmem; goto cleanup; }
+ if(fz_isarray(tmpobj)) {
+ fz_obj *objnum;
+
+ if(fz_arraylen(tmpobj) != func->n)
+ goto cleanup;
+
+ for(i = 0; i < func->n; ++i) {
+ objnum = fz_arrayget(tmpobj,i);
+ if(!fz_isint(objnum) && !fz_isreal(objnum))
+ goto cleanup;
+
+ c1[i] = fz_toreal(objnum);
+ }
+ }
+ else {
+ if(func->n != 1)
+ goto cleanup;
+
+ c1[0] = 1;
+ }
+
+ return nil;
+cleanup:
+ if(err) return err;
+ return fz_throw("syntaxerror : ");
+}
+
+static fz_error *
+execexponentialfunc(pdf_function *func, float in, float *out)
+{
+ fz_error *err = nil;
+ float x = in;
+ float tmp;
+ int i;
+
+ if(func->type != PDF_FUNC_EXPONENTIAL)
+ goto cleanup;
+
+ MIN_MAX(x,func->domain[0],func->domain[1]);
+
+ /* constraint */
+ if(func->u.e.n != (int)func->u.e.n && x < 0)
+ goto cleanup;
+
+ if(func->u.e.n < 0 && x == 0)
+ goto cleanup;
+
+ tmp = pow(x, func->u.e.n);
+ for (i = 0; i < func->n; ++i) {
+ out[i] = func->u.e.c0[i] +
+ tmp * (func->u.e.c1[i] - func->u.e.c0[i]);
+ if (func->range) {
+ MIN_MAX(out[i],func->range[i*2],func->range[i*2+1]);
+ }
+ }
+
+ return nil;
+cleanup:
+ if(err) return err;
+ return fz_throw("syntaxerror : ");
+}
+
+static fz_error *
+loadstitchingfunc(pdf_function *func, pdf_xref *xref, fz_obj *dict)
+{
+ fz_error *err;
+ fz_obj *tmpobj;
+ fz_obj *funcobj;
+ fz_obj *numobj;
+ pdf_function **funcs;
+ float *bounds, *encode;
+ int i;
+ int k;
+
+ if(func->m != 1)
+ goto cleanup;
+
+ /* required */
+ tmpobj = fz_dictgets(dict,"Functions");
+ if(!fz_isarray(tmpobj))
+ goto cleanup;
+ k = fz_arraylen(tmpobj);
+
+ func->u.st.funcs = funcs = fz_malloc(k*sizeof(pdf_function*));
+ if(!funcs) { err = fz_outofmem; goto cleanup; }
+ memset(funcs, 0, k * sizeof(pdf_function*));
+
+ for(i = 0; i < k; ++i) {
+ funcobj = fz_arrayget(tmpobj,i);
+ err = pdf_loadfunction(funcs+i,xref,funcobj);
+ if(err) goto cleanup;
+ if(funcs[i]->m != 1 || funcs[i]->n != funcs[0]->n)
+ goto cleanup;
+ }
+
+ if(!func->range)
+ func->n = funcs[0]->n;
+ else if(func->n != funcs[0]->n)
+ goto cleanup;
+
+ /* required */
+ tmpobj = fz_dictgets(dict,"Bounds");
+ if(!fz_isarray(tmpobj) || fz_arraylen(tmpobj) != k-1)
+ goto cleanup;
+
+ func->u.st.bounds = bounds = fz_malloc((k-1) * sizeof(float));
+ if(!bounds) { err = fz_outofmem; goto cleanup; }
+
+ for(i = 0; i < k-1; ++i) {
+ numobj = fz_arrayget(tmpobj,i);
+ if(!fz_isint(numobj) && !fz_isreal(numobj))
+ goto cleanup;
+ bounds[i] = fz_toreal(numobj);
+ if(i && bounds[i-1] >= bounds[i])
+ goto cleanup;
+ }
+ if(k != 1 &&
+ (func->domain[0] >= bounds[0] || func->domain[1] <= bounds[k-2]))
+ goto cleanup;
+
+ /* required */
+ tmpobj = fz_dictgets(dict,"Encode");
+ if(!fz_isarray(tmpobj) || fz_arraylen(tmpobj) != k*2)
+ goto cleanup;
+
+ func->u.st.encode = encode = fz_malloc((k*2) * sizeof(float));
+ if(!encode) { err = fz_outofmem; goto cleanup; }
+
+ for(i = 0; i < k*2; ++i) {
+ numobj = fz_arrayget(tmpobj,i);
+ if(!fz_isint(numobj) && !fz_isreal(numobj))
+ goto cleanup;
+ encode[i] = fz_toreal(numobj);
+ }
+
+ func->u.st.k = k;
+
+ return nil;
+
+cleanup:
+ if(err) return err;
+ return fz_throw("syntaxerror : ");
+}
+
+static fz_error*
+execstitchinigfunc(pdf_function *func, float in, float *out)
+{
+ fz_error *err = nil;
+ float low,high;
+ int k = func->u.st.k;
+ float *bounds = func->u.st.bounds;
+ int i;
+
+ MIN_MAX(in,func->domain[0],func->domain[1]);
+
+ for(i = 0; i < k - 1; ++i) {
+ if (in < bounds[i])
+ break;
+ }
+ if(i == 0) {
+ low = func->domain[0];
+ high = bounds[0];
+ }
+ else if(i == k - 1) {
+ low = bounds[k-2];
+ high = func->domain[1];
+ }
+ else {
+ low = bounds[i-1];
+ high = bounds[i];
+ }
+
+ in = INTERPOLATE(in,low,high,
+ func->u.st.encode[i*2],func->u.st.encode[i*2 + 1]);
+
+ err = pdf_execfunction(func->u.st.funcs[i],&in,1,out,func->n);
+ if(err) return err;
+
+ return nil;
+}
+
+static fz_error *
+resizecode(pdf_function *func, int newsize) {
+ if (newsize >= func->u.p.cap) {
+ int newcodecap = func->u.p.cap + 64;
+ psobj *newcode;
+ newcode = fz_realloc(func->u.p.code, newcodecap * sizeof(psobj));
+ if(!newcode)
+ return fz_outofmem;
+ func->u.p.cap = newcodecap;
+ func->u.p.code = newcode;
+ }
+ return nil;
+}
+
+static fz_error *
+parsecode(pdf_function *func, fz_file *stream, int *codeptr)
+{
+ fz_error *err = nil;
+ char buf[64];
+ int buflen = sizeof(buf) / sizeof(buf[0]);
+ int len;
+ int token;
+ char *p;
+ int isReal;
+ int opPtr, elsePtr;
+ int a, b, mid, cmp;
+
+ memset(buf,0,sizeof(buf));
+
+ while (1) {
+ token = pdf_pslex(stream,buf,buflen,&len);
+ if(token == PDF_TERROR || token == PDF_TEOF)
+ goto cleanup;
+
+ switch(token)
+ {
+ case PDF_TINT:
+ resizecode(func,*codeptr);
+ func->u.p.code[*codeptr].type = psInt;
+ func->u.p.code[*codeptr].intg = atoi(buf);
+ ++*codeptr;
+ break;
+ case PDF_TREAL:
+ resizecode(func,*codeptr);
+ func->u.p.code[*codeptr].type = psReal;
+ func->u.p.code[*codeptr].real = atof(buf);
+ ++*codeptr;
+ break;
+ case PDF_TPSOPENBRACE:
+ opPtr = *codeptr;
+ *codeptr += 3;
+ resizecode(func,opPtr + 2);
+ err = parsecode(func, stream, codeptr);
+ if(err) goto cleanup;
+
+ token = pdf_pslex(stream,buf,buflen,&len);
+
+ if(token == PDF_TEOF || token == PDF_TERROR)
+ goto cleanup;
+
+ if(token == PDF_TPSOPENBRACE) {
+ elsePtr = *codeptr;
+ err = parsecode(func, stream, codeptr);
+ if(err) goto cleanup;
+ token = pdf_pslex(stream,buf,buflen,&len);
+ if(token == PDF_TERROR || token == PDF_TEOF)
+ goto cleanup;
+ }
+ else
+ elsePtr = -1;
+
+ if(token == PDF_TPSOPERATION) {
+ if(!strcmp(buf,"if")) {
+ if (elsePtr >= 0)
+ goto cleanup;
+ func->u.p.code[opPtr].type = psOperator;
+ func->u.p.code[opPtr].op = psOpIf;
+ func->u.p.code[opPtr+2].type = psBlock;
+ func->u.p.code[opPtr+2].blk = *codeptr;
+ }
+ else if(!strcmp(buf,"ifelse")) {
+ if (elsePtr < 0)
+ goto cleanup;
+ func->u.p.code[opPtr].type = psOperator;
+ func->u.p.code[opPtr].op = psOpIfelse;
+ func->u.p.code[opPtr+1].type = psBlock;
+ func->u.p.code[opPtr+1].blk = elsePtr;
+ func->u.p.code[opPtr+2].type = psBlock;
+ func->u.p.code[opPtr+2].blk = *codeptr;
+ }
+ else
+ goto cleanup;
+ }
+ else
+ goto cleanup;
+ break;
+ case PDF_TPSCLOSEBRACE:
+ resizecode(func,*codeptr);
+ func->u.p.code[*codeptr].type = psOperator;
+ func->u.p.code[*codeptr].op = psOpReturn;
+ ++*codeptr;
+ return nil;
+ case PDF_TPSOPERATION:
+ a = -1;
+ b = sizeof(psOpNames) / sizeof(psOpNames[0]);
+ // invariant: psOpNames[a] < op < psOpNames[b]
+ while (b - a > 1) {
+ mid = (a + b) / 2;
+ cmp = strcmp(buf,psOpNames[mid]);
+ if (cmp > 0) {
+ a = mid;
+ } else if (cmp < 0) {
+ b = mid;
+ } else {
+ a = b = mid;
+ }
+ }
+ if (cmp != 0)
+ goto cleanup;
+
+ resizecode(func,*codeptr);
+ func->u.p.code[*codeptr].type = psOperator;
+ func->u.p.code[*codeptr].op = a;
+ ++*codeptr;
+ break;
+ default:
+ goto cleanup;
+ }
+ }
+ return nil;
+cleanup:
+ if(err) return err;
+ return fz_throw("syntaxerror : postscript code");
+}
+
+static fz_error *
+loadpostscriptfunc(pdf_function *func, pdf_xref *xref,
+ fz_obj *dict, int oid, int gid)
+{
+ fz_error *err = nil;
+ fz_obj *tmpobj;
+ unsigned char *streamsamples = nil;
+ int codeptr;
+
+ /* read postcript from stream */
+ err = pdf_openstream(xref, oid, gid);
+ if (err) goto cleanup;
+ codeptr = 0;
+ if(fz_readbyte(xref->stream) != '{')
+ goto cleanup;
+
+ err = parsecode(func, xref->stream, &codeptr);
+ if(err) goto cleanup;
+
+ pdf_closestream(xref);
+ return nil;
+
+cleanup:
+ pdf_closestream(xref);
+ if(err) return err;
+ return fz_throw("syntaxerror : ");
+}
+
+static fz_error *
+execpostscriptfunc(pdf_function *func, psstack *st, int codeptr)
+{
+ fz_error *err = nil;
+ int i1, i2;
+ float r1, r2;
+ int b1, b2;
+
+ while (1) {
+ switch (func->u.p.code[codeptr].type) {
+ case psInt:
+ SAFE_PUSHINT(st,func->u.p.code[codeptr++].intg);
+ break;
+ case psReal:
+ SAFE_PUSHREAL(st,func->u.p.code[codeptr++].real);
+ break;
+ case psOperator:
+ switch (func->u.p.code[codeptr++].op) {
+ case psOpAbs:
+ if (topisint(st)) {
+ SAFE_POPINT(st,&i1);
+ SAFE_PUSHINT(st,abs(i1));
+ } else {
+ SAFE_POPNUM(st,&r1);
+ SAFE_PUSHREAL(st,fabs(r1));
+ }
+ break;
+ case psOpAdd:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ SAFE_PUSHINT(st,i1 + i2);
+ } else {
+ SAFE_POPNUM(st,&r2);
+ SAFE_POPNUM(st,&r1);
+ SAFE_PUSHREAL(st,r1 + r2);
+ }
+ break;
+ case psOpAnd:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st, &i2);
+ SAFE_POPINT(st, &i1);
+ SAFE_PUSHINT(st,i1 & i2);
+ } else {
+ SAFE_POPBOOL(st, &b2);
+ SAFE_POPBOOL(st, &b1);
+ SAFE_PUSHBOOL(st,b1 && b2);
+ }
+ break;
+ case psOpAtan:
+ SAFE_POPNUM(st, &r2);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st,atan2(r1, r2)*RADIAN);
+ break;
+ case psOpBitshift:
+ SAFE_POPINT(st, &i2);
+ SAFE_POPINT(st, &i1);
+ if (i2 > 0) {
+ SAFE_PUSHINT(st,i1 << i2);
+ } else if (i2 < 0) {
+ SAFE_PUSHINT(st,(int)((unsigned int)i1 >> i2));
+ } else {
+ SAFE_PUSHINT(st,i1);
+ }
+ break;
+ case psOpCeiling:
+ if (!topisint(st)) {
+ SAFE_POPNUM(st,&r1);
+ SAFE_PUSHREAL(st,ceil(r1));
+ }
+ break;
+ case psOpCopy:
+ SAFE_POPINT(st,&i1);
+ SAFE_COPY(st,i1);
+ break;
+ case psOpCos:
+ SAFE_POPNUM(st,&r1);
+ SAFE_PUSHREAL(st,cos(r1/RADIAN));
+ break;
+ case psOpCvi:
+ if (!topisint(st)) {
+ SAFE_POPNUM(st,&r1);
+ SAFE_PUSHINT(st,(int)r1);
+ }
+ break;
+ case psOpCvr:
+ if (!topisreal(st)) {
+ SAFE_POPNUM(st,&r1);
+ SAFE_PUSHREAL(st,r1);
+ }
+ break;
+ case psOpDiv:
+ SAFE_POPNUM(st, &r2);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st,r1 / r2);
+ break;
+ case psOpDup:
+ SAFE_COPY(st,1);
+ break;
+ case psOpEq:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st, &i2);
+ SAFE_POPINT(st, &i1);
+ SAFE_PUSHBOOL(st,i1 == i2);
+ } else if (toptwoarenums(st)) {
+ SAFE_POPNUM(st, &r1);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHBOOL(st,r1 == r2);
+ } else {
+ SAFE_POPBOOL(st, &b2);
+ SAFE_POPBOOL(st, &b2);
+ SAFE_PUSHBOOL(st,b1 == b2);
+ }
+ break;
+ case psOpExch:
+ roll(st,2, 1);
+ break;
+ case psOpExp:
+ SAFE_POPNUM(st, &r2);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st,pow(r1, r2));
+ break;
+ case psOpFalse:
+ SAFE_PUSHBOOL(st,0);
+ break;
+ case psOpFloor:
+ if (!topisint(st)) {
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st,floor(r1));
+ }
+ break;
+ case psOpGe:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st, &i2);
+ SAFE_POPINT(st, &i1);
+ SAFE_PUSHBOOL(st,i1 >= i2);
+ } else {
+ SAFE_POPNUM(st, &r2);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHBOOL(st,r1 >= r2);
+ }
+ break;
+ case psOpGt:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ SAFE_PUSHBOOL(st,i1 > i2);
+ } else {
+ SAFE_POPNUM(st, &r2);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHBOOL(st,r1 > r2);
+ }
+ break;
+ case psOpIdiv:
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ SAFE_PUSHINT(st,i1 / i2);
+ break;
+ case psOpIndex:
+ SAFE_POPINT(st, &i1);
+ SAFE_INDEX(st, i1);
+ break;
+ case psOpLe:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ SAFE_PUSHBOOL(st,i1 <= i2);
+ } else {
+ SAFE_POPNUM(st, &r2);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHBOOL(st,r1 <= r2);
+ }
+ break;
+ case psOpLn:
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st,log(r1));
+ break;
+ case psOpLog:
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st,log10(r1));
+ break;
+ case psOpLt:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ SAFE_PUSHBOOL(st,i1 < i2);
+ } else {
+ SAFE_POPNUM(st, &r2);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHBOOL(st,r1 < r2);
+ }
+ break;
+ case psOpMod:
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ SAFE_PUSHINT(st,i1 % i2);
+ break;
+ case psOpMul:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ //~ should check for out-of-range, and push a real instead
+ SAFE_PUSHINT(st,i1 * i2);
+ } else {
+ SAFE_POPNUM(st, &r2);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st,r1 * r2);
+ }
+ break;
+ case psOpNe:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st, &i2);
+ SAFE_POPINT(st, &i1);
+ SAFE_PUSHBOOL(st, i1 != i2);
+ } else if (toptwoarenums(st)) {
+ SAFE_POPNUM(st, &r2);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHBOOL(st, r1 != r2);
+ } else {
+ SAFE_POPBOOL(st, &b2);
+ SAFE_POPBOOL(st, &b1);
+ SAFE_PUSHBOOL(st,b1 != b2);
+ }
+ break;
+ case psOpNeg:
+ if (topisint(st)) {
+ SAFE_POPINT(st, &i1);
+ SAFE_PUSHINT(st, -i1);
+ } else {
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st, -r1);
+ }
+ break;
+ case psOpNot:
+ if (topisint(st)) {
+ SAFE_POPINT(st, &i1);
+ SAFE_PUSHINT(st, ~i1);
+ } else {
+ SAFE_POPBOOL(st, &b1);
+ SAFE_PUSHBOOL(st, !b1);
+ }
+ break;
+ case psOpOr:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ SAFE_PUSHINT(st,i1 | i2);
+ } else {
+ SAFE_POPBOOL(st, &b2);
+ SAFE_POPBOOL(st, &b1);
+ SAFE_PUSHBOOL(st,b1 || b2);
+ }
+ break;
+ case psOpPop:
+ SAFE_POP(st);
+ break;
+ case psOpRoll:
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ roll(st,i1, i2);
+ break;
+ case psOpRound:
+ if (!topisint(st)) {
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st,(r1 >= 0) ? floor(r1 + 0.5) : ceil(r1 - 0.5));
+ }
+ break;
+ case psOpSin:
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st, sin(r1/RADIAN));
+ break;
+ case psOpSqrt:
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st, sqrt(r1));
+ break;
+ case psOpSub:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ SAFE_PUSHINT(st,i1 - i2);
+ } else {
+ SAFE_POPNUM(st, &r2);
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st,r1 - r2);
+ }
+ break;
+ case psOpTrue:
+ SAFE_PUSHBOOL(st,1);
+ break;
+ case psOpTruncate:
+ if (!topisint(st)) {
+ SAFE_POPNUM(st, &r1);
+ SAFE_PUSHREAL(st,(r1 >= 0) ? floor(r1) : ceil(r1));
+ }
+ break;
+ case psOpXor:
+ if (toptwoareints(st)) {
+ SAFE_POPINT(st,&i2);
+ SAFE_POPINT(st,&i1);
+ SAFE_PUSHINT(st,i1 ^ i2);
+ } else {
+ SAFE_POPBOOL(st, &b2);
+ SAFE_POPBOOL(st, &b1);
+ SAFE_PUSHBOOL(st,b1 ^ b2);
+ }
+ break;
+ case psOpIf:
+ SAFE_POPBOOL(st, &b1);
+ if (b1) {
+ execpostscriptfunc(func, st, codeptr + 2);
+ }
+ codeptr = func->u.p.code[codeptr + 1].blk;
+ break;
+ case psOpIfelse:
+ SAFE_POPBOOL(st, &b1);
+ if (b1) {
+ execpostscriptfunc(func, st, codeptr + 2);
+ } else {
+ execpostscriptfunc(func, st, func->u.p.code[codeptr].blk);
+ }
+ codeptr = func->u.p.code[codeptr + 1].blk;
+ break;
+ case psOpReturn:
+ return nil;
+ }
+ break;
+ default:
+ return fz_throw("syntaxerror : ");
+ break;
+ }
+ }
+
+cleanup:
+ return err;
+}
+
+void
+pdf_freefunc(pdf_function *func)
+{
+ int i;
+
+ if(func->domain)
+ fz_free(func->domain);
+
+ if(func->range)
+ fz_free(func->range);
+
+ switch(func->type) {
+ case PDF_FUNC_SAMPLE:
+ if(func->u.sa.decode)
+ fz_free(func->u.sa.decode);
+ if(func->u.sa.encode)
+ fz_free(func->u.sa.encode);
+ if(func->u.sa.samples)
+ fz_free(func->u.sa.samples);
+ break;
+ case PDF_FUNC_EXPONENTIAL:
+ if(func->u.e.c0)
+ fz_free(func->u.e.c0);
+ if(func->u.e.c1)
+ fz_free(func->u.e.c1);
+ break;
+ case PDF_FUNC_STITCHING:
+ if(func->u.st.bounds)
+ fz_free(func->u.st.bounds);
+ if(func->u.st.encode)
+ fz_free(func->u.st.encode);
+ if(func->u.st.funcs) {
+ for(i = 0; i < func->u.st.k; ++i)
+ pdf_freefunc(func->u.st.funcs[i]);
+
+ fz_free(func->u.st.funcs);
+ }
+ break;
+ case PDF_FUNC_POSTSCRIPT:
+ if(func->u.p.code)
+ fz_free(func->u.p.code);
+ break;
+ }
+ fz_free(func);
+}
+
+fz_error *
+pdf_loadfunction(pdf_function **func, pdf_xref *xref, fz_obj *obj)
+{
+ fz_error *err = nil;
+ fz_obj *objfunc = nil;
+ fz_obj *tmpobj;
+ fz_obj *functype;
+ pdf_function *newfunc = nil;
+ int tmp;
+ int i;
+ float min,max;
+
+ newfunc = fz_malloc(sizeof(pdf_function));
+ if(!newfunc) return fz_outofmem;
+ memset(newfunc,0,sizeof(pdf_function));
+
+ objfunc = obj;
+ err = pdf_resolve(&objfunc,xref);
+ if(err) { objfunc = nil; goto cleanup; }
+
+ if(!fz_isdict(objfunc))
+ goto cleanup;
+
+ /* required */
+ tmpobj = fz_dictgets(objfunc,"FunctionType");
+ if(!fz_isint(tmpobj))
+ goto cleanup;
+ newfunc->type = fz_toint(tmpobj);
+
+ /* required */
+ tmpobj = fz_dictgets(objfunc,"Domain");
+ if(!fz_isarray(tmpobj))
+ goto cleanup;
+ tmp = fz_arraylen(tmpobj);
+ if(tmp % 2)
+ goto cleanup;
+ newfunc->m = tmp / 2;
+ newfunc->domain = fz_malloc(tmp * sizeof(float));
+ for (i = 0; i < tmp / 2; ++i)
+ {
+ min = fz_toreal(fz_arrayget(tmpobj, i*2));
+ max = fz_toreal(fz_arrayget(tmpobj, i*2+1));
+ if(min > max)
+ goto cleanup;
+ newfunc->domain[i*2] = min;
+ newfunc->domain[i*2+1] = max;
+ }
+
+ /* required for type0 and type4, optional otherwise */
+ tmpobj = fz_dictgets(objfunc,"Range");
+ if(fz_isarray(tmpobj)) {
+ tmp = fz_arraylen(tmpobj);
+ if(tmp % 2)
+ goto cleanup;
+ newfunc->n = tmp / 2;
+ newfunc->range = fz_malloc(tmp * sizeof(float));
+ for (i = 0; i < tmp / 2; ++i)
+ {
+ min = fz_toreal(fz_arrayget(tmpobj, i*2));
+ max = fz_toreal(fz_arrayget(tmpobj, i*2+1));
+ if(min > max)
+ goto cleanup;
+ newfunc->range[i*2] = min;
+ newfunc->range[i*2+1] = max;
+ }
+ }
+ else if(newfunc->type == PDF_FUNC_SAMPLE ||
+ newfunc->type == PDF_FUNC_POSTSCRIPT)
+ goto cleanup;
+
+ switch(newfunc->type)
+ {
+ case PDF_FUNC_SAMPLE:
+ if(!fz_isindirect(obj))
+ goto cleanup;
+ if(!pdf_isstream(xref, fz_tonum(obj), fz_togen(obj)))
+ goto cleanup;
+ err = loadsamplefunc(newfunc, xref, objfunc,
+ fz_tonum(obj), fz_togen(obj));
+ if(err) goto cleanup;
+ break;
+ case PDF_FUNC_EXPONENTIAL:
+ err = loadexponentialfunc(newfunc, objfunc);
+ if(err) goto cleanup;
+ break;
+ case PDF_FUNC_STITCHING:
+ err = loadstitchingfunc(newfunc, xref, objfunc);
+ if(err) goto cleanup;
+ break;
+ case PDF_FUNC_POSTSCRIPT:
+ if(!fz_isindirect(obj))
+ goto cleanup;
+ if(!pdf_isstream(xref, fz_tonum(obj), fz_togen(obj)))
+ goto cleanup;
+ err = loadpostscriptfunc(newfunc, xref, objfunc,
+ fz_tonum(obj), fz_togen(obj));
+ if(err) goto cleanup;
+ break;
+ default:
+ goto cleanup;
+ }
+
+ fz_dropobj(objfunc);
+
+ *func = newfunc;
+
+ return nil;
+
+cleanup:
+ if(objfunc)
+ fz_dropobj(objfunc);
+
+ pdf_freefunc(newfunc);
+
+ if(err) return err;
+ return fz_throw("syntaxerror : ");
+}
+
+fz_error *
+pdf_execfunction(pdf_function *func, float *in, int inlen, float *out, int outlen)
+{
+ fz_error *err = nil;
+ int i;
+
+ if(func->m != inlen || func->n != outlen)
+ return fz_throw("syntaxerror : input lenth or output length mismatch");
+
+ switch(func->type) {
+ case PDF_FUNC_SAMPLE:
+ err = execsamplefunc(func, in, out);
+ break;
+ case PDF_FUNC_EXPONENTIAL:
+ err = execexponentialfunc(func, *in, out);
+ break;
+ case PDF_FUNC_STITCHING:
+ err = execstitchinigfunc(func, *in, out);
+ break;
+ case PDF_FUNC_POSTSCRIPT:
+ {
+ psstack st;
+ initstack(&st);
+ for (i = 0; i < func->m; ++i)
+ SAFE_PUSHREAL(&st,in[i]);
+
+ err = execpostscriptfunc(func, &st, 0);
+ if(err) goto cleanup;
+
+ for (i = func->n - 1; i >= 0; --i) {
+ SAFE_POPNUM(&st,out+i);
+ MIN_MAX(out[i],func->range[i*2],func->range[i*2+1]);
+ }
+ }
+ break;
+ }
+cleanup:
+ return err;
+} \ No newline at end of file
diff --git a/mupdf/lex.c b/mupdf/lex.c
index d8ec7d75..5f09c45a 100644
--- a/mupdf/lex.c
+++ b/mupdf/lex.c
@@ -340,3 +340,54 @@ pdf_lex(fz_file *f, unsigned char *buf, int n, int *sl)
}
}
+int
+pdf_pslex(fz_file *f, unsigned char *buf, int n, int *sl)
+{
+ int c;
+
+ while (1)
+ {
+ c = fz_peekbyte(f);
+
+ if (c == EOF)
+ return PDF_TEOF;
+
+ else if (iswhite(c))
+ lexwhite(f);
+
+ else if (isnumber(c))
+ {
+ lexnumber(f, buf, n);
+ *sl = strlen(buf);
+ if (strchr(buf, '.'))
+ return PDF_TREAL;
+ return PDF_TINT;
+ }
+
+ else if (isregular(c))
+ {
+ lexname(f, buf, n);
+ *sl = strlen(buf);
+ return PDF_TPSOPERATION;
+ }
+
+ else if (c == '{')
+ {
+ buf[0] = fz_readbyte(f);
+ buf[1] = 0;
+ return PDF_TPSOPENBRACE;
+ }
+
+ else if (c == '}')
+ {
+ buf[0] = fz_readbyte(f);
+ buf[1] = 0;
+ return PDF_TPSCLOSEBRACE;
+ }
+
+ else
+ {
+ return PDF_TERROR;
+ }
+ }
+}