forked from KolibriOS/kolibrios
2305 lines
50 KiB
C
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;
|
||
|
}
|