shithub: pdffs

ref: ee8ac152c7ed0eb09e087212a4e521bb448c7831
dir: pdffs/op.c

View raw version
#include <u.h>
#include <libc.h>
#include "pdf.h"

enum {
	Evenodd = 1<<0,
	Nonstroking = 1<<1,
	Leading = 1<<2,
	Nextline = 1<<3,
	TwTc = 1<<4,
};

typedef struct Op Op;

struct Op {
	char *s;
	int (*f)(Op *op, Page *p);
	int argc;
	int flags;
};

static int
cobegin(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
coend(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gspush(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gspop(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gsctm(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gswidth(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gscap(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gsjoin(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gsmiterlim(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gsdash(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gsintent(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gsflatness(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
gsstate(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
pcmove(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
pcline(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
pccurve(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
pcsubpath(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
pcrect(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
ppstroke(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
ppstrokec(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
ppfill(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
ppfills(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
ppfillcfs(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
ppc(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
cpclip(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
cspace(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
ccolour(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
ccolour2(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
cgray(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
crgb(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
ccmyk(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
sshade(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
eoobject(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
iibegin(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
iidata(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
iiend(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
tsspace(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
tswspace(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
tshscale(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
tslead(Op *op, Page *p)
{
	int d = arrayget(p->stack, 0)->num.d / 20;
	while(d > 0){
		d -= 1;
		if(bufput(&p->buf, (uchar*)"\n", 1) == -1)
			sysfatal("OOM");
	}
	USED(op, p);
	return 0;
}

static int
tsfontsz(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
tsrendmode(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
tsrise(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
tobegin(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
toend(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
tpmove(Op *op, Page *p)
{
	Object *x, *y;
	x = arrayget(p->stack, 0);
	y = arrayget(p->stack, 1);
	if(y->num.d != 0){
		if(bufput(&p->buf, (uchar*)"\n", 1) == -1)
			sysfatal("OOM");
	}
	else if(x->num.d < 50)
		if(bufput(&p->buf, (uchar*)" ", 1) == -1)
			sysfatal("OOM");
	USED(op, p);
	return 0;
}

static int
tpmatrix(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
tpmove0(Op *op, Page *p)
{
	USED(op, p);
	if(bufput(&p->buf, (uchar*)"\n", 1) == -1)
		sysfatal("OOM");
	return 0;
}

static int
thshow(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
thshowarr(Op *op, Page *p)
{
	Object *arr = arrayget(p->stack, 0);
	Object *o;
	int i;
	for(i = 0; i < arraylen(arr); i += 1){
		o = arrayget(arr, i);
		if(o->type == Ostr){
			if(bufput(&p->buf, (uchar*)o->str, o->len) == -1)
				sysfatal("OOM");
		}
		else if(o->num.d < -150){
			if(bufput(&p->buf, (uchar*)" ", 1) == -1)
				sysfatal("OOM");
		}
	}
	USED(op);
	return 0;
}

static int
t3width(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t3widthbb(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4add(Op *op, Page *p)
{
	USED(op, p);
/*
	double x;
	x = objat(s+1, Onum)->num.d + objat(s+0, Onum)->num.d;
	s = pop(s);
	s->num.d = x;
	s->num.i = x;
*/
	return 0;
}

static int
t4sub(Op *op, Page *p)
{
	USED(op, p);
/*
	double x;
	x = objat(s+1, Onum)->num.d - objat(s+0, Onum)->num.d;
	s = pop(s);
	s->num.d = x;
	s->num.i = x;
*/
	return 0;
}

static int
t4mul(Op *op, Page *p)
{
	USED(op, p);
/*
	double x;

	x = objat(s+1, Onum)->num.d * objat(s+0, Onum)->num.d;
	s = pop(s);
	s->num.d = x;
	s->num.i = x;
*/
	return 0;
}

static int
t4div(Op *op, Page *p)
{
	USED(op, p);
/*
	double x;

	x = objat(s+1, Onum)->num.d / objat(s+0, Onum)->num.d;
	s = pop(s);
	s->num.d = x;
	s->num.i = x;
*/
	return 0;
}

static int
t4idiv(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4mod(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4neg(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4abs(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4ceiling(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4floor(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4round(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4truncate(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4sqrt(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4sin(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4cos(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4atan(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4exp(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4ln(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4log(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4cvi(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4cvr(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4eq(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4ne(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4gt(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4ge(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4lt(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4le(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4and(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4or(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4xor(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4not(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4bitshift(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4true(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4false(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4if(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4ifelse(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4pop(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4exch(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4dup(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4copy(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4index(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
t4roll(Op *op, Page *p)
{
	USED(op, p);
	return 0;
}

static int
opignore(Op *op, Page *p)
{
	USED(op, p);
	return 1;
}

static Op ops[] = {
	/* 7.8.2 Compatibility operators */
	{"BX", cobegin, 0,}, /* begin a compatilibity section */
	{"EX", coend, 0,},   /* end a compatilibity section */

	/* 8.4.4 Graphics state operators */
	{"q", gspush, 0,},   /* push the current graphics state (gs) on gs stack */
	{"Q", gspop, 0,},    /* pop ^ */
	{"cm", gsctm, 6,},     /* current transformation matrix (ctm) */
	{"w", gswidth, 1,},    /* line width */
	{"J", gscap, 1,},      /* line cap style */
	{"j", gsjoin, 1,},     /* line join style */
	{"M", gsmiterlim, 1,}, /* miter limit */
	{"d", gsdash, 2,},     /* line dash pattern */
	{"ri", gsintent, 1,},  /* colour rendering intent */
	{"i", gsflatness, 1,}, /* flatness tolerance */
	{"gs", gsstate, 1,},   /* graphics state parameters */

	/* 8.5.2 Path construction operators */
	{"m", pcmove, 2,},   /* move to coords */
	{"l", pcline, 2,},   /* straight line to coords */
	{"c", pccurve, 6,},  /* Bézier curve */
	{"v", pccurve, 4,},  /* Bézier curve */
	{"y", pccurve, 4,},  /* Bézier curve */
	{"h", pcsubpath, 0}, /* close subpath */
	{"re", pcrect, 4,},  /* rectangle */

	/* 8.5.3 Path painting operators */
	{"S", ppstroke, 0,},            /* stroke the path */
	{"s", ppstrokec, 0,},           /* close and stroke */
	{"f", ppfill, 0,},              /* fill */
	{"F", ppfill, 0,},              /* same damn thing, but DEPRECATED */
	{"f*", ppfill, 0, Evenodd,},    /* fill, even/odd rule */
	{"B", ppfills, 0,},             /* fill and stroke */
	{"B*", ppfills, 0, Evenodd,},   /* fill and stroke, even/odd rule */
	{"b", ppfillcfs, 0,},           /* close, fill and stroke */
	{"b*", ppfillcfs, 0, Evenodd,}, /* close, fill and stroke, even/odd rule */
	{"n", ppc, 0, 0},               /* end the path */

	/* 8.5.4 Clipping path operators */
	{"W", cpclip, 0,},           /* clip */
	{"W*", cpclip, 0, Evenodd,}, /* clip, even/odd rule */

	/* 8.6.8 Colour operators */
	{"CS", cspace, 1,},               /* colour space */
	{"cs", cspace, 1, Nonstroking,},  /* colour space, nonstroking */
	{"SC", ccolour, -1,},              /* colour */
	{"sc", ccolour, -1, Nonstroking,}, /* colour, nonstroking */
	{"SCN", ccolour2, -1,},            /* color (more spaces) */
	{"scn", ccolour2, -1,},            /* color (more spaces), nonstroking */
	{"G", cgray, 1,},                 /* gray */
	{"g", cgray, 1, Nonstroking,},    /* gray, nonstroking */
	{"RG", crgb, 3,},                 /* RGB */
	{"rg", crgb, 3, Nonstroking,},    /* RGB, nonstroking */
	{"K", ccmyk, 4,},                 /* CMYK */
	{"k", ccmyk, 4, Nonstroking,},    /* CMYK, nonstroking */

	/* 8.7.4.2 Shading operator */
	{"sh", sshade, 1,}, /* shading */

	/* 8.8 External objects */
	{"Do", eoobject, 1,}, /* paint XObject */

	/* 8.9.7 Inline images */
	{"BI", iibegin, 0,}, /* begin */
	{"ID", iidata, 0,},  /* data */
	{"EI", iiend, 0,},   /* end */

	/* 9.3.1 Text state parameters */
	{"Tc", tsspace, 1,},    /* spacing */
	{"Tw", tswspace, 1,},   /* word spacing */
	{"Tz", tshscale, 1,},   /* horizontal spacing */
	{"TL", tslead, 1,},     /* leading */
	{"Tf", tsfontsz, 1,},   /* font size */
	{"Tr", tsrendmode, 1,}, /* rendeing mode */
	{"Ts", tsrise, 1,},     /* rise */

	/* 9.4.1 Text objects */
	{"BT", tobegin, 0,}, /* begin */
	{"ET", toend, 0,},   /* end */

	/* 9.4.2 Text position operators */
	{"Td", tpmove, 2,},           /* move, next line */
	{"TD", tpmove, 2, Leading,},  /* move, next line, leading */
	{"Tm", tpmatrix, 6,},         /* (line) matrix */
	{"T*", tpmove0, 0, Leading,}, /* move, next line, leading */

	/* 9.4.3 Text showing operators */
	{"Tj", thshow, 1,},                /* show string */
	{"'", thshow, 1, Nextline,},       /* next line & show */
	{"\"", thshow, 3, Nextline|TwTc,}, /* next line, Tw, Tc & show */
	{"TJ", thshowarr, 1,},             /* show array */

	/* 9.6.4 Type 3 font operators */
	{"d0", t3width, 2,},   /* width info */
	{"d1", t3widthbb, 6,}, /* width & bounding box */

	/* 14.6 Marked content */
	{"BDC", opignore, 2,},
	{"EMC", opignore, 0,},

	/* 7.10.5.2 Operators and operands */
	/* B.2 Arithmetic operators */
	{"add", t4add, 2,},
	{"sub", t4sub, 2,},
	{"mul", t4mul, 2,},
	{"div", t4div, 2,},
	{"idiv", t4idiv, 2,},
	{"mod", t4mod, 2,},
	{"neg", t4neg, 1,},
	{"abs", t4abs, 1,},
	{"ceiling", t4ceiling, 1,},
	{"floor", t4floor, 1,},
	{"round", t4round, 1,},
	{"truncate", t4truncate, 1,},
	{"sqrt", t4sqrt, 1,},
	{"sin", t4sin, 1,},
	{"cos", t4cos, 1,},
	{"atan", t4atan, 2,},
	{"exp", t4exp, 2,},
	{"ln", t4ln, 1,},
	{"log", t4log, 1,},
	{"cvi", t4cvi, 1,},
	{"cvr", t4cvr, 1,},
	/* B.3 Relational, boolean, and bitwise operators */
	{"eq", t4eq, 2,},
	{"ne", t4ne, 2,},
	{"gt", t4gt, 2,},
	{"ge", t4ge, 2,},
	{"lt", t4lt, 2,},
	{"le", t4le, 2,},
	{"and", t4and, 2,},
	{"or", t4or, 2,},
	{"xor", t4xor, 2,},
	{"not", t4not, 1,},
	{"bitshift", t4bitshift, 2,},
	{"true", t4true, 0,},
	{"false", t4false, 0,},
	/* B.4 Conditional operators */
	{"if", t4if, 2,},
	{"ifelse", t4ifelse, 3,},
	/* B.5 Stack operators */
	{"pop", t4pop, 1,},
	{"exch", t4exch, 2,},
	{"dup", t4dup, 1,},
	{"copy", t4copy, -1,},
	{"index", t4index, -1,},
	{"roll", t4roll, -2,},

	/* terminator */
	{nil, nil, 0,},
};

// If an op is found at the current position in the stream, the associated Op is
// returned and the stream is advanced. Otherwise, nil is returned and the stream
// is left unchanged.
Op *
opfind(Stream *s)
{
	int i;
	uint len;
	Op *op;
	char *b = (char*)s->buf.b + s->buf.off;
	i = 0;
	while(ops[i].s != nil){
		op = &ops[i];
		len = strlen(op->s);
		if(strncmp(op->s, b, len) == 0 && (isws(b[len]) || isdelim(b[len]))){
			s->buf.off += len;
			return op;
		}
		i += 1;
	}
	return nil;
}

void
pageinit(Page *page)
{
	bufinit(&page->buf, 0, 0);
	// Stack is per-content-stream, so we don't create it here
	page->stack = nil;
}

void
pagefree(Page *p)
{
	buffree(&p->buf);
	pdfobjfree(p->stack);
}

static void
stackreset(Object *stack)
{
	int i;
	for(i = 0; i < stack->array.ne; i += 1)
		pdfobjfree(stack->array.e[i]);
	stack->array.ne = 0;
	free(stack->array.e);
	stack->array.e = nil;
}

static void
pagerendercontent(Page *p, Object *content)
{
	Stream *s;
	Object *o;
	Op *op;
	s = Sopen(content);
	if(s == nil){
		fprint(2, "%O\n", content);
		sysfatal("%r");
	}
	p->stack = arraynew(content->pdf);
	if(p->stack == nil)
		return;
	while(s->buf.off != s->buf.sz){
		while(isws(s->buf.b[s->buf.off]) && s->buf.off != s->buf.sz)
			s->buf.off += 1;
		if(s->buf.off == s->buf.sz)
			break;
		op = opfind(s);
		if(op != nil){
			op->f(op, p);
			stackreset(p->stack);
		} else{
			o = pdfobj(content->pdf, s);
			if(o == nil){
				fprint(2, "failed to read operand: %r\n");
				break;
			}
			if(!arrayadd(p->stack, o)){
				fprint(2, "Failed to push operand to stack: %r\n");
				break;
			}
		}
	}
	if(bufput(&p->buf, (uchar*)"\n", 1) == -1)
		sysfatal("OOM");
	Sclose(s);
}

int
pagerender(Page *p, Object *o)
{
	Object *content;
	int i;
	content = dictget(o, "Contents");
	if(content->type == Oarray)
		for(i = 0; i < arraylen(content); i += 1)
			pagerendercontent(p, arrayget(content, i));
	else if(content->type != Onull)
		pagerendercontent(p, content);
	return 1;
}