#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; static voidmatidentity(double *arr){ double src[6] = { 1, 0, 0, 1, 0, 0 }; memcpy(arr, src, sizeof(double) * 6);} static voidmatmult(double *m1, double *m2, double *out){ double result[6]; result[0] = m1[0] * m2[0] + m1[1] * m2[2]; result[1] = m1[0] * m2[1] + m1[1] * m2[3]; result[2] = m1[2] * m2[0] + m1[3] * m2[2]; result[3] = m1[2] * m2[1] + m1[3] * m2[3]; result[4] = m1[4] * m2[0] + m1[5] * m2[2] + m2[4]; result[5] = m1[4] * m2[1] + m1[5] * m2[3] + m2[5]; memcpy(out, result, sizeof(double) * 6);} struct Op { char *s; int (*f)(Op *op, Page *p); int argc; int flags;}; static intflagless(Op *op){ if(op->flags != 0){ fprint(2, "Op '%s' expected no flags\n", op->s); return 0; } return 1;} static intcobegin(Op *op, Page *p){ USED(op, p); return 0;} static intcoend(Op *op, Page *p){ USED(op, p); return 0;} static intgspush(Op *op, Page *p){ USED(op); GS *r = realloc(p->GS, sizeof(GS) * (p->nGS + 1)); if(r == nil) return 0; p->GS = r; p->nGS += 1; p->GSactive = &p->GS[p->nGS - 1]; *(p->GSactive) = p->GS[p->nGS - 2]; return 1;} static intgspop(Op *op, Page *p){ USED(op); GS *r = realloc(p->GS, sizeof(GS) * (p->nGS - 1)); if(r == nil) return 0; p->GS = r; p->nGS -= 1; p->GSactive = &p->GS[p->nGS - 1]; return 1;} /* six parameters give the inputs a,b,c,d,e,f for the matrix [a b 0] [c d 0] [e f 1] That matrix should be premultiplied with the current matrix newCTM = input x oldCTM (8.3.4) */static intgsctm(Op *op, Page *p){ double input[6]; int i; for(i = 0; i < 6; i += 1) input[i] = arrayget(p->stack, i)->num.d; matmult(input, p->GSactive->CTM, p->GSactive->CTM); return flagless(op);} static intgswidth(Op *op, Page *p){ p->GSactive->LW = arrayget(p->stack, 0)->num.i; return flagless(op);} static intgscap(Op *op, Page *p){ USED(op, p); return 0;} static intgsjoin(Op *op, Page *p){ USED(op, p); return 0;} static intgsmiterlim(Op *op, Page *p){ USED(op, p); return 0;} static intgsdash(Op *op, Page *p){ USED(op, p); return 0;} static intgsintent(Op *op, Page *p){ USED(op, p); return 0;} static intgsflatness(Op *op, Page *p){ p->GSactive->FL = arrayget(p->stack, 0)->num.d; return flagless(op);} static intgsstate(Op *op, Page *p){ USED(op, p); return 0;} static intpcmove(Op *op, Page *p){ USED(op, p); return 1;} static intpcline(Op *op, Page *p){ USED(op, p); return 1;} static intpccurve(Op *op, Page *p){ USED(op, p); return 1;} static intpcsubpath(Op *op, Page *p){ USED(op, p); return 0;} static intpcrect(Op *op, Page *p){ USED(op, p); return 0;} static intppstroke(Op *op, Page *p){ USED(op, p); return 1;} static intppstrokec(Op *op, Page *p){ USED(op, p); return 0;} static intppfill(Op *op, Page *p){ USED(op, p); return 0;} static intppfills(Op *op, Page *p){ USED(op, p); return 0;} static intppfillcfs(Op *op, Page *p){ USED(op, p); return 0;} static intppc(Op *op, Page *p){ USED(op, p); return 0;} static intcpclip(Op *op, Page *p){ USED(op, p); return 0;} static intcspace(Op *op, Page *p){ USED(op, p); return 0;} static intccolour(Op *op, Page *p){ USED(op, p); return 0;} static intccolour2(Op *op, Page *p){ USED(op, p); return 0;} static intcgray(Op *op, Page *p){ int value = 255 * arrayget(p->stack, 0)->num.d; int i; u32int *color; if(op->flags & Nonstroking){ color = &p->GSactive->NSC; p->GSactive->NSCS = DeviceGray; } else{ color = &p->GSactive->SC; p->GSactive->SCS = DeviceGray; } *color = 0; for(i = 0; i < 3; i += 1) *color = (*color | value) << 8; *color |= 255; return 1;} static intcrgb(Op *op, Page *p){ USED(op, p); return 0;} static intccmyk(Op *op, Page *p){ USED(op, p); return 0;} static intsshade(Op *op, Page *p){ USED(op, p); return 0;} static inteoobject(Op *op, Page *p){ USED(op, p); return 0;} static intiibegin(Op *op, Page *p){ USED(op, p); return 0;} static intiidata(Op *op, Page *p){ USED(op, p); return 0;} static intiiend(Op *op, Page *p){ USED(op, p); return 0;} static inttsspace(Op *op, Page *p){ USED(op, p); return 0;} static inttswspace(Op *op, Page *p){ USED(op, p); return 0;} static inttshscale(Op *op, Page *p){ USED(op, p); return 0;} static inttslead(Op *op, Page *p){ p->TS.TL = arrayget(p->stack, 0)->num.d; return flagless(op);} static intfontwidths(Page *p){ Object *o; int i; if(p->GSactive->Font.widths != nil) free(p->GSactive->Font.widths); o = dictget(p->GSactive->Font.font, "FirstChar"); if(o == nil) return 1; p->GSactive->Font.first = o->num.i; p->GSactive->Font.last = dictget(p->GSactive->Font.font, "LastChar")->num.i; p->GSactive->Font.widths = malloc(sizeof(int) * (p->GSactive->Font.last - p->GSactive->Font.first + 1)); if(p->GSactive->Font.widths == nil){ print("Failed to allocate for (%d, %d): %d\n", p->GSactive->Font.first, p->GSactive->Font.last, p->GSactive->Font.last - p->GSactive->Font.first + 1); return 1; } o = dictget(p->GSactive->Font.font, "Widths"); if(o == nil) return 0; for(i = 0; i < arraylen(o); i += 1) p->GSactive->Font.widths[i] = arrayget(o, i)->num.i; o = dictget(p->GSactive->Font.font, "FontDescriptor"); p->GSactive->Font.defwidth = dictget(o, "MissingWidth")->num.i; return 1;} static inttsfontsz(Op *op, Page *p){ char *name = arrayget(p->stack, 0)->name; p->GSactive->Font.font = dictget(dictget(dictget(p->obj, "Resources"), "Font"), name); if(p->GSactive->Font.font == nil){ werrstr("Font not found: '%s'", name); return 0; } p->GSactive->Font.enc = dictget(p->GSactive->Font.font, "Encoding"); if(p->GSactive->Font.enc) p->GSactive->Font.enc = dictget(p->GSactive->Font.enc, "Differences"); p->GSactive->Font.size = arrayget(p->stack, 1)->num.d; return fontwidths(p) && flagless(op);} static inttsrendmode(Op *op, Page *p){ USED(op, p); return 0;} static inttsrise(Op *op, Page *p){ USED(op, p); return 0;} static inttobegin(Op *op, Page *p){ if(p->TS.inobj){ werrstr("Text objects must not be nested"); return 0; } matidentity(p->TS.Tm); matidentity(p->TS.Tlm); p->TS.inobj = 1; p->GSactive->Font.font = nil; return flagless(op);} static inttoend(Op *op, Page *p){ if(!p->TS.inobj){ werrstr("ET found without BT"); return 0; } p->TS.inobj = 0; return flagless(op);} static inttmove(Page *p, double x, double y, int tlm){ double shift[6] = {1, 0, 0, 1, x, y}; if(tlm){ matmult(shift, p->TS.Tlm, p->TS.Tlm); memcpy(p->TS.Tm, p->TS.Tlm, sizeof(double) * 6); } else{ matmult(shift, p->TS.Tm, p->TS.Tm); } return 1;} static inttpmove(Op *op, Page *p){ Object *x, *y; x = arrayget(p->stack, 0); y = arrayget(p->stack, 1); if(op->flags & Leading) p->TS.TL = -y->num.d; return tmove(p, x->num.d, y->num.d, 1);} static inttpmatrix(Op *op, Page *p){ int i; for(i = 0; i < 6; i += 1){ p->TS.Tm[i] = arrayget(p->stack, i)->num.d; p->TS.Tlm[i] = p->TS.Tm[i]; } return flagless(op);} static inttpmove0(Op *op, Page *p){ return tmove(p, 0, 0 - p->TS.TL, 1) && flagless(op);} //TODO: replace this with a precomputed map when the font is loadedstatic intwritepatched(Page *p, uchar c){Lines 1–500 of 1327. Load more
#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; static void matidentity(double *arr) { double src[6] = { 1, 0, 0, 1, 0, 0 }; memcpy(arr, src, sizeof(double) * 6); } static void matmult(double *m1, double *m2, double *out) { double result[6]; result[0] = m1[0] * m2[0] + m1[1] * m2[2]; result[1] = m1[0] * m2[1] + m1[1] * m2[3]; result[2] = m1[2] * m2[0] + m1[3] * m2[2]; result[3] = m1[2] * m2[1] + m1[3] * m2[3]; result[4] = m1[4] * m2[0] + m1[5] * m2[2] + m2[4]; result[5] = m1[4] * m2[1] + m1[5] * m2[3] + m2[5]; memcpy(out, result, sizeof(double) * 6); } struct Op { char *s; int (*f)(Op *op, Page *p); int argc; int flags; }; static int flagless(Op *op) { if(op->flags != 0){ fprint(2, "Op '%s' expected no flags\n", op->s); return 0; } return 1; } 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); GS *r = realloc(p->GS, sizeof(GS) * (p->nGS + 1)); if(r == nil) return 0; p->GS = r; p->nGS += 1; p->GSactive = &p->GS[p->nGS - 1]; *(p->GSactive) = p->GS[p->nGS - 2]; return 1; } static int gspop(Op *op, Page *p) { USED(op); GS *r = realloc(p->GS, sizeof(GS) * (p->nGS - 1)); if(r == nil) return 0; p->GS = r; p->nGS -= 1; p->GSactive = &p->GS[p->nGS - 1]; return 1; } /* six parameters give the inputs a,b,c,d,e,f for the matrix [a b 0] [c d 0] [e f 1] That matrix should be premultiplied with the current matrix newCTM = input x oldCTM (8.3.4) */ static int gsctm(Op *op, Page *p) { double input[6]; int i; for(i = 0; i < 6; i += 1) input[i] = arrayget(p->stack, i)->num.d; matmult(input, p->GSactive->CTM, p->GSactive->CTM); return flagless(op); } static int gswidth(Op *op, Page *p) { p->GSactive->LW = arrayget(p->stack, 0)->num.i; return flagless(op); } 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) { p->GSactive->FL = arrayget(p->stack, 0)->num.d; return flagless(op); } static int gsstate(Op *op, Page *p) { USED(op, p); return 0; } static int pcmove(Op *op, Page *p) { USED(op, p); return 1; } static int pcline(Op *op, Page *p) { USED(op, p); return 1; } static int pccurve(Op *op, Page *p) { USED(op, p); return 1; } 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 1; } 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) { int value = 255 * arrayget(p->stack, 0)->num.d; int i; u32int *color; if(op->flags & Nonstroking){ color = &p->GSactive->NSC; p->GSactive->NSCS = DeviceGray; } else{ color = &p->GSactive->SC; p->GSactive->SCS = DeviceGray; } *color = 0; for(i = 0; i < 3; i += 1) *color = (*color | value) << 8; *color |= 255; return 1; } 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) { p->TS.TL = arrayget(p->stack, 0)->num.d; return flagless(op); } static int fontwidths(Page *p) { Object *o; int i; if(p->GSactive->Font.widths != nil) free(p->GSactive->Font.widths); o = dictget(p->GSactive->Font.font, "FirstChar"); if(o == nil) return 1; p->GSactive->Font.first = o->num.i; p->GSactive->Font.last = dictget(p->GSactive->Font.font, "LastChar")->num.i; p->GSactive->Font.widths = malloc(sizeof(int) * (p->GSactive->Font.last - p->GSactive->Font.first + 1)); if(p->GSactive->Font.widths == nil){ print("Failed to allocate for (%d, %d): %d\n", p->GSactive->Font.first, p->GSactive->Font.last, p->GSactive->Font.last - p->GSactive->Font.first + 1); return 1; } o = dictget(p->GSactive->Font.font, "Widths"); if(o == nil) return 0; for(i = 0; i < arraylen(o); i += 1) p->GSactive->Font.widths[i] = arrayget(o, i)->num.i; o = dictget(p->GSactive->Font.font, "FontDescriptor"); p->GSactive->Font.defwidth = dictget(o, "MissingWidth")->num.i; return 1; } static int tsfontsz(Op *op, Page *p) { char *name = arrayget(p->stack, 0)->name; p->GSactive->Font.font = dictget(dictget(dictget(p->obj, "Resources"), "Font"), name); if(p->GSactive->Font.font == nil){ werrstr("Font not found: '%s'", name); return 0; } p->GSactive->Font.enc = dictget(p->GSactive->Font.font, "Encoding"); if(p->GSactive->Font.enc) p->GSactive->Font.enc = dictget(p->GSactive->Font.enc, "Differences"); p->GSactive->Font.size = arrayget(p->stack, 1)->num.d; return fontwidths(p) && flagless(op); } 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) { if(p->TS.inobj){ werrstr("Text objects must not be nested"); return 0; } matidentity(p->TS.Tm); matidentity(p->TS.Tlm); p->TS.inobj = 1; p->GSactive->Font.font = nil; return flagless(op); } static int toend(Op *op, Page *p) { if(!p->TS.inobj){ werrstr("ET found without BT"); return 0; } p->TS.inobj = 0; return flagless(op); } static int tmove(Page *p, double x, double y, int tlm) { double shift[6] = {1, 0, 0, 1, x, y}; if(tlm){ matmult(shift, p->TS.Tlm, p->TS.Tlm); memcpy(p->TS.Tm, p->TS.Tlm, sizeof(double) * 6); } else{ matmult(shift, p->TS.Tm, p->TS.Tm); } return 1; } static int tpmove(Op *op, Page *p) { Object *x, *y; x = arrayget(p->stack, 0); y = arrayget(p->stack, 1); if(op->flags & Leading) p->TS.TL = -y->num.d; return tmove(p, x->num.d, y->num.d, 1); } static int tpmatrix(Op *op, Page *p) { int i; for(i = 0; i < 6; i += 1){ p->TS.Tm[i] = arrayget(p->stack, i)->num.d; p->TS.Tlm[i] = p->TS.Tm[i]; } return flagless(op); } static int tpmove0(Op *op, Page *p) { return tmove(p, 0, 0 - p->TS.TL, 1) && flagless(op); } //TODO: replace this with a precomputed map when the font is loaded static int writepatched(Page *p, uchar c) {