ref: 12d4d90f79385a358e03d3a6d5e8bbec06aeeda5
dir: /op.c/
#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; }