kolibrios-gitea/contrib/media/updf/pdf/pdf_interpret.c
right-hearted 4f7ee97ec9 uPDF with buttons
git-svn-id: svn://kolibrios.org@4680 a494cfbc-eb01-0410-851d-a64ba20cac60
2014-03-22 21:00:40 +00:00

2305 lines
50 KiB
C

#include "fitz.h"
#include "mupdf.h"
#define TILE
typedef struct pdf_material_s pdf_material;
typedef struct pdf_gstate_s pdf_gstate;
typedef struct pdf_csi_s pdf_csi;
enum
{
PDF_FILL,
PDF_STROKE,
};
enum
{
PDF_MAT_NONE,
PDF_MAT_COLOR,
PDF_MAT_PATTERN,
PDF_MAT_SHADE,
};
struct pdf_material_s
{
int kind;
fz_colorspace *colorspace;
pdf_pattern *pattern;
fz_shade *shade;
float alpha;
float v[32];
};
struct pdf_gstate_s
{
fz_matrix ctm;
int clip_depth;
/* path stroking */
fz_stroke_state stroke_state;
/* materials */
pdf_material stroke;
pdf_material fill;
/* text state */
float char_space;
float word_space;
float scale;
float leading;
pdf_font_desc *font;
float size;
int render;
float rise;
/* transparency */
int blendmode;
pdf_xobject *softmask;
fz_matrix softmask_ctm;
float softmask_bc[FZ_MAX_COLORS];
int luminosity;
};
struct pdf_csi_s
{
fz_device *dev;
pdf_xref *xref;
/* usage mode for optional content groups */
char *target; /* "View", "Print", "Export" */
/* interpreter stack */
fz_obj *obj;
char name[256];
unsigned char string[256];
int string_len;
float stack[32];
int top;
int xbalance;
int in_text;
/* path object state */
fz_path *path;
/* text object state */
fz_text *text;
fz_matrix tlm;
fz_matrix tm;
int text_mode;
int accumulate;
/* graphics state */
fz_matrix top_ctm;
pdf_gstate gstate[64];
int gtop;
};
static fz_error pdf_run_buffer(pdf_csi *csi, fz_obj *rdb, fz_buffer *contents);
static fz_error pdf_run_xobject(pdf_csi *csi, fz_obj *resources, pdf_xobject *xobj, fz_matrix transform);
static void pdf_show_pattern(pdf_csi *csi, pdf_pattern *pat, fz_rect area, int what);
static int
pdf_is_hidden_ocg(fz_obj *xobj, char *target)
{
char target_state[16];
fz_obj *obj;
fz_strlcpy(target_state, target, sizeof target_state);
fz_strlcat(target_state, "State", sizeof target_state);
obj = fz_dict_gets(xobj, "OC");
obj = fz_dict_gets(obj, "OCGs");
if (fz_is_array(obj))
obj = fz_array_get(obj, 0);
obj = fz_dict_gets(obj, "Usage");
obj = fz_dict_gets(obj, target);
obj = fz_dict_gets(obj, target_state);
return !strcmp(fz_to_name(obj), "OFF");
}
/*
* Emit graphics calls to device.
*/
static void
pdf_begin_group(pdf_csi *csi, fz_rect bbox)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_error error;
if (gstate->softmask)
{
pdf_xobject *softmask = gstate->softmask;
fz_rect bbox = fz_transform_rect(gstate->softmask_ctm, softmask->bbox);
fz_matrix save_ctm = gstate->ctm;
gstate->softmask = NULL;
gstate->ctm = gstate->softmask_ctm;
fz_begin_mask(csi->dev, bbox, gstate->luminosity,
softmask->colorspace, gstate->softmask_bc);
error = pdf_run_xobject(csi, NULL, softmask, fz_identity);
if (error)
fz_catch(error, "cannot run softmask");
fz_end_mask(csi->dev);
gstate->softmask = softmask;
gstate->ctm = save_ctm;
}
if (gstate->blendmode)
fz_begin_group(csi->dev, bbox, 1, 0, gstate->blendmode, 1);
}
static void
pdf_end_group(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
if (gstate->blendmode)
fz_end_group(csi->dev);
if (gstate->softmask)
fz_pop_clip(csi->dev);
}
static void
pdf_show_shade(pdf_csi *csi, fz_shade *shd)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_rect bbox;
bbox = fz_bound_shade(shd, gstate->ctm);
pdf_begin_group(csi, bbox);
fz_fill_shade(csi->dev, shd, gstate->ctm, gstate->fill.alpha);
pdf_end_group(csi);
}
static void
pdf_show_image(pdf_csi *csi, fz_pixmap *image)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_rect bbox;
bbox = fz_transform_rect(gstate->ctm, fz_unit_rect);
if (image->mask)
{
/* apply blend group even though we skip the softmask */
if (gstate->blendmode)
fz_begin_group(csi->dev, bbox, 0, 0, gstate->blendmode, 1);
fz_clip_image_mask(csi->dev, image->mask, &bbox, gstate->ctm);
}
else
pdf_begin_group(csi, bbox);
if (!image->colorspace)
{
switch (gstate->fill.kind)
{
case PDF_MAT_NONE:
break;
case PDF_MAT_COLOR:
fz_fill_image_mask(csi->dev, image, gstate->ctm,
gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
break;
case PDF_MAT_PATTERN:
if (gstate->fill.pattern)
{
fz_clip_image_mask(csi->dev, image, &bbox, gstate->ctm);
pdf_show_pattern(csi, gstate->fill.pattern, bbox, PDF_FILL);
fz_pop_clip(csi->dev);
}
break;
case PDF_MAT_SHADE:
if (gstate->fill.shade)
{
fz_clip_image_mask(csi->dev, image, &bbox, gstate->ctm);
fz_fill_shade(csi->dev, gstate->fill.shade, gstate->ctm, gstate->fill.alpha);
fz_pop_clip(csi->dev);
}
break;
}
}
else
{
fz_fill_image(csi->dev, image, gstate->ctm, gstate->fill.alpha);
}
if (image->mask)
{
fz_pop_clip(csi->dev);
if (gstate->blendmode)
fz_end_group(csi->dev);
}
else
pdf_end_group(csi);
}
static void pdf_show_clip(pdf_csi *csi, int even_odd)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->clip_depth++;
fz_clip_path(csi->dev, csi->path, NULL, even_odd, gstate->ctm);
}
static void
pdf_show_path(pdf_csi *csi, int doclose, int dofill, int dostroke, int even_odd)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_path *path;
fz_rect bbox;
path = csi->path;
csi->path = fz_new_path();
if (doclose)
fz_closepath(path);
if (dostroke)
bbox = fz_bound_path(path, &gstate->stroke_state, gstate->ctm);
else
bbox = fz_bound_path(path, NULL, gstate->ctm);
if (dofill || dostroke)
pdf_begin_group(csi, bbox);
if (dofill)
{
switch (gstate->fill.kind)
{
case PDF_MAT_NONE:
break;
case PDF_MAT_COLOR:
fz_fill_path(csi->dev, path, even_odd, gstate->ctm,
gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
break;
case PDF_MAT_PATTERN:
if (gstate->fill.pattern)
{
fz_clip_path(csi->dev, path, NULL, even_odd, gstate->ctm);
pdf_show_pattern(csi, gstate->fill.pattern, bbox, PDF_FILL);
fz_pop_clip(csi->dev);
}
break;
case PDF_MAT_SHADE:
if (gstate->fill.shade)
{
fz_clip_path(csi->dev, path, NULL, even_odd, gstate->ctm);
fz_fill_shade(csi->dev, gstate->fill.shade, csi->top_ctm, gstate->fill.alpha);
fz_pop_clip(csi->dev);
}
break;
}
}
if (dostroke)
{
switch (gstate->stroke.kind)
{
case PDF_MAT_NONE:
break;
case PDF_MAT_COLOR:
fz_stroke_path(csi->dev, path, &gstate->stroke_state, gstate->ctm,
gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha);
break;
case PDF_MAT_PATTERN:
if (gstate->stroke.pattern)
{
fz_clip_stroke_path(csi->dev, path, &bbox, &gstate->stroke_state, gstate->ctm);
pdf_show_pattern(csi, gstate->stroke.pattern, bbox, PDF_FILL);
fz_pop_clip(csi->dev);
}
break;
case PDF_MAT_SHADE:
if (gstate->stroke.shade)
{
fz_clip_stroke_path(csi->dev, path, &bbox, &gstate->stroke_state, gstate->ctm);
fz_fill_shade(csi->dev, gstate->stroke.shade, csi->top_ctm, gstate->stroke.alpha);
fz_pop_clip(csi->dev);
}
break;
}
}
if (dofill || dostroke)
pdf_end_group(csi);
fz_free_path(path);
}
/*
* Assemble and emit text
*/
static void
pdf_flush_text(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_text *text;
int dofill = 0;
int dostroke = 0;
int doclip = 0;
int doinvisible = 0;
fz_rect bbox;
if (!csi->text)
return;
text = csi->text;
csi->text = NULL;
dofill = dostroke = doclip = doinvisible = 0;
switch (csi->text_mode)
{
case 0: dofill = 1; break;
case 1: dostroke = 1; break;
case 2: dofill = dostroke = 1; break;
case 3: doinvisible = 1; break;
case 4: dofill = doclip = 1; break;
case 5: dostroke = doclip = 1; break;
case 6: dofill = dostroke = doclip = 1; break;
case 7: doclip = 1; break;
}
bbox = fz_bound_text(text, gstate->ctm);
pdf_begin_group(csi, bbox);
if (doinvisible)
fz_ignore_text(csi->dev, text, gstate->ctm);
if (doclip)
{
if (csi->accumulate < 2)
gstate->clip_depth++;
fz_clip_text(csi->dev, text, gstate->ctm, csi->accumulate);
csi->accumulate = 2;
}
if (dofill)
{
switch (gstate->fill.kind)
{
case PDF_MAT_NONE:
break;
case PDF_MAT_COLOR:
fz_fill_text(csi->dev, text, gstate->ctm,
gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
break;
case PDF_MAT_PATTERN:
if (gstate->fill.pattern)
{
fz_clip_text(csi->dev, text, gstate->ctm, 0);
pdf_show_pattern(csi, gstate->fill.pattern, bbox, PDF_FILL);
fz_pop_clip(csi->dev);
}
break;
case PDF_MAT_SHADE:
if (gstate->fill.shade)
{
fz_clip_text(csi->dev, text, gstate->ctm, 0);
fz_fill_shade(csi->dev, gstate->fill.shade, csi->top_ctm, gstate->fill.alpha);
fz_pop_clip(csi->dev);
}
break;
}
}
if (dostroke)
{
switch (gstate->stroke.kind)
{
case PDF_MAT_NONE:
break;
case PDF_MAT_COLOR:
fz_stroke_text(csi->dev, text, &gstate->stroke_state, gstate->ctm,
gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha);
break;
case PDF_MAT_PATTERN:
if (gstate->stroke.pattern)
{
fz_clip_stroke_text(csi->dev, text, &gstate->stroke_state, gstate->ctm);
pdf_show_pattern(csi, gstate->stroke.pattern, bbox, PDF_FILL);
fz_pop_clip(csi->dev);
}
break;
case PDF_MAT_SHADE:
if (gstate->stroke.shade)
{
fz_clip_stroke_text(csi->dev, text, &gstate->stroke_state, gstate->ctm);
fz_fill_shade(csi->dev, gstate->stroke.shade, csi->top_ctm, gstate->stroke.alpha);
fz_pop_clip(csi->dev);
}
break;
}
}
pdf_end_group(csi);
fz_free_text(text);
}
static void
pdf_show_char(pdf_csi *csi, int cid)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
pdf_font_desc *fontdesc = gstate->font;
fz_matrix tsm, trm;
float w0, w1, tx, ty;
pdf_hmtx h;
pdf_vmtx v;
int gid;
int ucsbuf[8];
int ucslen;
int i;
tsm.a = gstate->size * gstate->scale;
tsm.b = 0;
tsm.c = 0;
tsm.d = gstate->size;
tsm.e = 0;
tsm.f = gstate->rise;
ucslen = 0;
if (fontdesc->to_unicode)
ucslen = pdf_lookup_cmap_full(fontdesc->to_unicode, cid, ucsbuf);
if (ucslen == 0 && cid < fontdesc->cid_to_ucs_len)
{
ucsbuf[0] = fontdesc->cid_to_ucs[cid];
ucslen = 1;
}
if (ucslen == 0 || (ucslen == 1 && ucsbuf[0] == 0))
{
ucsbuf[0] = '?';
ucslen = 1;
}
gid = pdf_font_cid_to_gid(fontdesc, cid);
if (fontdesc->wmode == 1)
{
v = pdf_get_vmtx(fontdesc, cid);
tsm.e -= v.x * gstate->size * 0.001f;
tsm.f -= v.y * gstate->size * 0.001f;
}
trm = fz_concat(tsm, csi->tm);
/* flush buffered text if face or matrix or rendermode has changed */
if (!csi->text ||
fontdesc->font != csi->text->font ||
fontdesc->wmode != csi->text->wmode ||
fabsf(trm.a - csi->text->trm.a) > FLT_EPSILON ||
fabsf(trm.b - csi->text->trm.b) > FLT_EPSILON ||
fabsf(trm.c - csi->text->trm.c) > FLT_EPSILON ||
fabsf(trm.d - csi->text->trm.d) > FLT_EPSILON ||
gstate->render != csi->text_mode)
{
pdf_flush_text(csi);
csi->text = fz_new_text(fontdesc->font, trm, fontdesc->wmode);
csi->text->trm.e = 0;
csi->text->trm.f = 0;
csi->text_mode = gstate->render;
}
/* add glyph to textobject */
fz_add_text(csi->text, gid, ucsbuf[0], trm.e, trm.f);
/* add filler glyphs for one-to-many unicode mapping */
for (i = 1; i < ucslen; i++)
fz_add_text(csi->text, -1, ucsbuf[i], trm.e, trm.f);
if (fontdesc->wmode == 0)
{
h = pdf_get_hmtx(fontdesc, cid);
w0 = h.w * 0.001f;
tx = (w0 * gstate->size + gstate->char_space) * gstate->scale;
csi->tm = fz_concat(fz_translate(tx, 0), csi->tm);
}
if (fontdesc->wmode == 1)
{
w1 = v.w * 0.001f;
ty = w1 * gstate->size + gstate->char_space;
csi->tm = fz_concat(fz_translate(0, ty), csi->tm);
}
}
static void
pdf_show_space(pdf_csi *csi, float tadj)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
pdf_font_desc *fontdesc = gstate->font;
if (!fontdesc)
{
fz_warn("cannot draw text since font and size not set");
return;
}
if (fontdesc->wmode == 0)
csi->tm = fz_concat(fz_translate(tadj * gstate->scale, 0), csi->tm);
else
csi->tm = fz_concat(fz_translate(0, tadj), csi->tm);
}
static void
pdf_show_string(pdf_csi *csi, unsigned char *buf, int len)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
pdf_font_desc *fontdesc = gstate->font;
unsigned char *end = buf + len;
int cpt, cid;
if (!fontdesc)
{
fz_warn("cannot draw text since font and size not set");
return;
}
while (buf < end)
{
buf = pdf_decode_cmap(fontdesc->encoding, buf, &cpt);
cid = pdf_lookup_cmap(fontdesc->encoding, cpt);
if (cid >= 0)
pdf_show_char(csi, cid);
else
fz_warn("cannot encode character with code point %#x", cpt);
if (cpt == 32)
pdf_show_space(csi, gstate->word_space);
}
}
static void
pdf_show_text(pdf_csi *csi, fz_obj *text)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
int i;
if (fz_is_array(text))
{
for (i = 0; i < fz_array_len(text); i++)
{
fz_obj *item = fz_array_get(text, i);
if (fz_is_string(item))
pdf_show_string(csi, (unsigned char *)fz_to_str_buf(item), fz_to_str_len(item));
else
pdf_show_space(csi, - fz_to_real(item) * gstate->size * 0.001f);
}
}
else if (fz_is_string(text))
{
pdf_show_string(csi, (unsigned char *)fz_to_str_buf(text), fz_to_str_len(text));
}
}
/*
* Interpreter and graphics state stack.
*/
static void
pdf_init_gstate(pdf_gstate *gs, fz_matrix ctm)
{
gs->ctm = ctm;
gs->clip_depth = 0;
gs->stroke_state.start_cap = 0;
gs->stroke_state.dash_cap = 0;
gs->stroke_state.end_cap = 0;
gs->stroke_state.linejoin = 0;
gs->stroke_state.linewidth = 1;
gs->stroke_state.miterlimit = 10;
gs->stroke_state.dash_phase = 0;
gs->stroke_state.dash_len = 0;
memset(gs->stroke_state.dash_list, 0, sizeof(gs->stroke_state.dash_list));
gs->stroke.kind = PDF_MAT_COLOR;
gs->stroke.colorspace = fz_keep_colorspace(fz_device_gray);
gs->stroke.v[0] = 0;
gs->stroke.pattern = NULL;
gs->stroke.shade = NULL;
gs->stroke.alpha = 1;
gs->fill.kind = PDF_MAT_COLOR;
gs->fill.colorspace = fz_keep_colorspace(fz_device_gray);
gs->fill.v[0] = 0;
gs->fill.pattern = NULL;
gs->fill.shade = NULL;
gs->fill.alpha = 1;
gs->char_space = 0;
gs->word_space = 0;
gs->scale = 1;
gs->leading = 0;
gs->font = NULL;
gs->size = -1;
gs->render = 0;
gs->rise = 0;
gs->blendmode = 0;
gs->softmask = NULL;
gs->softmask_ctm = fz_identity;
gs->luminosity = 0;
}
static pdf_csi *
pdf_new_csi(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 = NULL;
csi->name[0] = 0;
csi->string_len = 0;
memset(csi->stack, 0, sizeof csi->stack);
csi->xbalance = 0;
csi->in_text = 0;
csi->path = fz_new_path();
csi->text = NULL;
csi->tlm = fz_identity;
csi->tm = fz_identity;
csi->text_mode = 0;
csi->accumulate = 1;
csi->top_ctm = ctm;
pdf_init_gstate(&csi->gstate[0], ctm);
csi->gtop = 0;
return csi;
}
static void
pdf_clear_stack(pdf_csi *csi)
{
int i;
if (csi->obj)
fz_drop_obj(csi->obj);
csi->obj = NULL;
csi->name[0] = 0;
csi->string_len = 0;
for (i = 0; i < csi->top; i++)
csi->stack[i] = 0;
csi->top = 0;
}
static pdf_material *
pdf_keep_material(pdf_material *mat)
{
if (mat->colorspace)
fz_keep_colorspace(mat->colorspace);
if (mat->pattern)
pdf_keep_pattern(mat->pattern);
if (mat->shade)
fz_keep_shade(mat->shade);
return mat;
}
static pdf_material *
pdf_drop_material(pdf_material *mat)
{
if (mat->colorspace)
fz_drop_colorspace(mat->colorspace);
if (mat->pattern)
pdf_drop_pattern(mat->pattern);
if (mat->shade)
fz_drop_shade(mat->shade);
return mat;
}
static 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_keep_material(&gs->stroke);
pdf_keep_material(&gs->fill);
if (gs->font)
pdf_keep_font(gs->font);
if (gs->softmask)
pdf_keep_xobject(gs->softmask);
}
static void
pdf_grestore(pdf_csi *csi)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
int clip_depth = gs->clip_depth;
if (csi->gtop == 0)
{
fz_warn("gstate underflow in content stream");
return;
}
pdf_drop_material(&gs->stroke);
pdf_drop_material(&gs->fill);
if (gs->font)
pdf_drop_font(gs->font);
if (gs->softmask)
pdf_drop_xobject(gs->softmask);
csi->gtop --;
gs = csi->gstate + csi->gtop;
while (clip_depth > gs->clip_depth)
{
fz_pop_clip(csi->dev);
clip_depth--;
}
}
static void
pdf_free_csi(pdf_csi *csi)
{
while (csi->gtop)
pdf_grestore(csi);
pdf_drop_material(&csi->gstate[0].fill);
pdf_drop_material(&csi->gstate[0].stroke);
if (csi->gstate[0].font)
pdf_drop_font(csi->gstate[0].font);
if (csi->gstate[0].softmask)
pdf_drop_xobject(csi->gstate[0].softmask);
while (csi->gstate[0].clip_depth--)
fz_pop_clip(csi->dev);
if (csi->path) fz_free_path(csi->path);
if (csi->text) fz_free_text(csi->text);
pdf_clear_stack(csi);
fz_free(csi);
}
/*
* Material state
*/
static void
pdf_set_colorspace(pdf_csi *csi, int what, fz_colorspace *colorspace)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
pdf_material *mat;
pdf_flush_text(csi);
mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
fz_drop_colorspace(mat->colorspace);
mat->kind = PDF_MAT_COLOR;
mat->colorspace = fz_keep_colorspace(colorspace);
mat->v[0] = 0;
mat->v[1] = 0;
mat->v[2] = 0;
mat->v[3] = 1;
}
static void
pdf_set_color(pdf_csi *csi, int what, float *v)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
pdf_material *mat;
int i;
pdf_flush_text(csi);
mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
switch (mat->kind)
{
case PDF_MAT_PATTERN:
case PDF_MAT_COLOR:
if (!strcmp(mat->colorspace->name, "Lab"))
{
mat->v[0] = v[0] / 100;
mat->v[1] = (v[1] + 100) / 200;
mat->v[2] = (v[2] + 100) / 200;
}
for (i = 0; i < mat->colorspace->n; i++)
mat->v[i] = v[i];
break;
default:
fz_warn("color incompatible with material");
}
}
static void
pdf_set_shade(pdf_csi *csi, int what, fz_shade *shade)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
pdf_material *mat;
pdf_flush_text(csi);
mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
if (mat->shade)
fz_drop_shade(mat->shade);
mat->kind = PDF_MAT_SHADE;
mat->shade = fz_keep_shade(shade);
}
static void
pdf_set_pattern(pdf_csi *csi, int what, pdf_pattern *pat, float *v)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
pdf_material *mat;
pdf_flush_text(csi);
mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
if (mat->pattern)
pdf_drop_pattern(mat->pattern);
mat->kind = PDF_MAT_PATTERN;
if (pat)
mat->pattern = pdf_keep_pattern(pat);
else
mat->pattern = NULL;
if (v)
pdf_set_color(csi, what, v);
}
static void
pdf_unset_pattern(pdf_csi *csi, int what)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
pdf_material *mat;
mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
if (mat->kind == PDF_MAT_PATTERN)
{
if (mat->pattern)
pdf_drop_pattern(mat->pattern);
mat->pattern = NULL;
mat->kind = PDF_MAT_COLOR;
}
}
/*
* Patterns, XObjects and ExtGState
*/
static void
pdf_show_pattern(pdf_csi *csi, pdf_pattern *pat, fz_rect area, int what)
{
pdf_gstate *gstate;
fz_matrix ptm, invptm;
fz_matrix oldtopctm;
fz_error error;
int x0, y0, x1, y1;
int oldtop;
pdf_gsave(csi);
gstate = csi->gstate + csi->gtop;
if (pat->ismask)
{
pdf_unset_pattern(csi, PDF_FILL);
pdf_unset_pattern(csi, PDF_STROKE);
if (what == PDF_FILL)
{
pdf_drop_material(&gstate->stroke);
pdf_keep_material(&gstate->fill);
gstate->stroke = gstate->fill;
}
if (what == PDF_STROKE)
{
pdf_drop_material(&gstate->fill);
pdf_keep_material(&gstate->stroke);
gstate->fill = gstate->stroke;
}
}
else
{
// TODO: unset only the current fill/stroke or both?
pdf_unset_pattern(csi, what);
}
/* don't apply softmasks to objects in the pattern as well */
if (gstate->softmask)
{
pdf_drop_xobject(gstate->softmask);
gstate->softmask = NULL;
}
ptm = fz_concat(pat->matrix, csi->top_ctm);
invptm = fz_invert_matrix(ptm);
/* patterns are painted using the ctm in effect at the beginning of the content stream */
/* get bbox of shape in pattern space for stamping */
area = fz_transform_rect(invptm, area);
x0 = floorf(area.x0 / pat->xstep);
y0 = floorf(area.y0 / pat->ystep);
x1 = ceilf(area.x1 / pat->xstep);
y1 = ceilf(area.y1 / pat->ystep);
oldtopctm = csi->top_ctm;
oldtop = csi->gtop;
#ifdef TILE
if ((x1 - x0) * (y1 - y0) > 0)
{
fz_begin_tile(csi->dev, area, pat->bbox, pat->xstep, pat->ystep, ptm);
gstate->ctm = ptm;
csi->top_ctm = gstate->ctm;
pdf_gsave(csi);
error = pdf_run_buffer(csi, pat->resources, pat->contents);
if (error)
fz_catch(error, "cannot render pattern tile");
pdf_grestore(csi);
while (oldtop < csi->gtop)
pdf_grestore(csi);
fz_end_tile(csi->dev);
}
#else
{
int x, y;
for (y = y0; y < y1; y++)
{
for (x = x0; x < x1; x++)
{
gstate->ctm = fz_concat(fz_translate(x * pat->xstep, y * pat->ystep), ptm);
csi->top_ctm = gstate->ctm;
error = pdf_run_csi_buffer(csi, pat->resources, pat->contents);
while (oldtop < csi->gtop)
pdf_grestore(csi);
if (error)
{
fz_catch(error, "cannot render pattern tile");
goto cleanup;
}
}
}
}
cleanup:
#endif
csi->top_ctm = oldtopctm;
pdf_grestore(csi);
}
static fz_error
pdf_run_xobject(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(xobj->matrix, transform);
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_transform_rect(gstate->ctm, xobj->bbox);
gstate->softmask = NULL;
popmask = 1;
fz_begin_mask(csi->dev, bbox, gstate->luminosity,
softmask->colorspace, gstate->softmask_bc);
error = pdf_run_xobject(csi, resources, softmask, fz_identity);
if (error)
return fz_rethrow(error, "cannot run softmask");
fz_end_mask(csi->dev);
pdf_drop_xobject(softmask);
}
fz_begin_group(csi->dev,
fz_transform_rect(gstate->ctm, xobj->bbox),
xobj->isolated, xobj->knockout, gstate->blendmode, gstate->fill.alpha);
gstate->blendmode = 0;
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);
pdf_show_clip(csi, 0);
pdf_show_path(csi, 0, 0, 0, 0);
/* run contents */
oldtopctm = csi->top_ctm;
csi->top_ctm = gstate->ctm;
if (xobj->resources)
resources = xobj->resources;
error = pdf_run_buffer(csi, resources, xobj->contents);
if (error)
fz_catch(error, "cannot interpret XObject stream");
csi->top_ctm = oldtopctm;
while (oldtop < csi->gtop)
pdf_grestore(csi);
pdf_grestore(csi);
/* wrap up transparency stacks */
if (xobj->transparency)
{
fz_end_group(csi->dev);
if (popmask)
fz_pop_clip(csi->dev);
}
return fz_okay;
}
static fz_error
pdf_run_extgstate(pdf_csi *csi, fz_obj *rdb, fz_obj *extgstate)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_colorspace *colorspace;
int i, k;
pdf_flush_text(csi);
for (i = 0; i < fz_dict_len(extgstate); i++)
{
fz_obj *key = fz_dict_get_key(extgstate, i);
fz_obj *val = fz_dict_get_val(extgstate, i);
char *s = fz_to_name(key);
if (!strcmp(s, "Font"))
{
if (fz_is_array(val) && fz_array_len(val) == 2)
{
fz_error error;
fz_obj *font = fz_array_get(val, 0);
if (gstate->font)
{
pdf_drop_font(gstate->font);
gstate->font = NULL;
}
error = pdf_load_font(&gstate->font, csi->xref, rdb, font);
if (error)
return fz_rethrow(error, "cannot load font (%d %d R)", fz_to_num(font), fz_to_gen(font));
if (!gstate->font)
return fz_throw("cannot find font in store");
gstate->size = fz_to_real(fz_array_get(val, 1));
}
else
return fz_throw("malformed /Font dictionary");
}
else if (!strcmp(s, "LC"))
{
gstate->stroke_state.start_cap = fz_to_int(val);
gstate->stroke_state.dash_cap = fz_to_int(val);
gstate->stroke_state.end_cap = fz_to_int(val);
}
else if (!strcmp(s, "LW"))
gstate->stroke_state.linewidth = fz_to_real(val);
else if (!strcmp(s, "LJ"))
gstate->stroke_state.linejoin = fz_to_int(val);
else if (!strcmp(s, "ML"))
gstate->stroke_state.miterlimit = fz_to_real(val);
else if (!strcmp(s, "D"))
{
if (fz_is_array(val) && fz_array_len(val) == 2)
{
fz_obj *dashes = fz_array_get(val, 0);
gstate->stroke_state.dash_len = MAX(fz_array_len(dashes), 32);
for (k = 0; k < gstate->stroke_state.dash_len; k++)
gstate->stroke_state.dash_list[k] = fz_to_real(fz_array_get(dashes, k));
gstate->stroke_state.dash_phase = fz_to_real(fz_array_get(val, 1));
}
else
return fz_throw("malformed /D");
}
else if (!strcmp(s, "CA"))
gstate->stroke.alpha = fz_to_real(val);
else if (!strcmp(s, "ca"))
gstate->fill.alpha = fz_to_real(val);
else if (!strcmp(s, "BM"))
{
if (fz_is_array(val))
val = fz_array_get(val, 0);
gstate->blendmode = fz_find_blendmode(fz_to_name(val));
}
else if (!strcmp(s, "SMask"))
{
if (fz_is_dict(val))
{
fz_error error;
pdf_xobject *xobj;
fz_obj *group, *luminosity, *bc;
if (gstate->softmask)
{
pdf_drop_xobject(gstate->softmask);
gstate->softmask = NULL;
}
group = fz_dict_gets(val, "G");
if (!group)
return fz_throw("cannot load softmask xobject (%d %d R)", fz_to_num(val), fz_to_gen(val));
error = pdf_load_xobject(&xobj, csi->xref, group);
if (error)
return fz_rethrow(error, "cannot load xobject (%d %d R)", fz_to_num(val), fz_to_gen(val));
colorspace = xobj->colorspace;
if (!colorspace)
colorspace = fz_device_gray;
gstate->softmask_ctm = fz_concat(xobj->matrix, gstate->ctm);
gstate->softmask = xobj;
for (k = 0; k < colorspace->n; k++)
gstate->softmask_bc[k] = 0;
bc = fz_dict_gets(val, "BC");
if (fz_is_array(bc))
{
for (k = 0; k < colorspace->n; k++)
gstate->softmask_bc[k] = fz_to_real(fz_array_get(bc, k));
}
luminosity = fz_dict_gets(val, "S");
if (fz_is_name(luminosity) && !strcmp(fz_to_name(luminosity), "Luminosity"))
gstate->luminosity = 1;
else
gstate->luminosity = 0;
}
else if (fz_is_name(val) && !strcmp(fz_to_name(val), "None"))
{
if (gstate->softmask)
{
pdf_drop_xobject(gstate->softmask);
gstate->softmask = NULL;
}
}
}
else if (!strcmp(s, "TR"))
{
if (fz_is_name(val) && strcmp(fz_to_name(val), "Identity"))
fz_warn("ignoring transfer function");
}
}
return fz_okay;
}
/*
* Operators
*/
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_pixmap *img;
fz_obj *obj;
error = pdf_parse_dict(&obj, csi->xref, file, buf, buflen);
if (error)
return fz_rethrow(error, "cannot parse inline image dictionary");
/* read whitespace after ID keyword */
ch = fz_read_byte(file);
if (ch == '\r')
if (fz_peek_byte(file) == '\n')
fz_read_byte(file);
error = pdf_load_inline_image(&img, csi->xref, rdb, obj, file);
fz_drop_obj(obj);
if (error)
return fz_rethrow(error, "cannot load inline image");
pdf_show_image(csi, img);
fz_drop_pixmap(img);
/* find EI */
ch = fz_read_byte(file);
while (ch != 'E' && ch != EOF)
ch = fz_read_byte(file);
ch = fz_read_byte(file);
if (ch != 'I')
return fz_rethrow(error, "syntax error after inline image");
return fz_okay;
}
static void pdf_run_B(pdf_csi *csi)
{
pdf_show_path(csi, 0, 1, 1, 0);
}
static void pdf_run_BMC(pdf_csi *csi)
{
}
static void pdf_run_BT(pdf_csi *csi)
{
csi->in_text = 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_show_path(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_set_pattern(csi, what, NULL, NULL);
}
else
{
if (!strcmp(csi->name, "DeviceGray"))
colorspace = fz_keep_colorspace(fz_device_gray);
else if (!strcmp(csi->name, "DeviceRGB"))
colorspace = fz_keep_colorspace(fz_device_rgb);
else if (!strcmp(csi->name, "DeviceCMYK"))
colorspace = fz_keep_colorspace(fz_device_cmyk);
else
{
dict = fz_dict_gets(rdb, "ColorSpace");
if (!dict)
return fz_throw("cannot find ColorSpace dictionary");
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find colorspace resource '%s'", csi->name);
error = pdf_load_colorspace(&colorspace, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load colorspace (%d 0 R)", fz_to_num(obj));
}
pdf_set_colorspace(csi, what, colorspace);
fz_drop_colorspace(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_STROKE);
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_FILL);
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_dict_gets(rdb, "XObject");
if (!dict)
return fz_throw("cannot find XObject dictionary when looking for: '%s'", csi->name);
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find xobject resource: '%s'", csi->name);
subtype = fz_dict_gets(obj, "Subtype");
if (!fz_is_name(subtype))
return fz_throw("no XObject subtype specified");
if (pdf_is_hidden_ocg(obj, csi->target))
return fz_okay;
if (!strcmp(fz_to_name(subtype), "Form") && fz_dict_gets(obj, "Subtype2"))
subtype = fz_dict_gets(obj, "Subtype2");
if (!strcmp(fz_to_name(subtype), "Form"))
{
pdf_xobject *xobj;
error = pdf_load_xobject(&xobj, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load xobject (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
/* Inherit parent resources, in case this one was empty XXX check where it's loaded */
if (!xobj->resources)
xobj->resources = fz_keep_obj(rdb);
error = pdf_run_xobject(csi, xobj->resources, xobj, fz_identity);
if (error)
return fz_rethrow(error, "cannot draw xobject (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
pdf_drop_xobject(xobj);
}
else if (!strcmp(fz_to_name(subtype), "Image"))
{
if ((csi->dev->hints & FZ_IGNORE_IMAGE) == 0)
{
fz_pixmap *img;
error = pdf_load_image(&img, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load image (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
pdf_show_image(csi, img);
fz_drop_pixmap(img);
}
}
else if (!strcmp(fz_to_name(subtype), "PS"))
{
fz_warn("ignoring XObject with subtype PS");
}
else
{
return fz_throw("unknown XObject subtype: '%s'", fz_to_name(subtype));
}
return fz_okay;
}
static void pdf_run_EMC(pdf_csi *csi)
{
}
static void pdf_run_ET(pdf_csi *csi)
{
pdf_flush_text(csi);
csi->accumulate = 1;
csi->in_text = 0;
}
static void pdf_run_EX(pdf_csi *csi)
{
csi->xbalance --;
}
static void pdf_run_F(pdf_csi *csi)
{
pdf_show_path(csi, 0, 1, 0, 0);
}
static void pdf_run_G(pdf_csi *csi)
{
pdf_set_colorspace(csi, PDF_STROKE, fz_device_gray);
pdf_set_color(csi, PDF_STROKE, csi->stack);
}
static void pdf_run_J(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->stroke_state.start_cap = csi->stack[0];
gstate->stroke_state.dash_cap = csi->stack[0];
gstate->stroke_state.end_cap = csi->stack[0];
}
static void pdf_run_K(pdf_csi *csi)
{
pdf_set_colorspace(csi, PDF_STROKE, fz_device_cmyk);
pdf_set_color(csi, PDF_STROKE, csi->stack);
}
static void pdf_run_M(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->stroke_state.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_set_colorspace(csi, PDF_STROKE, fz_device_rgb);
pdf_set_color(csi, PDF_STROKE, csi->stack);
}
static void pdf_run_S(pdf_csi *csi)
{
pdf_show_path(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_MAT_PATTERN;
switch (kind)
{
case PDF_MAT_NONE:
return fz_throw("cannot set color in mask objects");
case PDF_MAT_COLOR:
pdf_set_color(csi, what, csi->stack);
break;
case PDF_MAT_PATTERN:
dict = fz_dict_gets(rdb, "Pattern");
if (!dict)
return fz_throw("cannot find Pattern dictionary");
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find pattern resource '%s'", csi->name);
patterntype = fz_dict_gets(obj, "PatternType");
if (fz_to_int(patterntype) == 1)
{
pdf_pattern *pat;
error = pdf_load_pattern(&pat, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load pattern (%d 0 R)", fz_to_num(obj));
pdf_set_pattern(csi, what, pat, csi->top > 0 ? csi->stack : NULL);
pdf_drop_pattern(pat);
}
else if (fz_to_int(patterntype) == 2)
{
fz_shade *shd;
error = pdf_load_shading(&shd, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load shading (%d 0 R)", fz_to_num(obj));
pdf_set_shade(csi, what, shd);
fz_drop_shade(shd);
}
else
{
return fz_throw("unknown pattern type: %d", fz_to_int(patterntype));
}
break;
case PDF_MAT_SHADE:
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_STROKE, &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_FILL, &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->char_space = csi->stack[0];
}
static void pdf_run_Tw(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->word_space = 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_flush_text(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_drop_font(gstate->font);
gstate->font = NULL;
dict = fz_dict_gets(rdb, "Font");
if (!dict)
return fz_throw("cannot find Font dictionary");
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find font resource: '%s'", csi->name);
error = pdf_load_font(&gstate->font, csi->xref, rdb, obj);
if (error)
return fz_rethrow(error, "cannot load font (%d 0 R)", fz_to_num(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)
{
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->string_len)
pdf_show_string(csi, csi->string, csi->string_len);
else
pdf_show_text(csi, csi->obj);
}
static void pdf_run_TJ(pdf_csi *csi)
{
if (csi->string_len)
pdf_show_string(csi, csi->string, csi->string_len);
else
pdf_show_text(csi, csi->obj);
}
static void pdf_run_W(pdf_csi *csi)
{
pdf_show_clip(csi, 0);
}
static void pdf_run_Wstar(pdf_csi *csi)
{
pdf_show_clip(csi, 1);
}
static void pdf_run_b(pdf_csi *csi)
{
pdf_show_path(csi, 1, 1, 1, 0);
}
static void pdf_run_bstar(pdf_csi *csi)
{
pdf_show_path(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->stroke_state.dash_len = MIN(fz_array_len(array), nelem(gstate->stroke_state.dash_list));
for (i = 0; i < gstate->stroke_state.dash_len; i++)
gstate->stroke_state.dash_list[i] = fz_to_real(fz_array_get(array, i));
gstate->stroke_state.dash_phase = csi->stack[0];
}
static void pdf_run_d0(pdf_csi *csi)
{
csi->dev->flags |= FZ_CHARPROC_COLOR;
}
static void pdf_run_d1(pdf_csi *csi)
{
csi->dev->flags |= FZ_CHARPROC_MASK;
}
static void pdf_run_f(pdf_csi *csi)
{
pdf_show_path(csi, 0, 1, 0, 0);
}
static void pdf_run_fstar(pdf_csi *csi)
{
pdf_show_path(csi, 0, 1, 0, 1);
}
static void pdf_run_g(pdf_csi *csi)
{
pdf_set_colorspace(csi, PDF_FILL, fz_device_gray);
pdf_set_color(csi, PDF_FILL, 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_dict_gets(rdb, "ExtGState");
if (!dict)
return fz_throw("cannot find ExtGState dictionary");
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find extgstate resource '%s'", csi->name);
error = pdf_run_extgstate(csi, rdb, obj);
if (error)
return fz_rethrow(error, "cannot set ExtGState (%d 0 R)", fz_to_num(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->stroke_state.linejoin = csi->stack[0];
}
static void pdf_run_k(pdf_csi *csi)
{
pdf_set_colorspace(csi, PDF_FILL, fz_device_cmyk);
pdf_set_color(csi, PDF_FILL, 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_show_path(csi, 0, 0, 0, 0);
}
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_set_colorspace(csi, PDF_FILL, fz_device_rgb);
pdf_set_color(csi, PDF_FILL, csi->stack);
}
static void pdf_run_ri(pdf_csi *csi)
{
}
static void pdf_run(pdf_csi *csi)
{
pdf_show_path(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_dict_gets(rdb, "Shading");
if (!dict)
return fz_throw("cannot find shading dictionary");
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find shading resource: '%s'", csi->name);
if ((csi->dev->hints & FZ_IGNORE_SHADE) == 0)
{
error = pdf_load_shading(&shd, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load shading (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
pdf_show_shade(csi, shd);
fz_drop_shade(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;
pdf_flush_text(csi); /* linewidth affects stroked text rendering mode */
gstate->stroke_state.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->string_len)
pdf_show_string(csi, csi->string, csi->string_len);
else
pdf_show_text(csi, csi->obj);
}
static void pdf_run_dquote(pdf_csi *csi)
{
fz_matrix m;
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->word_space = csi->stack[0];
gstate->char_space = csi->stack[1];
m = fz_translate(0, -gstate->leading);
csi->tlm = fz_concat(m, csi->tlm);
csi->tm = csi->tlm;
if (csi->string_len)
pdf_show_string(csi, csi->string, csi->string_len);
else
pdf_show_text(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_run_keyword(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(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_run_stream(pdf_csi *csi, fz_obj *rdb, fz_stream *file, char *buf, int buflen)
{
fz_error error;
int tok, len, in_array;
/* make sure we have a clean slate if we come here from flush_text */
pdf_clear_stack(csi);
in_array = 0;
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 (in_array)
{
if (tok == PDF_TOK_CLOSE_ARRAY)
{
in_array = 0;
}
else if (tok == PDF_TOK_INT || tok == PDF_TOK_REAL)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
pdf_show_space(csi, -fz_atof(buf) * gstate->size * 0.001f);
}
else if (tok == PDF_TOK_STRING)
{
pdf_show_string(csi, (unsigned char *)buf, len);
}
else if (tok == PDF_TOK_KEYWORD)
{
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_TOK_EOF)
return fz_okay;
else
return fz_throw("syntax error in array");
}
else switch (tok)
{
case PDF_TOK_ENDSTREAM:
case PDF_TOK_EOF:
return fz_okay;
case PDF_TOK_OPEN_ARRAY:
if (!csi->in_text)
{
error = pdf_parse_array(&csi->obj, csi->xref, file, buf, buflen);
if (error)
return fz_rethrow(error, "cannot parse array");
}
else
{
in_array = 1;
}
break;
case PDF_TOK_OPEN_DICT:
error = pdf_parse_dict(&csi->obj, csi->xref, file, buf, buflen);
if (error)
return fz_rethrow(error, "cannot parse dictionary");
break;
case PDF_TOK_NAME:
fz_strlcpy(csi->name, buf, sizeof(csi->name));
break;
case PDF_TOK_INT:
csi->stack[csi->top] = atoi(buf);
csi->top ++;
break;
case PDF_TOK_REAL:
csi->stack[csi->top] = fz_atof(buf);
csi->top ++;
break;
case PDF_TOK_STRING:
if (len <= sizeof(csi->string))
{
memcpy(csi->string, buf, len);
csi->string_len = len;
}
else
{
csi->obj = fz_new_string(buf, len);
}
break;
case PDF_TOK_KEYWORD:
error = pdf_run_keyword(csi, rdb, file, buf);
if (error)
return fz_rethrow(error, "cannot run keyword");
pdf_clear_stack(csi);
break;
default:
return fz_throw("syntax error in content stream");
}
}
}
/*
* Entry points
*/
static fz_error
pdf_run_buffer(pdf_csi *csi, fz_obj *rdb, fz_buffer *contents)
{
fz_error error;
int len = sizeof csi->xref->scratch;
char *buf = fz_malloc(len); /* we must be re-entrant for type3 fonts */
fz_stream *file = fz_open_buffer(contents);
int save_in_text = csi->in_text;
csi->in_text = 0;
error = pdf_run_stream(csi, rdb, file, buf, len);
csi->in_text = save_in_text;
fz_close(file);
fz_free(buf);
if (error)
return fz_rethrow(error, "cannot parse content stream");
return fz_okay;
}
fz_error
pdf_run_page_with_usage(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)
fz_begin_group(dev, fz_transform_rect(ctm, page->mediabox), 1, 0, 0, 1);
csi = pdf_new_csi(xref, dev, ctm, target);
error = pdf_run_buffer(csi, page->resources, page->contents);
pdf_free_csi(csi);
if (error)
return fz_rethrow(error, "cannot parse page content stream");
for (annot = page->annots; annot; annot = annot->next)
{
flags = fz_to_int(fz_dict_gets(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_is_hidden_ocg(annot->obj, target))
continue;
csi = pdf_new_csi(xref, dev, ctm, target);
error = pdf_run_xobject(csi, page->resources, annot->ap, annot->matrix);
pdf_free_csi(csi);
if (error)
return fz_rethrow(error, "cannot parse annotation appearance stream");
}
if (page->transparency)
fz_end_group(dev);
return fz_okay;
}
fz_error
pdf_run_page(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm)
{
return pdf_run_page_with_usage(xref, page, dev, ctm, "View");
}
fz_error
pdf_run_glyph(pdf_xref *xref, fz_obj *resources, fz_buffer *contents, fz_device *dev, fz_matrix ctm)
{
pdf_csi *csi = pdf_new_csi(xref, dev, ctm, "View");
fz_error error = pdf_run_buffer(csi, resources, contents);
pdf_free_csi(csi);
if (error)
return fz_rethrow(error, "cannot parse glyph content stream");
return fz_okay;
}