diff options
Diffstat (limited to 'pdf/pdf_interpret.c')
-rw-r--r-- | pdf/pdf_interpret.c | 1464 |
1 files changed, 1464 insertions, 0 deletions
diff --git a/pdf/pdf_interpret.c b/pdf/pdf_interpret.c new file mode 100644 index 00000000..dbeeb997 --- /dev/null +++ b/pdf/pdf_interpret.c @@ -0,0 +1,1464 @@ +#include "fitz.h" +#include "mupdf.h" + +static pdf_csi * +pdf_newcsi(pdf_xref *xref, fz_device *dev, fz_matrix ctm, char *target) +{ + pdf_csi *csi; + + csi = fz_malloc(sizeof(pdf_csi)); + csi->xref = xref; + csi->dev = dev; + csi->target = target; + + csi->top = 0; + csi->obj = nil; + csi->name[0] = 0; + csi->stringlen = 0; + memset(csi->stack, 0, sizeof csi->stack); + + csi->xbalance = 0; + csi->intext = 0; + csi->inarray = 0; + + csi->path = fz_newpath(); + csi->clip = 0; + csi->clipevenodd = 0; + + csi->text = nil; + csi->tlm = fz_identity; + csi->tm = fz_identity; + csi->textmode = 0; + csi->accumulate = 1; + + csi->topctm = ctm; + pdf_initgstate(&csi->gstate[0], ctm); + csi->gtop = 0; + + return csi; +} + +static void +pdf_clearstack(pdf_csi *csi) +{ + int i; + + if (csi->obj) + fz_dropobj(csi->obj); + csi->obj = nil; + + csi->name[0] = 0; + csi->stringlen = 0; + for (i = 0; i < csi->top; i++) + csi->stack[i] = 0; + + csi->top = 0; +} + +pdf_material * +pdf_keepmaterial(pdf_material *mat) +{ + if (mat->colorspace) + fz_keepcolorspace(mat->colorspace); + if (mat->pattern) + pdf_keeppattern(mat->pattern); + if (mat->shade) + fz_keepshade(mat->shade); + return mat; +} + +pdf_material * +pdf_dropmaterial(pdf_material *mat) +{ + if (mat->colorspace) + fz_dropcolorspace(mat->colorspace); + if (mat->pattern) + pdf_droppattern(mat->pattern); + if (mat->shade) + fz_dropshade(mat->shade); + return mat; +} + +void +pdf_gsave(pdf_csi *csi) +{ + pdf_gstate *gs = csi->gstate + csi->gtop; + + if (csi->gtop == nelem(csi->gstate) - 1) + { + fz_warn("gstate overflow in content stream"); + return; + } + + memcpy(&csi->gstate[csi->gtop + 1], &csi->gstate[csi->gtop], sizeof(pdf_gstate)); + + csi->gtop ++; + + pdf_keepmaterial(&gs->stroke); + pdf_keepmaterial(&gs->fill); + if (gs->font) + pdf_keepfont(gs->font); + if (gs->softmask) + pdf_keepxobject(gs->softmask); +} + +void +pdf_grestore(pdf_csi *csi) +{ + pdf_gstate *gs = csi->gstate + csi->gtop; + int clipdepth = gs->clipdepth; + + if (csi->gtop == 0) + { + fz_warn("gstate underflow in content stream"); + return; + } + + pdf_dropmaterial(&gs->stroke); + pdf_dropmaterial(&gs->fill); + if (gs->font) + pdf_dropfont(gs->font); + if (gs->softmask) + pdf_dropxobject(gs->softmask); + + csi->gtop --; + + gs = csi->gstate + csi->gtop; + while (clipdepth > gs->clipdepth) + { + csi->dev->popclip(csi->dev->user); + clipdepth--; + } +} + +static void +pdf_freecsi(pdf_csi *csi) +{ + while (csi->gtop) + pdf_grestore(csi); + + pdf_dropmaterial(&csi->gstate[0].fill); + pdf_dropmaterial(&csi->gstate[0].stroke); + if (csi->gstate[0].font) + pdf_dropfont(csi->gstate[0].font); + if (csi->gstate[0].softmask) + pdf_dropxobject(csi->gstate[0].softmask); + + while (csi->gstate[0].clipdepth--) + csi->dev->popclip(csi->dev->user); + + if (csi->path) fz_freepath(csi->path); + if (csi->text) fz_freetext(csi->text); + + pdf_clearstack(csi); + + fz_free(csi); +} + +static int +pdf_ishiddenocg(pdf_csi *csi, fz_obj *xobj) +{ + char target_state[16]; + fz_obj *obj; + + fz_strlcpy(target_state, csi->target, sizeof target_state); + fz_strlcat(target_state, "State", sizeof target_state); + + obj = fz_dictgets(xobj, "OC"); + obj = fz_dictgets(obj, "OCGs"); + if (fz_isarray(obj)) + obj = fz_arrayget(obj, 0); + obj = fz_dictgets(obj, "Usage"); + obj = fz_dictgets(obj, csi->target); + obj = fz_dictgets(obj, target_state); + return !strcmp(fz_toname(obj), "OFF"); +} + +fz_error +pdf_runxobject(pdf_csi *csi, fz_obj *resources, pdf_xobject *xobj, fz_matrix transform) +{ + fz_error error; + pdf_gstate *gstate; + fz_matrix oldtopctm; + int oldtop; + int popmask; + + pdf_gsave(csi); + + gstate = csi->gstate + csi->gtop; + oldtop = csi->gtop; + popmask = 0; + + /* apply xobject's transform matrix */ + transform = fz_concat(transform, xobj->matrix); + gstate->ctm = fz_concat(transform, gstate->ctm); + + /* apply soft mask, create transparency group and reset state */ + if (xobj->transparency) + { + if (gstate->softmask) + { + pdf_xobject *softmask = gstate->softmask; + fz_rect bbox = fz_transformrect(gstate->ctm, xobj->bbox); + + gstate->softmask = nil; + popmask = 1; + + csi->dev->beginmask(csi->dev->user, bbox, gstate->luminosity, + softmask->colorspace, gstate->softmaskbc); + error = pdf_runxobject(csi, resources, softmask, fz_identity); + if (error) + return fz_rethrow(error, "cannot run softmask"); + csi->dev->endmask(csi->dev->user); + + pdf_dropxobject(softmask); + } + + csi->dev->begingroup(csi->dev->user, + fz_transformrect(gstate->ctm, xobj->bbox), + xobj->isolated, xobj->knockout, gstate->blendmode, gstate->fill.alpha); + + gstate->blendmode = FZ_BNORMAL; + gstate->stroke.alpha = 1; + gstate->fill.alpha = 1; + } + + /* clip to the bounds */ + + fz_moveto(csi->path, xobj->bbox.x0, xobj->bbox.y0); + fz_lineto(csi->path, xobj->bbox.x1, xobj->bbox.y0); + fz_lineto(csi->path, xobj->bbox.x1, xobj->bbox.y1); + fz_lineto(csi->path, xobj->bbox.x0, xobj->bbox.y1); + fz_closepath(csi->path); + csi->clip = 1; + pdf_showpath(csi, 0, 0, 0, 0); + + /* run contents */ + + oldtopctm = csi->topctm; + csi->topctm = gstate->ctm; + + if (xobj->resources) + resources = xobj->resources; + + error = pdf_runcsibuffer(csi, resources, xobj->contents); + if (error) + return fz_rethrow(error, "cannot interpret XObject stream"); + + csi->topctm = oldtopctm; + + while (oldtop < csi->gtop) + pdf_grestore(csi); + + pdf_grestore(csi); + + /* wrap up transparency stacks */ + + if (xobj->transparency) + { + csi->dev->endgroup(csi->dev->user); + if (popmask) + csi->dev->popclip(csi->dev->user); + } + + return fz_okay; +} + +static fz_error +pdf_runinlineimage(pdf_csi *csi, fz_obj *rdb, fz_stream *file, fz_obj *dict) +{ + fz_error error; + fz_pixmap *img; + int ch; + + error = pdf_loadinlineimage(&img, csi->xref, rdb, dict, file); + if (error) + return fz_rethrow(error, "cannot load inline image"); + + /* find EI */ + ch = fz_readbyte(file); + while (ch != 'E' && ch != EOF) + ch = fz_readbyte(file); + ch = fz_readbyte(file); + if (ch != 'I') + { + fz_droppixmap(img); + return fz_rethrow(error, "syntax error after inline image"); + } + + pdf_showimage(csi, img); + + fz_droppixmap(img); + return fz_okay; +} + +static fz_error +pdf_runextgstate(pdf_csi *csi, fz_obj *rdb, fz_obj *extgstate) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + fz_colorspace *colorspace; + int i, k; + + pdf_flushtext(csi); + + for (i = 0; i < fz_dictlen(extgstate); i++) + { + fz_obj *key = fz_dictgetkey(extgstate, i); + fz_obj *val = fz_dictgetval(extgstate, i); + char *s = fz_toname(key); + + if (!strcmp(s, "Font")) + { + if (fz_isarray(val) && fz_arraylen(val) == 2) + { + fz_error error; + fz_obj *font = fz_arrayget(val, 0); + + if (gstate->font) + { + pdf_dropfont(gstate->font); + gstate->font = nil; + } + + error = pdf_loadfont(&gstate->font, csi->xref, rdb, font); + if (error) + return fz_rethrow(error, "cannot load font (%d %d R)", fz_tonum(font), fz_togen(font)); + if (!gstate->font) + return fz_throw("cannot find font in store"); + gstate->size = fz_toreal(fz_arrayget(val, 1)); + } + else + return fz_throw("malformed /Font dictionary"); + } + + else if (!strcmp(s, "LW")) + gstate->strokestate.linewidth = fz_toreal(val); + else if (!strcmp(s, "LC")) + gstate->strokestate.linecap = fz_toint(val); + else if (!strcmp(s, "LJ")) + gstate->strokestate.linejoin = fz_toint(val); + else if (!strcmp(s, "ML")) + gstate->strokestate.miterlimit = fz_toreal(val); + + else if (!strcmp(s, "D")) + { + if (fz_isarray(val) && fz_arraylen(val) == 2) + { + fz_obj *dashes = fz_arrayget(val, 0); + gstate->strokestate.dashlen = MAX(fz_arraylen(dashes), 32); + for (k = 0; k < gstate->strokestate.dashlen; k++) + gstate->strokestate.dashlist[k] = fz_toreal(fz_arrayget(dashes, k)); + gstate->strokestate.dashphase = fz_toreal(fz_arrayget(val, 1)); + } + else + return fz_throw("malformed /D"); + } + + else if (!strcmp(s, "CA")) + gstate->stroke.alpha = fz_toreal(val); + + else if (!strcmp(s, "ca")) + gstate->fill.alpha = fz_toreal(val); + + else if (!strcmp(s, "BM")) + { + if (fz_isarray(val)) + val = fz_arrayget(val, 0); + + gstate->blendmode = FZ_BNORMAL; + for (k = 0; fz_blendnames[k]; k++) + if (!strcmp(fz_blendnames[k], fz_toname(val))) + gstate->blendmode = k; + } + + else if (!strcmp(s, "SMask")) + { + if (fz_isdict(val)) + { + fz_error error; + pdf_xobject *xobj; + fz_obj *group, *luminosity, *bc; + + if (gstate->softmask) + { + pdf_dropxobject(gstate->softmask); + gstate->softmask = nil; + } + + group = fz_dictgets(val, "G"); + if (!group) + return fz_throw("cannot load softmask xobject (%d %d R)", fz_tonum(val), fz_togen(val)); + error = pdf_loadxobject(&xobj, csi->xref, group); + if (error) + return fz_rethrow(error, "cannot load xobject (%d %d R)", fz_tonum(val), fz_togen(val)); + + colorspace = xobj->colorspace; + if (!colorspace) + colorspace = fz_devicegray; + + gstate->softmaskctm = fz_concat(xobj->matrix, gstate->ctm); + gstate->softmask = xobj; + for (k = 0; k < colorspace->n; k++) + gstate->softmaskbc[k] = 0; + + bc = fz_dictgets(val, "BC"); + if (fz_isarray(bc)) + { + for (k = 0; k < colorspace->n; k++) + gstate->softmaskbc[k] = fz_toreal(fz_arrayget(bc, k)); + } + + luminosity = fz_dictgets(val, "S"); + if (fz_isname(luminosity) && !strcmp(fz_toname(luminosity), "Luminosity")) + gstate->luminosity = 1; + else + gstate->luminosity = 0; + } + else if (fz_isname(val) && !strcmp(fz_toname(val), "None")) + { + if (gstate->softmask) + { + pdf_dropxobject(gstate->softmask); + gstate->softmask = nil; + } + } + } + + else if (!strcmp(s, "TR")) + { + if (fz_isname(val) && strcmp(fz_toname(val), "Identity")) + fz_warn("ignoring transfer function"); + } + } + + return fz_okay; +} + +static void pdf_run_BDC(pdf_csi *csi) +{ +} + +static fz_error pdf_run_BI(pdf_csi *csi, fz_obj *rdb, fz_stream *file) +{ + int ch; + fz_error error; + char *buf = csi->xref->scratch; + int buflen = sizeof(csi->xref->scratch); + fz_obj *obj; + + error = pdf_parsedict(&obj, csi->xref, file, buf, buflen); + if (error) + return fz_rethrow(error, "cannot parse inline image dictionary"); + + /* read whitespace after ID keyword */ + ch = fz_readbyte(file); + if (ch == '\r') + if (fz_peekbyte(file) == '\n') + fz_readbyte(file); + + error = pdf_runinlineimage(csi, rdb, file, obj); + fz_dropobj(obj); + if (error) + return fz_rethrow(error, "cannot parse inline image"); + + return fz_okay; +} + +static void pdf_run_B(pdf_csi *csi) +{ + pdf_showpath(csi, 0, 1, 1, 0); +} + +static void pdf_run_BMC(pdf_csi *csi) +{ +} + +static void pdf_run_BT(pdf_csi *csi) +{ + csi->intext = 1; + csi->tm = fz_identity; + csi->tlm = fz_identity; +} + +static void pdf_run_BX(pdf_csi *csi) +{ + csi->xbalance ++; +} + +static void pdf_run_Bstar(pdf_csi *csi) +{ + pdf_showpath(csi, 0, 1, 1, 1); +} + +static fz_error pdf_run_cs_imp(pdf_csi *csi, fz_obj *rdb, int what) +{ + fz_colorspace *colorspace; + fz_obj *obj, *dict; + fz_error error; + + if (!strcmp(csi->name, "Pattern")) + { + pdf_setpattern(csi, what, nil, nil); + } + else + { + if (!strcmp(csi->name, "DeviceGray")) + colorspace = fz_keepcolorspace(fz_devicegray); + else if (!strcmp(csi->name, "DeviceRGB")) + colorspace = fz_keepcolorspace(fz_devicergb); + else if (!strcmp(csi->name, "DeviceCMYK")) + colorspace = fz_keepcolorspace(fz_devicecmyk); + else + { + dict = fz_dictgets(rdb, "ColorSpace"); + if (!dict) + return fz_throw("cannot find ColorSpace dictionary"); + obj = fz_dictgets(dict, csi->name); + if (!obj) + return fz_throw("cannot find colorspace resource '%s'", csi->name); + error = pdf_loadcolorspace(&colorspace, csi->xref, obj); + if (error) + return fz_rethrow(error, "cannot load colorspace (%d 0 R)", fz_tonum(obj)); + } + + pdf_setcolorspace(csi, what, colorspace); + + fz_dropcolorspace(colorspace); + } + return fz_okay; +} + +static void pdf_run_CS(pdf_csi *csi, fz_obj *rdb) +{ + fz_error error; + error = pdf_run_cs_imp(csi, rdb, PDF_MSTROKE); + if (error) + fz_catch(error, "cannot set colorspace"); +} + +static void pdf_run_cs(pdf_csi *csi, fz_obj *rdb) +{ + fz_error error; + error = pdf_run_cs_imp(csi, rdb, PDF_MFILL); + if (error) + fz_catch(error, "cannot set colorspace"); +} + +static void pdf_run_DP(pdf_csi *csi) +{ +} + +static fz_error pdf_run_Do(pdf_csi *csi, fz_obj *rdb) +{ + fz_obj *dict; + fz_obj *obj; + fz_obj *subtype; + fz_error error; + + dict = fz_dictgets(rdb, "XObject"); + if (!dict) + return fz_throw("cannot find XObject dictionary when looking for: '%s'", csi->name); + + obj = fz_dictgets(dict, csi->name); + if (!obj) + return fz_throw("cannot find xobject resource: '%s'", csi->name); + + subtype = fz_dictgets(obj, "Subtype"); + if (!fz_isname(subtype)) + return fz_throw("no XObject subtype specified"); + + if (pdf_ishiddenocg(csi, obj)) + return fz_okay; + + if (!strcmp(fz_toname(subtype), "Form") && fz_dictgets(obj, "Subtype2")) + subtype = fz_dictgets(obj, "Subtype2"); + + if (!strcmp(fz_toname(subtype), "Form")) + { + pdf_xobject *xobj; + + error = pdf_loadxobject(&xobj, csi->xref, obj); + if (error) + return fz_rethrow(error, "cannot load xobject (%d %d R)", fz_tonum(obj), fz_togen(obj)); + + /* Inherit parent resources, in case this one was empty XXX check where it's loaded */ + if (!xobj->resources) + xobj->resources = fz_keepobj(rdb); + + error = pdf_runxobject(csi, xobj->resources, xobj, fz_identity); + if (error) + return fz_rethrow(error, "cannot draw xobject (%d %d R)", fz_tonum(obj), fz_togen(obj)); + + pdf_dropxobject(xobj); + } + + else if (!strcmp(fz_toname(subtype), "Image")) + { + if ((csi->dev->hints & FZ_IGNOREIMAGE) == 0) + { + fz_pixmap *img; + error = pdf_loadimage(&img, csi->xref, obj); + if (error) + return fz_rethrow(error, "cannot load image (%d %d R)", fz_tonum(obj), fz_togen(obj)); + pdf_showimage(csi, img); + fz_droppixmap(img); + } + } + + else if (!strcmp(fz_toname(subtype), "PS")) + { + fz_warn("ignoring XObject with subtype PS"); + } + + else + { + return fz_throw("unknown XObject subtype: '%s'", fz_toname(subtype)); + } + + return fz_okay; +} + +static void pdf_run_EMC(pdf_csi *csi) +{ +} + +static void pdf_run_ET(pdf_csi *csi) +{ + pdf_flushtext(csi); + csi->accumulate = 1; + csi->intext = 0; +} + +static void pdf_run_EX(pdf_csi *csi) +{ + csi->xbalance --; +} + +static void pdf_run_F(pdf_csi *csi) +{ + pdf_showpath(csi, 0, 1, 0, 0); +} + +static void pdf_run_G(pdf_csi *csi) +{ + pdf_setcolorspace(csi, PDF_MSTROKE, fz_devicegray); + pdf_setcolor(csi, PDF_MSTROKE, csi->stack); +} + +static void pdf_run_J(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + gstate->strokestate.linecap = csi->stack[0]; +} + +static void pdf_run_K(pdf_csi *csi) +{ + pdf_setcolorspace(csi, PDF_MSTROKE, fz_devicecmyk); + pdf_setcolor(csi, PDF_MSTROKE, csi->stack); +} + +static void pdf_run_M(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + gstate->strokestate.miterlimit = csi->stack[0]; +} + +static void pdf_run_MP(pdf_csi *csi) +{ +} + +static void pdf_run_Q(pdf_csi *csi) +{ + pdf_grestore(csi); +} + +static void pdf_run_RG(pdf_csi *csi) +{ + pdf_setcolorspace(csi, PDF_MSTROKE, fz_devicergb); + pdf_setcolor(csi, PDF_MSTROKE, csi->stack); +} + +static void pdf_run_S(pdf_csi *csi) +{ + pdf_showpath(csi, 0, 0, 1, 0); +} + +static fz_error pdf_run_SC_imp(pdf_csi *csi, fz_obj *rdb, int what, pdf_material *mat) +{ + fz_error error; + fz_obj *patterntype; + fz_obj *dict; + fz_obj *obj; + int kind; + + kind = mat->kind; + if (csi->name[0]) + kind = PDF_MPATTERN; + + switch (kind) + { + case PDF_MNONE: + return fz_throw("cannot set color in mask objects"); + + case PDF_MCOLOR: + pdf_setcolor(csi, what, csi->stack); + break; + + case PDF_MPATTERN: + dict = fz_dictgets(rdb, "Pattern"); + if (!dict) + return fz_throw("cannot find Pattern dictionary"); + + obj = fz_dictgets(dict, csi->name); + if (!obj) + return fz_throw("cannot find pattern resource '%s'", csi->name); + + patterntype = fz_dictgets(obj, "PatternType"); + + if (fz_toint(patterntype) == 1) + { + pdf_pattern *pat; + error = pdf_loadpattern(&pat, csi->xref, obj); + if (error) + return fz_rethrow(error, "cannot load pattern (%d 0 R)", fz_tonum(obj)); + pdf_setpattern(csi, what, pat, csi->top > 0 ? csi->stack : nil); + pdf_droppattern(pat); + } + else if (fz_toint(patterntype) == 2) + { + fz_shade *shd; + error = pdf_loadshading(&shd, csi->xref, obj); + if (error) + return fz_rethrow(error, "cannot load shading (%d 0 R)", fz_tonum(obj)); + pdf_setshade(csi, what, shd); + fz_dropshade(shd); + } + else + { + return fz_throw("unknown pattern type: %d", fz_toint(patterntype)); + } + break; + + case PDF_MSHADE: + return fz_throw("cannot set color in shade objects"); + } + + return fz_okay; +} + +static void pdf_run_SC(pdf_csi *csi, fz_obj *rdb) +{ + fz_error error; + pdf_gstate *gstate = csi->gstate + csi->gtop; + error = pdf_run_SC_imp(csi, rdb, PDF_MSTROKE, &gstate->stroke); + if (error) + fz_catch(error, "cannot set color and colorspace"); +} + +static void pdf_run_sc(pdf_csi *csi, fz_obj *rdb) +{ + fz_error error; + pdf_gstate *gstate = csi->gstate + csi->gtop; + error = pdf_run_SC_imp(csi, rdb, PDF_MFILL, &gstate->fill); + if (error) + fz_catch(error, "cannot set color and colorspace"); +} + +static void pdf_run_Tc(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + gstate->charspace = csi->stack[0]; +} + +static void pdf_run_Tw(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + gstate->wordspace = csi->stack[0]; +} + +static void pdf_run_Tz(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + float a = csi->stack[0] / 100; + pdf_flushtext(csi); + gstate->scale = a; +} + +static void pdf_run_TL(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + gstate->leading = csi->stack[0]; +} + +static fz_error pdf_run_Tf(pdf_csi *csi, fz_obj *rdb) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + fz_error error; + fz_obj *dict; + fz_obj *obj; + + gstate->size = csi->stack[0]; + if (gstate->font) + pdf_dropfont(gstate->font); + gstate->font = nil; + + dict = fz_dictgets(rdb, "Font"); + if (!dict) + return fz_throw("cannot find Font dictionary"); + + obj = fz_dictgets(dict, csi->name); + if (!obj) + return fz_throw("cannot find font resource: '%s'", csi->name); + + error = pdf_loadfont(&gstate->font, csi->xref, rdb, obj); + if (error) + return fz_rethrow(error, "cannot load font (%d 0 R)", fz_tonum(obj)); + + return fz_okay; +} + +static void pdf_run_Tr(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + gstate->render = csi->stack[0]; +} + +static void pdf_run_Ts(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + gstate->rise = csi->stack[0]; +} + +static void pdf_run_Td(pdf_csi *csi) +{ + fz_matrix m = fz_translate(csi->stack[0], csi->stack[1]); + csi->tlm = fz_concat(m, csi->tlm); + csi->tm = csi->tlm; +} + +static void pdf_run_TD(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + fz_matrix m; + + gstate->leading = -csi->stack[1]; + m = fz_translate(csi->stack[0], csi->stack[1]); + csi->tlm = fz_concat(m, csi->tlm); + csi->tm = csi->tlm; +} + +static void pdf_run_Tm(pdf_csi *csi) +{ + pdf_flushtext(csi); + csi->tm.a = csi->stack[0]; + csi->tm.b = csi->stack[1]; + csi->tm.c = csi->stack[2]; + csi->tm.d = csi->stack[3]; + csi->tm.e = csi->stack[4]; + csi->tm.f = csi->stack[5]; + csi->tlm = csi->tm; +} + +static void pdf_run_Tstar(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + fz_matrix m = fz_translate(0, -gstate->leading); + csi->tlm = fz_concat(m, csi->tlm); + csi->tm = csi->tlm; +} + +static void pdf_run_Tj(pdf_csi *csi) +{ + if (csi->stringlen) + pdf_showstring(csi, csi->string, csi->stringlen); + else + pdf_showtext(csi, csi->obj); +} + +static void pdf_run_TJ(pdf_csi *csi) +{ + if (csi->stringlen) + pdf_showstring(csi, csi->string, csi->stringlen); + else + pdf_showtext(csi, csi->obj); +} + +static void pdf_run_W(pdf_csi *csi) +{ + csi->clip = 1; + csi->clipevenodd = 0; +} + +static void pdf_run_Wstar(pdf_csi *csi) +{ + csi->clip = 1; + csi->clipevenodd = 1; +} + +static void pdf_run_b(pdf_csi *csi) +{ + pdf_showpath(csi, 1, 1, 1, 0); +} + +static void pdf_run_bstar(pdf_csi *csi) +{ + pdf_showpath(csi, 1, 1, 1, 1); +} + +static void pdf_run_c(pdf_csi *csi) +{ + float a, b, c, d, e, f; + a = csi->stack[0]; + b = csi->stack[1]; + c = csi->stack[2]; + d = csi->stack[3]; + e = csi->stack[4]; + f = csi->stack[5]; + fz_curveto(csi->path, a, b, c, d, e, f); +} + +static void pdf_run_cm(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + fz_matrix m; + + m.a = csi->stack[0]; + m.b = csi->stack[1]; + m.c = csi->stack[2]; + m.d = csi->stack[3]; + m.e = csi->stack[4]; + m.f = csi->stack[5]; + + gstate->ctm = fz_concat(m, gstate->ctm); +} + +static void pdf_run_d(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + fz_obj *array; + int i; + + array = csi->obj; + gstate->strokestate.dashlen = MIN(fz_arraylen(array), nelem(gstate->strokestate.dashlist)); + for (i = 0; i < gstate->strokestate.dashlen; i++) + gstate->strokestate.dashlist[i] = fz_toreal(fz_arrayget(array, i)); + gstate->strokestate.dashphase = csi->stack[0]; +} + +static void pdf_run_d0(pdf_csi *csi) +{ +} + +static void pdf_run_d1(pdf_csi *csi) +{ +} + +static void pdf_run_f(pdf_csi *csi) +{ + pdf_showpath(csi, 0, 1, 0, 0); +} + +static void pdf_run_fstar(pdf_csi *csi) +{ + pdf_showpath(csi, 0, 1, 0, 1); +} + +static void pdf_run_g(pdf_csi *csi) +{ + pdf_setcolorspace(csi, PDF_MFILL, fz_devicegray); + pdf_setcolor(csi, PDF_MFILL, csi->stack); +} + +static fz_error pdf_run_gs(pdf_csi *csi, fz_obj *rdb) +{ + fz_error error; + fz_obj *dict; + fz_obj *obj; + + dict = fz_dictgets(rdb, "ExtGState"); + if (!dict) + return fz_throw("cannot find ExtGState dictionary"); + + obj = fz_dictgets(dict, csi->name); + if (!obj) + return fz_throw("cannot find extgstate resource '%s'", csi->name); + + error = pdf_runextgstate(csi, rdb, obj); + if (error) + return fz_rethrow(error, "cannot set ExtGState (%d 0 R)", fz_tonum(obj)); + return fz_okay; +} + +static void pdf_run_h(pdf_csi *csi) +{ + fz_closepath(csi->path); +} + +static void pdf_run_i(pdf_csi *csi) +{ +} + +static void pdf_run_j(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + gstate->strokestate.linejoin = csi->stack[0]; +} + +static void pdf_run_k(pdf_csi *csi) +{ + pdf_setcolorspace(csi, PDF_MFILL, fz_devicecmyk); + pdf_setcolor(csi, PDF_MFILL, csi->stack); +} + +static void pdf_run_l(pdf_csi *csi) +{ + float a, b; + a = csi->stack[0]; + b = csi->stack[1]; + fz_lineto(csi->path, a, b); +} + +static void pdf_run_m(pdf_csi *csi) +{ + float a, b; + a = csi->stack[0]; + b = csi->stack[1]; + fz_moveto(csi->path, a, b); +} + +static void pdf_run_n(pdf_csi *csi) +{ + pdf_showpath(csi, 0, 0, 0, csi->clipevenodd); +} + +static void pdf_run_q(pdf_csi *csi) +{ + pdf_gsave(csi); +} + +static void pdf_run_re(pdf_csi *csi) +{ + float x, y, w, h; + + x = csi->stack[0]; + y = csi->stack[1]; + w = csi->stack[2]; + h = csi->stack[3]; + + fz_moveto(csi->path, x, y); + fz_lineto(csi->path, x + w, y); + fz_lineto(csi->path, x + w, y + h); + fz_lineto(csi->path, x, y + h); + fz_closepath(csi->path); +} + +static void pdf_run_rg(pdf_csi *csi) +{ + pdf_setcolorspace(csi, PDF_MFILL, fz_devicergb); + pdf_setcolor(csi, PDF_MFILL, csi->stack); +} + +static void pdf_run_ri(pdf_csi *csi) +{ +} + +static void pdf_run_s(pdf_csi *csi) +{ + pdf_showpath(csi, 1, 0, 1, 0); +} + +static fz_error pdf_run_sh(pdf_csi *csi, fz_obj *rdb) +{ + fz_obj *dict; + fz_obj *obj; + fz_shade *shd; + fz_error error; + + dict = fz_dictgets(rdb, "Shading"); + if (!dict) + return fz_throw("cannot find shading dictionary"); + + obj = fz_dictgets(dict, csi->name); + if (!obj) + return fz_throw("cannot find shading resource: '%s'", csi->name); + + if ((csi->dev->hints & FZ_IGNORESHADE) == 0) + { + error = pdf_loadshading(&shd, csi->xref, obj); + if (error) + return fz_rethrow(error, "cannot load shading (%d %d R)", fz_tonum(obj), fz_togen(obj)); + pdf_showshade(csi, shd); + fz_dropshade(shd); + } + return fz_okay; +} + +static void pdf_run_v(pdf_csi *csi) +{ + float a, b, c, d; + a = csi->stack[0]; + b = csi->stack[1]; + c = csi->stack[2]; + d = csi->stack[3]; + fz_curvetov(csi->path, a, b, c, d); +} + +static void pdf_run_w(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + gstate->strokestate.linewidth = csi->stack[0]; +} + +static void pdf_run_y(pdf_csi *csi) +{ + float a, b, c, d; + a = csi->stack[0]; + b = csi->stack[1]; + c = csi->stack[2]; + d = csi->stack[3]; + fz_curvetoy(csi->path, a, b, c, d); +} + +static void pdf_run_squote(pdf_csi *csi) +{ + fz_matrix m; + pdf_gstate *gstate = csi->gstate + csi->gtop; + + m = fz_translate(0, -gstate->leading); + csi->tlm = fz_concat(m, csi->tlm); + csi->tm = csi->tlm; + + if (csi->stringlen) + pdf_showstring(csi, csi->string, csi->stringlen); + else + pdf_showtext(csi, csi->obj); +} + +static void pdf_run_dquote(pdf_csi *csi) +{ + fz_matrix m; + pdf_gstate *gstate = csi->gstate + csi->gtop; + + gstate->wordspace = csi->stack[0]; + gstate->charspace = csi->stack[1]; + + m = fz_translate(0, -gstate->leading); + csi->tlm = fz_concat(m, csi->tlm); + csi->tm = csi->tlm; + + if (csi->stringlen) + pdf_showstring(csi, csi->string, csi->stringlen); + else + pdf_showtext(csi, csi->obj); +} + +#define A(a) (a) +#define B(a,b) (a | b << 8) +#define C(a,b,c) (a | b << 8 | c << 16) + +static fz_error +pdf_runkeyword(pdf_csi *csi, fz_obj *rdb, fz_stream *file, char *buf) +{ + fz_error error; + int key; + + key = buf[0]; + if (buf[1]) + { + key |= buf[1] << 8; + if (buf[2]) + { + key |= buf[2] << 16; + if (buf[3]) + key = 0; + } + } + + switch (key) + { + case A('"'): pdf_run_dquote(csi); break; + case A('\''): pdf_run_squote(csi); break; + case A('B'): pdf_run_B(csi); break; + case B('B','*'): pdf_run_Bstar(csi); break; + case C('B','D','C'): pdf_run_BDC(csi); break; + case B('B','I'): + error = pdf_run_BI(csi, rdb, file); + if (error) + return fz_rethrow(error, "cannot draw inline image"); + break; + case C('B','M','C'): pdf_run_BMC(csi); break; + case B('B','T'): pdf_run_BT(csi); break; + case B('B','X'): pdf_run_BX(csi); break; + case B('C','S'): pdf_run_CS(csi, rdb); break; + case B('D','P'): pdf_run_DP(csi); break; + case B('D','o'): + error = pdf_run_Do(csi, rdb); + if (error) + fz_catch(error, "cannot draw xobject/image"); + break; + case C('E','M','C'): pdf_run_EMC(csi); break; + case B('E','T'): pdf_run_ET(csi); break; + case B('E','X'): pdf_run_EX(csi); break; + case A('F'): pdf_run_F(csi); break; + case A('G'): pdf_run_G(csi); break; + case A('J'): pdf_run_J(csi); break; + case A('K'): pdf_run_K(csi); break; + case A('M'): pdf_run_M(csi); break; + case B('M','P'): pdf_run_MP(csi); break; + case A('Q'): pdf_run_Q(csi); break; + case B('R','G'): pdf_run_RG(csi); break; + case A('S'): pdf_run_S(csi); break; + case B('S','C'): pdf_run_SC(csi, rdb); break; + case C('S','C','N'): pdf_run_SC(csi, rdb); break; + case B('T','*'): pdf_run_Tstar(csi); break; + case B('T','D'): pdf_run_TD(csi); break; + case B('T','J'): pdf_run_TJ(csi); break; + case B('T','L'): pdf_run_TL(csi); break; + case B('T','c'): pdf_run_Tc(csi); break; + case B('T','d'): pdf_run_Td(csi); break; + case B('T','f'): + error = pdf_run_Tf(csi, rdb); + if (error) + fz_catch(error, "cannot set font"); + break; + case B('T','j'): pdf_run_Tj(csi); break; + case B('T','m'): pdf_run_Tm(csi); break; + case B('T','r'): pdf_run_Tr(csi); break; + case B('T','s'): pdf_run_Ts(csi); break; + case B('T','w'): pdf_run_Tw(csi); break; + case B('T','z'): pdf_run_Tz(csi); break; + case A('W'): pdf_run_W(csi); break; + case B('W','*'): pdf_run_Wstar(csi); break; + case A('b'): pdf_run_b(csi); break; + case B('b','*'): pdf_run_bstar(csi); break; + case A('c'): pdf_run_c(csi); break; + case B('c','m'): pdf_run_cm(csi); break; + case B('c','s'): pdf_run_cs(csi, rdb); break; + case A('d'): pdf_run_d(csi); break; + case B('d','0'): pdf_run_d0(csi); break; + case B('d','1'): pdf_run_d1(csi); break; + case A('f'): pdf_run_f(csi); break; + case B('f','*'): pdf_run_fstar(csi); break; + case A('g'): pdf_run_g(csi); break; + case B('g','s'): + error = pdf_run_gs(csi, rdb); + if (error) + fz_catch(error, "cannot set graphics state"); + break; + case A('h'): pdf_run_h(csi); break; + case A('i'): pdf_run_i(csi); break; + case A('j'): pdf_run_j(csi); break; + case A('k'): pdf_run_k(csi); break; + case A('l'): pdf_run_l(csi); break; + case A('m'): pdf_run_m(csi); break; + case A('n'): pdf_run_n(csi); break; + case A('q'): pdf_run_q(csi); break; + case B('r','e'): pdf_run_re(csi); break; + case B('r','g'): pdf_run_rg(csi); break; + case B('r','i'): pdf_run_ri(csi); break; + case A('s'): pdf_run_s(csi); break; + case B('s','c'): pdf_run_sc(csi, rdb); break; + case C('s','c','n'): pdf_run_sc(csi, rdb); break; + case B('s','h'): + error = pdf_run_sh(csi, rdb); + if (error) + fz_catch(error, "cannot draw shading"); + break; + case A('v'): pdf_run_v(csi); break; + case A('w'): pdf_run_w(csi); break; + case A('y'): pdf_run_y(csi); break; + default: + if (!csi->xbalance) + fz_warn("unknown keyword: '%s'", buf); + break; + } + + return fz_okay; +} + +static fz_error +pdf_runcsifile(pdf_csi *csi, fz_obj *rdb, fz_stream *file, char *buf, int buflen) +{ + fz_error error; + int tok; + int len; + + pdf_clearstack(csi); + + while (1) + { + if (csi->top == nelem(csi->stack) - 1) + return fz_throw("stack overflow"); + + error = pdf_lex(&tok, file, buf, buflen, &len); + if (error) + return fz_rethrow(error, "lexical error in content stream"); + + if (csi->inarray) + { + if (tok == PDF_TCARRAY) + { + csi->inarray = 0; + } + else if (tok == PDF_TINT || tok == PDF_TREAL) + { + pdf_gstate *gstate = csi->gstate + csi->gtop; + pdf_showspace(csi, -atof(buf) * gstate->size * 0.001f); + } + else if (tok == PDF_TSTRING) + { + pdf_showstring(csi, (unsigned char *)buf, len); + } + else if (tok == PDF_TKEYWORD) + { + if (!strcmp(buf, "Tw") || !strcmp(buf, "Tc")) + fz_warn("ignoring keyword '%s' inside array", buf); + else + return fz_throw("syntax error in array"); + } + else if (tok == PDF_TEOF) + return fz_okay; + else + return fz_throw("syntax error in array"); + } + + else switch (tok) + { + case PDF_TENDSTREAM: + case PDF_TEOF: + return fz_okay; + + case PDF_TOARRAY: + if (!csi->intext) + { + error = pdf_parsearray(&csi->obj, csi->xref, file, buf, buflen); + if (error) + return fz_rethrow(error, "cannot parse array"); + } + else + { + csi->inarray = 1; + } + break; + + case PDF_TODICT: + error = pdf_parsedict(&csi->obj, csi->xref, file, buf, buflen); + if (error) + return fz_rethrow(error, "cannot parse dictionary"); + break; + + case PDF_TNAME: + fz_strlcpy(csi->name, buf, sizeof(csi->name)); + break; + + case PDF_TINT: + csi->stack[csi->top] = atoi(buf); + csi->top ++; + break; + + case PDF_TREAL: + csi->stack[csi->top] = atof(buf); + csi->top ++; + break; + + case PDF_TSTRING: + if (len <= sizeof(csi->string)) + { + memcpy(csi->string, buf, len); + csi->stringlen = len; + } + else + { + csi->obj = fz_newstring(buf, len); + } + break; + + case PDF_TKEYWORD: + error = pdf_runkeyword(csi, rdb, file, buf); + if (error) + return fz_rethrow(error, "cannot run keyword"); + pdf_clearstack(csi); + break; + + default: + return fz_throw("syntax error in content stream"); + } + } +} + +fz_error +pdf_runcsibuffer(pdf_csi *csi, fz_obj *rdb, fz_buffer *contents) +{ + fz_stream *file; + fz_error error; + file = fz_openbuffer(contents); + error = pdf_runcsifile(csi, rdb, file, csi->xref->scratch, sizeof csi->xref->scratch); + fz_close(file); + if (error) + return fz_rethrow(error, "cannot parse content stream"); + return fz_okay; +} + +fz_error +pdf_runpagewithtarget(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm, char *target) +{ + pdf_csi *csi; + fz_error error; + pdf_annot *annot; + int flags; + + if (page->transparency) + dev->begingroup(dev->user, + fz_transformrect(ctm, page->mediabox), + 0, 0, FZ_BNORMAL, 1); + + csi = pdf_newcsi(xref, dev, ctm, target); + error = pdf_runcsibuffer(csi, page->resources, page->contents); + pdf_freecsi(csi); + if (error) + return fz_rethrow(error, "cannot parse page content stream"); + + for (annot = page->annots; annot; annot = annot->next) + { + flags = fz_toint(fz_dictgets(annot->obj, "F")); + + /* TODO: NoZoom and NoRotate */ + if (flags & (1 << 0)) /* Invisible */ + continue; + if (flags & (1 << 1)) /* Hidden */ + continue; + if (flags & (1 << 5)) /* NoView */ + continue; + + if (pdf_ishiddenocg(csi, annot->obj)) + continue; + + csi = pdf_newcsi(xref, dev, ctm, target); + error = pdf_runxobject(csi, page->resources, annot->ap, annot->matrix); + pdf_freecsi(csi); + if (error) + return fz_rethrow(error, "cannot parse annotation appearance stream"); + } + + if (page->transparency) + dev->endgroup(dev->user); + + return fz_okay; +} + +fz_error +pdf_runpage(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm) +{ + return pdf_runpagewithtarget(xref, page, dev, ctm, "View"); +} + +fz_error +pdf_runglyph(pdf_xref *xref, fz_obj *resources, fz_buffer *contents, fz_device *dev, fz_matrix ctm) +{ + pdf_csi *csi = pdf_newcsi(xref, dev, ctm, "View"); + fz_error error = pdf_runcsibuffer(csi, resources, contents); + pdf_freecsi(csi); + if (error) + return fz_rethrow(error, "cannot parse glyph content stream"); + return fz_okay; +} |