summaryrefslogtreecommitdiff
path: root/pdf/pdf_build.c
diff options
context:
space:
mode:
Diffstat (limited to 'pdf/pdf_build.c')
-rw-r--r--pdf/pdf_build.c718
1 files changed, 718 insertions, 0 deletions
diff --git a/pdf/pdf_build.c b/pdf/pdf_build.c
new file mode 100644
index 00000000..616f1c86
--- /dev/null
+++ b/pdf/pdf_build.c
@@ -0,0 +1,718 @@
+#include "fitz.h"
+#include "mupdf.h"
+
+#define TILE
+
+void
+pdf_initgstate(pdf_gstate *gs, fz_matrix ctm)
+{
+ gs->ctm = ctm;
+ gs->clipdepth = 0;
+
+ gs->strokestate.linewidth = 1;
+ gs->strokestate.linecap = 0;
+ gs->strokestate.linejoin = 0;
+ gs->strokestate.miterlimit = 10;
+ gs->strokestate.dashphase = 0;
+ gs->strokestate.dashlen = 0;
+ memset(gs->strokestate.dashlist, 0, sizeof(gs->strokestate.dashlist));
+
+ gs->stroke.kind = PDF_MCOLOR;
+ gs->stroke.colorspace = fz_keepcolorspace(fz_devicegray);
+ gs->stroke.v[0] = 0;
+ gs->stroke.pattern = nil;
+ gs->stroke.shade = nil;
+ gs->stroke.alpha = 1;
+
+ gs->fill.kind = PDF_MCOLOR;
+ gs->fill.colorspace = fz_keepcolorspace(fz_devicegray);
+ gs->fill.v[0] = 0;
+ gs->fill.pattern = nil;
+ gs->fill.shade = nil;
+ gs->fill.alpha = 1;
+
+ 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->blendmode = FZ_BNORMAL;
+ gs->softmask = nil;
+ gs->softmaskctm = fz_identity;
+ gs->luminosity = 0;
+}
+
+void
+pdf_setcolorspace(pdf_csi *csi, int what, fz_colorspace *colorspace)
+{
+ pdf_gstate *gs = csi->gstate + csi->gtop;
+ pdf_material *mat;
+
+ pdf_flushtext(csi);
+
+ mat = what == PDF_MFILL ? &gs->fill : &gs->stroke;
+
+ fz_dropcolorspace(mat->colorspace);
+
+ mat->kind = PDF_MCOLOR;
+ mat->colorspace = fz_keepcolorspace(colorspace);
+
+ mat->v[0] = 0;
+ mat->v[1] = 0;
+ mat->v[2] = 0;
+ mat->v[3] = 1;
+}
+
+void
+pdf_setcolor(pdf_csi *csi, int what, float *v)
+{
+ pdf_gstate *gs = csi->gstate + csi->gtop;
+ pdf_material *mat;
+ int i;
+
+ pdf_flushtext(csi);
+
+ mat = what == PDF_MFILL ? &gs->fill : &gs->stroke;
+
+ switch (mat->kind)
+ {
+ case PDF_MPATTERN:
+ case PDF_MCOLOR:
+ 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_unsetpattern(pdf_csi *csi, int what)
+{
+ pdf_gstate *gs = csi->gstate + csi->gtop;
+ pdf_material *mat;
+ mat = what == PDF_MFILL ? &gs->fill : &gs->stroke;
+ if (mat->kind == PDF_MPATTERN)
+ {
+ if (mat->pattern)
+ pdf_droppattern(mat->pattern);
+ mat->pattern = nil;
+ mat->kind = PDF_MCOLOR;
+ }
+}
+
+void
+pdf_setpattern(pdf_csi *csi, int what, pdf_pattern *pat, float *v)
+{
+ pdf_gstate *gs = csi->gstate + csi->gtop;
+ pdf_material *mat;
+
+ pdf_flushtext(csi);
+
+ mat = what == PDF_MFILL ? &gs->fill : &gs->stroke;
+
+ if (mat->pattern)
+ pdf_droppattern(mat->pattern);
+
+ mat->kind = PDF_MPATTERN;
+ if (pat)
+ mat->pattern = pdf_keeppattern(pat);
+ else
+ mat->pattern = nil;
+
+ if (v)
+ pdf_setcolor(csi, what, v);
+}
+
+void
+pdf_setshade(pdf_csi *csi, int what, fz_shade *shade)
+{
+ pdf_gstate *gs = csi->gstate + csi->gtop;
+ pdf_material *mat;
+
+ pdf_flushtext(csi);
+
+ mat = what == PDF_MFILL ? &gs->fill : &gs->stroke;
+
+ if (mat->shade)
+ fz_dropshade(mat->shade);
+
+ mat->kind = PDF_MSHADE;
+ mat->shade = fz_keepshade(shade);
+}
+
+static void
+pdf_showpattern(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_unsetpattern(csi, PDF_MFILL);
+ pdf_unsetpattern(csi, PDF_MSTROKE);
+ if (what == PDF_MFILL)
+ {
+ pdf_dropmaterial(&gstate->stroke);
+ pdf_keepmaterial(&gstate->fill);
+ gstate->stroke = gstate->fill;
+ }
+ if (what == PDF_MSTROKE)
+ {
+ pdf_dropmaterial(&gstate->fill);
+ pdf_keepmaterial(&gstate->stroke);
+ gstate->fill = gstate->stroke;
+ }
+ }
+ else
+ {
+ // TODO: unset only the current fill/stroke or both?
+ pdf_unsetpattern(csi, what);
+ }
+
+ /* don't apply softmasks to objects in the pattern as well */
+ if (gstate->softmask)
+ {
+ pdf_dropxobject(gstate->softmask);
+ gstate->softmask = nil;
+ }
+
+ ptm = fz_concat(pat->matrix, csi->topctm);
+ invptm = fz_invertmatrix(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_transformrect(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->topctm;
+ oldtop = csi->gtop;
+
+#ifdef TILE
+ if ((x1 - x0) * (y1 - y0) > 0)
+ {
+ csi->dev->begintile(csi->dev->user, area, pat->bbox, pat->xstep, pat->ystep, ptm);
+ gstate->ctm = ptm;
+ csi->topctm = gstate->ctm;
+ error = pdf_runcsibuffer(csi, pat->resources, pat->contents);
+ if (error)
+ fz_catch(error, "cannot render pattern tile");
+ while (oldtop < csi->gtop)
+ pdf_grestore(csi);
+ csi->dev->endtile(csi->dev->user);
+ }
+#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->topctm = gstate->ctm;
+ error = pdf_runcsibuffer(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->topctm = oldtopctm;
+
+ pdf_grestore(csi);
+}
+
+static void
+pdf_begingroup(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_transformrect(gstate->ctm, softmask->bbox);
+
+ gstate->softmask = nil;
+
+ csi->dev->beginmask(csi->dev->user, bbox, gstate->luminosity,
+ softmask->colorspace, gstate->softmaskbc);
+ error = pdf_runxobject(csi, nil, softmask, fz_identity);
+ if (error)
+ fz_catch(error, "cannot run softmask");
+ csi->dev->endmask(csi->dev->user);
+
+ gstate->softmask = softmask;
+ }
+
+ if (gstate->blendmode != FZ_BNORMAL)
+ csi->dev->begingroup(csi->dev->user, bbox, 0, 0, gstate->blendmode, 1);
+}
+
+static void
+pdf_endgroup(pdf_csi *csi)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+
+ if (gstate->blendmode != FZ_BNORMAL)
+ csi->dev->endgroup(csi->dev->user);
+
+ if (gstate->softmask)
+ csi->dev->popclip(csi->dev->user);
+}
+
+void
+pdf_showshade(pdf_csi *csi, fz_shade *shd)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ fz_rect bbox;
+
+ bbox = fz_boundshade(shd, gstate->ctm);
+
+ pdf_begingroup(csi, bbox);
+
+ csi->dev->fillshade(csi->dev->user, shd, gstate->ctm, gstate->fill.alpha);
+
+ pdf_endgroup(csi);
+}
+
+void
+pdf_showimage(pdf_csi *csi, fz_pixmap *image)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ fz_rect bbox;
+
+ bbox = fz_transformrect(gstate->ctm, fz_unitrect);
+
+ if (image->mask)
+ {
+ /* apply blend group even though we skip the softmask */
+ if (gstate->blendmode != FZ_BNORMAL)
+ csi->dev->begingroup(csi->dev->user, bbox, 0, 0, gstate->blendmode, 1);
+ csi->dev->clipimagemask(csi->dev->user, image->mask, gstate->ctm);
+ }
+ else
+ pdf_begingroup(csi, bbox);
+
+ if (!image->colorspace)
+ {
+
+ switch (gstate->fill.kind)
+ {
+ case PDF_MNONE:
+ break;
+ case PDF_MCOLOR:
+ csi->dev->fillimagemask(csi->dev->user, image, gstate->ctm,
+ gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
+ break;
+ case PDF_MPATTERN:
+ if (gstate->fill.pattern)
+ {
+ csi->dev->clipimagemask(csi->dev->user, image, gstate->ctm);
+ pdf_showpattern(csi, gstate->fill.pattern, bbox, PDF_MFILL);
+ csi->dev->popclip(csi->dev->user);
+ }
+ break;
+ case PDF_MSHADE:
+ if (gstate->fill.shade)
+ {
+ csi->dev->clipimagemask(csi->dev->user, image, gstate->ctm);
+ csi->dev->fillshade(csi->dev->user, gstate->fill.shade, gstate->ctm, gstate->fill.alpha);
+ csi->dev->popclip(csi->dev->user);
+ }
+ break;
+ }
+ }
+ else
+ {
+ csi->dev->fillimage(csi->dev->user, image, gstate->ctm, gstate->fill.alpha);
+ }
+
+ if (image->mask)
+ {
+ csi->dev->popclip(csi->dev->user);
+ if (gstate->blendmode != FZ_BNORMAL)
+ csi->dev->endgroup(csi->dev->user);
+ }
+ else
+ pdf_endgroup(csi);
+}
+
+void
+pdf_showpath(pdf_csi *csi, int doclose, int dofill, int dostroke, int evenodd)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ fz_path *path;
+ fz_rect bbox;
+
+ path = csi->path;
+ csi->path = fz_newpath();
+
+ if (doclose)
+ fz_closepath(path);
+
+ if (dostroke)
+ bbox = fz_boundpath(path, &gstate->strokestate, gstate->ctm);
+ else
+ bbox = fz_boundpath(path, nil, gstate->ctm);
+
+ if (csi->clip)
+ {
+ gstate->clipdepth++;
+ csi->dev->clippath(csi->dev->user, path, evenodd, gstate->ctm);
+ csi->clip = 0;
+ }
+
+ pdf_begingroup(csi, bbox);
+
+ if (dofill)
+ {
+ switch (gstate->fill.kind)
+ {
+ case PDF_MNONE:
+ break;
+ case PDF_MCOLOR:
+ csi->dev->fillpath(csi->dev->user, path, evenodd, gstate->ctm,
+ gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
+ break;
+ case PDF_MPATTERN:
+ if (gstate->fill.pattern)
+ {
+ csi->dev->clippath(csi->dev->user, path, evenodd, gstate->ctm);
+ pdf_showpattern(csi, gstate->fill.pattern, bbox, PDF_MFILL);
+ csi->dev->popclip(csi->dev->user);
+ }
+ break;
+ case PDF_MSHADE:
+ if (gstate->fill.shade)
+ {
+ csi->dev->clippath(csi->dev->user, path, evenodd, gstate->ctm);
+ csi->dev->fillshade(csi->dev->user, gstate->fill.shade, csi->topctm, gstate->fill.alpha);
+ csi->dev->popclip(csi->dev->user);
+ }
+ break;
+ }
+ }
+
+ if (dostroke)
+ {
+ switch (gstate->stroke.kind)
+ {
+ case PDF_MNONE:
+ break;
+ case PDF_MCOLOR:
+ csi->dev->strokepath(csi->dev->user, path, &gstate->strokestate, gstate->ctm,
+ gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha);
+ break;
+ case PDF_MPATTERN:
+ if (gstate->stroke.pattern)
+ {
+ csi->dev->clipstrokepath(csi->dev->user, path, &gstate->strokestate, gstate->ctm);
+ pdf_showpattern(csi, gstate->stroke.pattern, bbox, PDF_MFILL);
+ csi->dev->popclip(csi->dev->user);
+ }
+ break;
+ case PDF_MSHADE:
+ if (gstate->stroke.shade)
+ {
+ csi->dev->clipstrokepath(csi->dev->user, path, &gstate->strokestate, gstate->ctm);
+ csi->dev->fillshade(csi->dev->user, gstate->stroke.shade, csi->topctm, gstate->stroke.alpha);
+ csi->dev->popclip(csi->dev->user);
+ }
+ break;
+ }
+ }
+
+ pdf_endgroup(csi);
+
+ fz_freepath(path);
+}
+
+void
+pdf_flushtext(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 = nil;
+
+ dofill = dostroke = doclip = doinvisible = 0;
+ switch (csi->textmode)
+ {
+ 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_boundtext(text, gstate->ctm);
+
+ pdf_begingroup(csi, bbox);
+
+ if (doinvisible)
+ csi->dev->ignoretext(csi->dev->user, text, gstate->ctm);
+
+ if (doclip)
+ {
+ if (csi->accumulate < 2)
+ gstate->clipdepth++;
+ csi->dev->cliptext(csi->dev->user, text, gstate->ctm, csi->accumulate);
+ csi->accumulate = 2;
+ }
+
+ if (dofill)
+ {
+ switch (gstate->fill.kind)
+ {
+ case PDF_MNONE:
+ break;
+ case PDF_MCOLOR:
+ csi->dev->filltext(csi->dev->user, text, gstate->ctm,
+ gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
+ break;
+ case PDF_MPATTERN:
+ if (gstate->fill.pattern)
+ {
+ csi->dev->cliptext(csi->dev->user, text, gstate->ctm, 0);
+ pdf_showpattern(csi, gstate->fill.pattern, bbox, PDF_MFILL);
+ csi->dev->popclip(csi->dev->user);
+ }
+ break;
+ case PDF_MSHADE:
+ if (gstate->fill.shade)
+ {
+ csi->dev->cliptext(csi->dev->user, text, gstate->ctm, 0);
+ csi->dev->fillshade(csi->dev->user, gstate->fill.shade, csi->topctm, gstate->fill.alpha);
+ csi->dev->popclip(csi->dev->user);
+ }
+ break;
+ }
+ }
+
+ if (dostroke)
+ {
+ switch (gstate->stroke.kind)
+ {
+ case PDF_MNONE:
+ break;
+ case PDF_MCOLOR:
+ csi->dev->stroketext(csi->dev->user, text, &gstate->strokestate, gstate->ctm,
+ gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha);
+ break;
+ case PDF_MPATTERN:
+ if (gstate->stroke.pattern)
+ {
+ csi->dev->clipstroketext(csi->dev->user, text, &gstate->strokestate, gstate->ctm);
+ pdf_showpattern(csi, gstate->stroke.pattern, bbox, PDF_MFILL);
+ csi->dev->popclip(csi->dev->user);
+ }
+ break;
+ case PDF_MSHADE:
+ if (gstate->stroke.shade)
+ {
+ csi->dev->clipstroketext(csi->dev->user, text, &gstate->strokestate, gstate->ctm);
+ csi->dev->fillshade(csi->dev->user, gstate->stroke.shade, csi->topctm, gstate->stroke.alpha);
+ csi->dev->popclip(csi->dev->user);
+ }
+ break;
+ }
+ }
+
+ pdf_endgroup(csi);
+
+ fz_freetext(text);
+}
+
+static void
+pdf_showglyph(pdf_csi *csi, int cid)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ pdf_fontdesc *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->tounicode)
+ ucslen = pdf_lookupcmapfull(fontdesc->tounicode, cid, ucsbuf);
+ if (ucslen == 0 && cid < fontdesc->ncidtoucs)
+ {
+ ucsbuf[0] = fontdesc->cidtoucs[cid];
+ ucslen = 1;
+ }
+ if (ucslen == 0 || (ucslen == 1 && ucsbuf[0] == 0))
+ {
+ ucsbuf[0] = '?';
+ ucslen = 1;
+ }
+
+ gid = pdf_fontcidtogid(fontdesc, cid);
+
+ if (fontdesc->wmode == 1)
+ {
+ v = pdf_getvmtx(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->textmode)
+ {
+ pdf_flushtext(csi);
+
+ csi->text = fz_newtext(fontdesc->font, trm, fontdesc->wmode);
+ csi->text->trm.e = 0;
+ csi->text->trm.f = 0;
+ csi->textmode = gstate->render;
+ }
+
+ /* add glyph to textobject */
+ fz_addtext(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_addtext(csi->text, -1, ucsbuf[i], trm.e, trm.f);
+
+ if (fontdesc->wmode == 0)
+ {
+ h = pdf_gethmtx(fontdesc, cid);
+ w0 = h.w * 0.001f;
+ tx = (w0 * gstate->size + gstate->charspace) * 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->charspace;
+ csi->tm = fz_concat(fz_translate(0, ty), csi->tm);
+ }
+}
+
+void
+pdf_showspace(pdf_csi *csi, float tadj)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ pdf_fontdesc *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);
+}
+
+void
+pdf_showstring(pdf_csi *csi, unsigned char *buf, int len)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ pdf_fontdesc *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_decodecmap(fontdesc->encoding, buf, &cpt);
+ cid = pdf_lookupcmap(fontdesc->encoding, cpt);
+ if (cid >= 0)
+ pdf_showglyph(csi, cid);
+ else
+ fz_warn("cannot encode character with code point %#x", cpt);
+ if (cpt == 32)
+ pdf_showspace(csi, gstate->wordspace);
+ }
+}
+
+void
+pdf_showtext(pdf_csi *csi, fz_obj *text)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ int i;
+
+ if (fz_isarray(text))
+ {
+ for (i = 0; i < fz_arraylen(text); i++)
+ {
+ fz_obj *item = fz_arrayget(text, i);
+ if (fz_isstring(item))
+ pdf_showstring(csi, (unsigned char *)fz_tostrbuf(item), fz_tostrlen(item));
+ else
+ pdf_showspace(csi, - fz_toreal(item) * gstate->size * 0.001f);
+ }
+ }
+ else if (fz_isstring(text))
+ {
+ pdf_showstring(csi, (unsigned char *)fz_tostrbuf(text), fz_tostrlen(text));
+ }
+}