#include #include void pdf_initgstate(pdf_gstate *gs) { gs->linewidth = 1.0; gs->linecap = 0; gs->linejoin = 0; gs->miterlimit = 10; gs->dashphase = 0; gs->dashlen = 0; memset(gs->dashlist, 0, sizeof(gs->dashlist)); gs->stroke.kind = PDF_MCOLOR; gs->stroke.cs = pdf_devicegray; gs->stroke.v[0] = 0; gs->fill.kind = PDF_MCOLOR; gs->fill.cs = pdf_devicegray; gs->fill.v[0] = 0; gs->charspace = 0; gs->wordspace = 0; gs->scale = 1; gs->leading = 0; gs->font = nil; gs->size = -1; gs->render = 0; gs->rise = 0; gs->head = nil; } fz_error * pdf_setcolorspace(pdf_csi *csi, int what, fz_colorspace *cs) { pdf_gstate *gs = csi->gstate + csi->gtop; fz_error *error; pdf_material *mat; error = pdf_flushtext(csi); if (error) return error; mat = what == PDF_MFILL ? &gs->fill : &gs->stroke; mat->kind = PDF_MCOLOR; mat->cs = cs; mat->v[0] = 0; // FIXME: default color mat->v[1] = 0; // FIXME: default color mat->v[2] = 0; // FIXME: default color mat->v[3] = 1; // FIXME: default color if (!strcmp(cs->name, "Indexed")) { mat->kind = PDF_MINDEXED; mat->indexed = (pdf_indexed*)cs; mat->cs = mat->indexed->base; } if (!strcmp(cs->name, "Lab")) mat->kind = PDF_MLAB; return nil; } fz_error * pdf_setcolor(pdf_csi *csi, int what, float *v) { pdf_gstate *gs = csi->gstate + csi->gtop; fz_error *error; pdf_indexed *ind; pdf_material *mat; int i, k; error = pdf_flushtext(csi); if (error) return error; mat = what == PDF_MFILL ? &gs->fill : &gs->stroke; switch (mat->kind) { case PDF_MCOLOR: for (i = 0; i < mat->cs->n; i++) mat->v[i] = v[i]; break; case PDF_MLAB: mat->v[0] = v[0] / 100.0; mat->v[1] = (v[1] + 100) / 200.0; mat->v[2] = (v[2] + 100) / 200.0; break; case PDF_MINDEXED: ind = mat->indexed; i = CLAMP(v[0], 0, ind->high); for (k = 0; k < ind->base->n; k++) mat->v[k] = ind->lookup[ind->base->n * i + k] / 255.0; break; default: return fz_throw("syntaxerror: color not compatible with material"); } return nil; } fz_error * pdf_buildstrokepath(pdf_gstate *gs, fz_pathnode *path) { fz_error *error; fz_stroke stroke; fz_dash *dash; stroke.linecap = gs->linecap; stroke.linejoin = gs->linejoin; stroke.linewidth = gs->linewidth; stroke.miterlimit = gs->miterlimit; if (gs->dashlen) { error = fz_newdash(&dash, gs->dashphase, gs->dashlen, gs->dashlist); if (error) return error; } else dash = nil; error = fz_endpath(path, FZ_STROKE, &stroke, dash); if (error) { fz_dropdash(dash); return error; } return nil; } fz_error * pdf_buildfillpath(pdf_gstate *gs, fz_pathnode *path, int eofill) { return fz_endpath(path, eofill ? FZ_EOFILL : FZ_FILL, nil, nil); } static fz_error * addcolorshape(pdf_gstate *gs, fz_node *shape, fz_colorspace *cs, float *v) { fz_error *error; fz_node *mask; fz_node *solid; error = fz_newmasknode(&mask); if (error) return error; error = fz_newcolornode(&solid, cs, cs->n, v); if (error) return error; fz_insertnode(mask, shape); fz_insertnode(mask, solid); fz_insertnode(gs->head, mask); return nil; } fz_error * pdf_addfillshape(pdf_gstate *gs, fz_node *shape) { switch (gs->fill.kind) { case PDF_MNONE: fz_insertnode(gs->head, shape); return nil; case PDF_MCOLOR: return addcolorshape(gs, shape, gs->fill.cs, gs->fill.v); default: return fz_throw("unimplemented material"); } } fz_error * pdf_addstrokeshape(pdf_gstate *gs, fz_node *shape) { switch (gs->stroke.kind) { case PDF_MNONE: fz_insertnode(gs->head, shape); return nil; case PDF_MCOLOR: return addcolorshape(gs, shape, gs->stroke.cs, gs->stroke.v); default: return fz_throw("unimplemented material"); } } fz_error * pdf_addclipmask(pdf_gstate *gs, fz_node *shape) { fz_error *error; fz_node *mask; fz_node *over; error = fz_newmasknode(&mask); if (error) return error; error = fz_newovernode(&over); if (error) return error; fz_insertnode(mask, shape); fz_insertnode(mask, over); fz_insertnode(gs->head, mask); gs->head = over; return nil; } fz_error * pdf_addtransform(pdf_gstate *gs, fz_node *transform) { fz_error *error; fz_node *over; error = fz_newovernode(&over); if (error) return error; fz_insertnode(gs->head, transform); fz_insertnode(transform, over); gs->head = over; return nil; } fz_error * pdf_showimage(pdf_csi *csi, pdf_image *img) { fz_error *error; fz_node *mask; fz_node *color; fz_node *shape; error = fz_newimagenode(&color, (fz_image*)img); if (error) return error; if (img->super.n == 0 && img->super.a == 1) { error = pdf_addfillshape(csi->gstate + csi->gtop, color); if (error) { fz_dropnode(color); return error; } } else { if (img->mask) { error = fz_newimagenode(&shape, (fz_image*)img->mask); if (error) return error; error = fz_newmasknode(&mask); if (error) return error; fz_insertnode(mask, shape); fz_insertnode(mask, color); fz_insertnode(csi->gstate[csi->gtop].head, mask); } else { fz_insertnode(csi->gstate[csi->gtop].head, color); } } return nil; } #if 0 BMC ... EMC object nesting can be completely fucked up and out of sync with graphics object nesting. fz_error * pdf_beginmarkedcontent(pdf_gstate *gs, fz_node *meta) { fz_error *error; fz_node *over; error = fz_newovernode(&over); if (error) return error; fz_insertnode(gs->head, meta); fz_insertnode(meta, over); gs->head = over; printf("begin mc meta=%p over=%p\n", meta, over); { fz_node *node = gs->head; while (node) { printf(" node=%p ismeta=%d\n", node, fz_ismetanode(node)); node = node->parent; } printf("okay.\n"); } return nil; } fz_error * pdf_endmarkedcontent(pdf_gstate *gs) { fz_node *node = gs->head; printf("end mc\n"); printf(" node=%p ismeta=%d\n", node, fz_ismetanode(node)); while (node && !fz_ismetanode(node)) { printf(" node=%p ismeta=%d\n", node, fz_ismetanode(node)); node = node->parent; } if (node == nil) return fz_throw("syntaxerror: unbalanced marked content"); gs->head = node->parent; return nil; } #endif fz_error * pdf_showpath(pdf_csi *csi, int doclose, int dofill, int dostroke, int evenodd) { pdf_gstate *gstate = csi->gstate + csi->gtop; fz_error *error; fz_pathnode *spath; fz_pathnode *fpath; if (doclose) { error = fz_closepath(csi->path); if (error) return error; } if (dofill && dostroke) { fpath = csi->path; error = fz_clonepath(&spath, fpath); if (error) return error; } else { spath = fpath = csi->path; } if (dofill) { error = pdf_buildfillpath(gstate, fpath, evenodd); if (error) return error; error = pdf_addfillshape(gstate, (fz_node*)fpath); if (error) return error; } if (dostroke) { error = pdf_buildstrokepath(gstate, spath); if (error) return error; error = pdf_addstrokeshape(gstate, (fz_node*)spath); if (error) return error; } if (!dofill && !dostroke) { fz_free(csi->path); } if (csi->clip) { error = pdf_addclipmask(gstate, (fz_node*)csi->clip); if (error) return error; csi->clip = nil; } csi->path = nil; error = fz_newpathnode(&csi->path); if (error) return error; return nil; } fz_error * pdf_flushtext(pdf_csi *csi) { pdf_gstate *gstate = csi->gstate + csi->gtop; fz_error *error; if (csi->text) { error = pdf_addfillshape(gstate, (fz_node*)csi->text); if (error) return error; csi->text = nil; } return nil; } fz_error * showglyph(pdf_csi *csi, int cid) { pdf_gstate *gstate = csi->gstate + csi->gtop; pdf_font *font = gstate->font; fz_error *error; fz_matrix tsm, trm, tm; float w0, w1, tx, ty; fz_hmtx h; fz_vmtx v; tsm.a = gstate->size * gstate->scale; tsm.b = 0; tsm.c = 0; tsm.d = gstate->size; tsm.e = 0; tsm.f = gstate->rise; tm = csi->tm; if (font->super.wmode == 1) { v = fz_getvmtx((fz_font*)font, cid); tm.e -= v.x * gstate->size / 1000.0; tm.f += v.y * gstate->size / 1000.0; } trm = fz_concat(tsm, tm); /* flush buffered text if face or matrix has changed */ if (!csi->text || ((fz_font*)font) != csi->text->font || fabs(trm.a - csi->text->trm.a) > FLT_EPSILON || fabs(trm.b - csi->text->trm.b) > FLT_EPSILON || fabs(trm.c - csi->text->trm.c) > FLT_EPSILON || fabs(trm.d - csi->text->trm.d) > FLT_EPSILON) { error = pdf_flushtext(csi); if (error) return error; error = fz_newtextnode(&csi->text, (fz_font*)font); if (error) return error; csi->text->trm = trm; csi->text->trm.e = 0; csi->text->trm.f = 0; } /* add glyph to textobject */ error = fz_addtext(csi->text, cid, trm.e, trm.f); if (error) return error; if (font->super.wmode == 0) { h = fz_gethmtx((fz_font*)font, cid); w0 = h.w / 1000.0; tx = (w0 * gstate->size + gstate->charspace) * gstate->scale; csi->tm = fz_concat(fz_translate(tx, 0), csi->tm); } else { w1 = v.w / 1000.0; ty = w1 * gstate->size + gstate->charspace; csi->tm = fz_concat(fz_translate(0, ty), csi->tm); } return nil; } void showspace(pdf_csi *csi, float tadj) { pdf_gstate *gstate = csi->gstate + csi->gtop; pdf_font *font = gstate->font; if (font->super.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); } fz_error * pdf_showtext(pdf_csi *csi, fz_obj *text) { pdf_gstate *gstate = csi->gstate + csi->gtop; pdf_font *font = gstate->font; fz_error *error; unsigned char *buf; unsigned char *end; int i, len; int cpt, cid; if (fz_isarray(text)) { for (i = 0; i < fz_arraylen(text); i++) { fz_obj *item = fz_arrayget(text, i); if (fz_isstring(item)) { error = pdf_showtext(csi, item); if (error) return error; } else { showspace(csi, - fz_toreal(item) * gstate->size / 1000.0); } } return nil; } buf = fz_tostringbuf(text); len = fz_tostringlen(text); end = buf + len; while (buf < end) { buf = fz_decodecpt(font->encoding, buf, &cpt); cid = fz_lookupcid(font->encoding, cpt); //printf("gl %s %g [%g %g %g %g %g %g] cpt<%02x> cid %d h %d\n", // font->super.name, size, // csi->tm.a, csi->tm.b, csi->tm.c, csi->tm.d, csi->tm.e, csi->tm.f, // cpt, cid, font->super.hadv[gid]); error = showglyph(csi, cid); if (error) return error; if (cpt == 32) showspace(csi, gstate->wordspace); } return nil; }