2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
28 #include "../../config.h"
33 #ifdef HAVE_SYS_STAT_H
51 #include "OutputDev.h"
54 #include "CharCodeToUnicode.h"
55 #include "NameToUnicodeTable.h"
56 #include "GlobalParams.h"
57 #include "FoFiType1C.h"
58 #include "FoFiTrueType.h"
60 #include "GFXOutputDev.h"
62 // swftools header files
64 #include "../gfxdevice.h"
65 #include "../gfxtools.h"
66 #include "../gfxfont.h"
67 #include "../gfxpoly.h"
68 #include "../devices/record.h"
69 #include "../devices/ops.h"
70 #include "../devices/polyops.h"
71 #include "../devices/render.h"
73 #include "../art/libart.h"
80 typedef struct _fontfile
83 int len; // basename length
85 struct _fontfile*next;
90 static fontfile_t* global_fonts = 0;
91 static fontfile_t* global_fonts_next = 0;
93 static int fontnum = 0;
97 static char* lastfontdir = 0;
108 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
109 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
110 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
111 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
112 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
113 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
114 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
115 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
116 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
117 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
118 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
119 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
120 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
121 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
124 static int verbose = 0;
125 static int dbgindent = 0;
126 static void dbg(const char*format, ...)
133 va_start(arglist, format);
134 vsprintf(buf, format, arglist);
137 while(l && buf[l-1]=='\n') {
142 int indent = dbgindent;
152 typedef struct _feature
155 struct _feature*next;
157 feature_t*featurewarnings = 0;
159 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
161 feature_t*f = featurewarnings;
163 if(!strcmp(feature, f->string))
167 f = (feature_t*)malloc(sizeof(feature_t));
168 f->string = strdup(feature);
169 f->next = featurewarnings;
172 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
173 if(this->config_break_on_warning) {
174 msg("<fatal> Aborting conversion due to unsupported feature");
178 msg("<notice> File contains %s",feature);
181 void GFXOutputDev::warnfeature(const char*feature,char fully)
183 showfeature(feature,fully,1);
185 void GFXOutputDev::infofeature(const char*feature)
187 showfeature(feature,0,0);
190 GFXOutputState::GFXOutputState() {
192 this->createsoftmask = 0;
193 this->transparencygroup = 0;
195 this->grouprecording = 0;
199 GBool GFXOutputDev::interpretType3Chars()
204 typedef struct _drawnchar
222 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
223 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
228 free(chars);chars = 0;
235 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
239 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
242 chars[num_chars].x = x;
243 chars[num_chars].y = y;
244 chars[num_chars].color = color;
245 chars[num_chars].charid = charid;
249 char* writeOutStdFont(fontentry* f)
254 char* tmpFileName = mktmpname(namebuf1);
256 sprintf(namebuf2, "%s.afm", tmpFileName);
257 fi = fopen(namebuf2, "wb");
260 fwrite(f->afm, 1, f->afmlen, fi);
263 sprintf(namebuf2, "%s.pfb", tmpFileName);
264 fi = fopen(namebuf2, "wb");
267 fwrite(f->pfb, 1, f->pfblen, fi);
269 return strdup(namebuf2);
271 void unlinkfont(char* filename)
276 msg("<verbose> Removing temporary font file %s", filename);
279 if(!strncmp(&filename[l-4],".afm",4)) {
280 memcpy(&filename[l-4],".pfb",4); unlink(filename);
281 memcpy(&filename[l-4],".pfa",4); unlink(filename);
282 memcpy(&filename[l-4],".afm",4);
285 if(!strncmp(&filename[l-4],".pfa",4)) {
286 memcpy(&filename[l-4],".afm",4); unlink(filename);
287 memcpy(&filename[l-4],".pfa",4);
290 if(!strncmp(&filename[l-4],".pfb",4)) {
291 memcpy(&filename[l-4],".afm",4); unlink(filename);
292 memcpy(&filename[l-4],".pfb",4);
298 GFXGlobalParams::GFXGlobalParams()
301 //setupBaseFonts(char *dir); //not tested yet
303 GFXGlobalParams::~GFXGlobalParams()
305 msg("<verbose> Performing cleanups");
307 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
308 if(pdf2t1map[t].fullfilename) {
309 unlinkfont(pdf2t1map[t].fullfilename);
313 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
315 msg("<verbose> looking for font %s in global params", fontName->getCString());
317 char*name = fontName->getCString();
318 /* see if it is a pdf standard font */
320 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
321 if(!strcmp(name, pdf2t1map[t].pdffont)) {
322 if(!pdf2t1map[t].fullfilename) {
323 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
324 if(!pdf2t1map[t].fullfilename) {
325 msg("<error> Couldn't save default font- is the Temp Directory writable?");
327 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
330 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
331 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
336 int bestlen = 0x7fffffff;
337 const char*bestfilename = 0;
339 fontfile_t*f = global_fonts;
341 if(strstr(f->filename, name)) {
342 if(f->len < bestlen) {
344 bestfilename = f->filename;
350 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
351 dfp->t1.fileName = new GString(bestfilename);
354 return GlobalParams::getDisplayFont(fontName);
357 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
361 this->xref = doc->getXRef();
364 this->textmodeinfo = 0;
367 this->type3active = 0;
370 this->substitutepos = 0;
371 this->type3Warning = 0;
372 this->user_movex = 0;
373 this->user_movey = 0;
376 this->user_clipx1 = 0;
377 this->user_clipy1 = 0;
378 this->user_clipx2 = 0;
379 this->user_clipy2 = 0;
380 this->current_text_stroke = 0;
381 this->current_text_clip = 0;
382 this->outer_clip_box = 0;
384 this->pagebuflen = 0;
386 this->config_convertgradients=0;
387 this->config_break_on_warning=0;
388 this->config_remapunicode=0;
389 this->config_transparent=0;
390 this->config_extrafontdata = 0;
391 this->config_fontquality = 10;
392 this->config_optimize_polygons = 0;
394 this->gfxfontlist = gfxfontlist_create();
396 memset(states, 0, sizeof(states));
399 void GFXOutputDev::setParameter(const char*key, const char*value)
401 if(!strcmp(key,"breakonwarning")) {
402 this->config_break_on_warning = atoi(value);
403 } else if(!strcmp(key,"remapunicode")) {
404 this->config_remapunicode = atoi(value);
405 } else if(!strcmp(key,"transparent")) {
406 this->config_transparent = atoi(value);
407 } else if(!strcmp(key,"extrafontdata")) {
408 this->config_extrafontdata = atoi(value);
409 } else if(!strcmp(key,"convertgradients")) {
410 this->config_convertgradients = atoi(value);
411 } else if(!strcmp(key,"optimize_polygons")) {
412 this->config_optimize_polygons = atoi(value);
413 } else if(!strcmp(key,"fontquality")) {
414 this->config_fontquality = atof(value);
415 if(this->config_fontquality<=1)
416 this->config_fontquality=1;
417 } else if(!strcmp(key,"help")) {
418 printf("\nPDF layer options:\n");
419 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
420 printf(" are not 100%% supported\n");
421 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
422 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
423 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
428 void GFXOutputDev::setDevice(gfxdevice_t*dev)
433 void GFXOutputDev::setMove(int x,int y)
435 this->user_movex = x;
436 this->user_movey = y;
439 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
441 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
442 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
444 this->user_clipx1 = x1;
445 this->user_clipy1 = y1;
446 this->user_clipx2 = x2;
447 this->user_clipy2 = y2;
450 static char*getFontName(GfxFont*font)
453 GString*gstr = font->getName();
454 char* fname = gstr==0?0:gstr->getCString();
458 sprintf(buf, "UFONT%d", r->num);
459 fontid = strdup(buf);
461 fontid = strdup(fname);
465 char* plus = strchr(fontid, '+');
466 if(plus && plus < &fontid[strlen(fontid)-1]) {
467 fontname = strdup(plus+1);
469 fontname = strdup(fontid);
475 static void dumpFontInfo(const char*loglevel, GfxFont*font);
476 static int lastdumps[1024];
477 static int lastdumppos = 0;
482 static void showFontError(GfxFont*font, int nr)
486 for(t=0;t<lastdumppos;t++)
487 if(lastdumps[t] == r->num)
491 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
492 lastdumps[lastdumppos++] = r->num;
494 msg("<warning> The following font caused problems:");
496 msg("<warning> The following font caused problems (substituting):");
498 msg("<warning> The following Type 3 Font will be rendered as graphics:");
499 dumpFontInfo("<warning>", font);
502 static void dumpFontInfo(const char*loglevel, GfxFont*font)
504 char* id = getFontID(font);
505 char* name = getFontName(font);
506 Ref* r=font->getID();
507 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
509 GString*gstr = font->getTag();
511 msg("%s| Tag: %s", loglevel, id);
513 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
515 GfxFontType type=font->getType();
517 case fontUnknownType:
518 msg("%s| Type: unknown",loglevel);
521 msg("%s| Type: 1",loglevel);
524 msg("%s| Type: 1C",loglevel);
527 msg("%s| Type: 3",loglevel);
530 msg("%s| Type: TrueType",loglevel);
533 msg("%s| Type: CIDType0",loglevel);
536 msg("%s| Type: CIDType0C",loglevel);
539 msg("%s| Type: CIDType2",loglevel);
544 GBool embedded = font->getEmbeddedFontID(&embRef);
546 if(font->getEmbeddedFontName()) {
547 embeddedName = font->getEmbeddedFontName()->getCString();
550 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
552 gstr = font->getExtFontFile();
554 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
556 // Get font descriptor flags.
557 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
558 if(font->isSerif()) msg("%s| is serif", loglevel);
559 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
560 if(font->isItalic()) msg("%s| is italic", loglevel);
561 if(font->isBold()) msg("%s| is bold", loglevel);
567 //void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
568 //void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
570 void dump_outline(gfxline_t*line)
573 if(line->type == gfx_moveTo) {
574 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
575 } else if(line->type == gfx_lineTo) {
576 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
577 } else if(line->type == gfx_splineTo) {
578 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
584 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
586 int num = path->getNumSubpaths();
589 double lastx=0,lasty=0,posx=0,posy=0;
592 msg("<warning> empty path");
596 gfxdrawer_target_gfxline(&draw);
598 for(t = 0; t < num; t++) {
599 GfxSubpath *subpath = path->getSubpath(t);
600 int subnum = subpath->getNumPoints();
601 double bx=0,by=0,cx=0,cy=0;
603 for(s=0;s<subnum;s++) {
606 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
609 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
610 draw.lineTo(&draw, lastx, lasty);
612 draw.moveTo(&draw, x,y);
617 } else if(subpath->getCurve(s) && cpos==0) {
621 } else if(subpath->getCurve(s) && cpos==1) {
629 draw.lineTo(&draw, x,y);
631 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
638 /* fix non-closed lines */
639 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
640 draw.lineTo(&draw, lastx, lasty);
642 gfxline_t*result = (gfxline_t*)draw.result(&draw);
644 gfxline_optimize(result);
649 GBool GFXOutputDev::useTilingPatternFill()
651 infofeature("tiled patterns");
652 // if(config_convertgradients)
656 GBool GFXOutputDev::useShadedFills()
658 infofeature("shaded fills");
659 if(config_convertgradients)
664 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
666 state->transform(x,y,nx,ny);
667 *nx += user_movex + clipmovex;
668 *ny += user_movey + clipmovey;
672 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
673 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
674 int paintType, Dict *resDict,
675 double *mat, double *bbox,
676 int x0, int y0, int x1, int y1,
677 double xStep, double yStep)
679 void GFXBitmapOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
680 int paintType, Dict *resDict,
681 double *mat, double *bbox,
682 int x0, int y0, int x1, int y1,
683 double xStep, double yStep)
686 msg("<debug> tilingPatternFill");
687 infofeature("tiling pattern fills");
690 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
692 msg("<error> functionShadedFill not supported yet");
693 infofeature("function shaded fills");
696 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
700 colspace->getRGB(col, &rgb);
701 c.r = colToByte(rgb.r);
702 c.g = colToByte(rgb.g);
703 c.b = colToByte(rgb.b);
708 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
710 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
711 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
714 this->transformXY(state, x0,y0, &x0,&y0);
715 this->transformXY(state, x1,y1, &x1,&y1);
716 this->transformXY(state, x2,y2, &x2,&y2);
721 shading->getColor(0.0, &color0);
722 shading->getColor(0.5, &color1);
723 shading->getColor(1.0, &color2);
725 GfxColorSpace* colspace = shading->getColorSpace();
727 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
728 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
729 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
730 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
731 infofeature("radial shaded fills");
733 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
737 g[0].color = col2col(colspace, &color0);
738 g[1].color = col2col(colspace, &color1);
739 g[2].color = col2col(colspace, &color2);
744 gfxbbox_t b = states[statepos].clipbbox;
745 gfxline_t p1,p2,p3,p4,p5;
746 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
747 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
748 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
749 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
750 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
753 //m.m00 = (x3-x0); m.m10 = (x1-x0);
754 //m.m01 = (y3-y0); m.m11 = (y1-y0);
755 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
756 m.m00 = (x1-x0); m.m10 = (x2-x0);
757 m.m01 = (y1-y0); m.m11 = (y2-y0);
761 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
765 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
768 shading->getCoords(&x0,&y0,&x1,&y1);
769 this->transformXY(state, x0,y0,&x0,&y0);
770 this->transformXY(state, x1,y1,&x1,&y1);
775 shading->getColor(0.0, &color0);
776 shading->getColor(0.5, &color1);
777 shading->getColor(1.0, &color2);
779 GfxColorSpace* colspace = shading->getColorSpace();
781 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
782 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
783 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
784 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
786 infofeature("axial shaded fills");
788 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
792 g[0].color = col2col(colspace, &color0);
793 g[1].color = col2col(colspace, &color1);
794 g[2].color = col2col(colspace, &color2);
799 double xMin,yMin,xMax,yMax;
800 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
801 this->transformXY(state, xMin, yMin, &xMin, &yMin);
802 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
805 xMin = 1024; yMin = 1024;
807 gfxbbox_t b = states[statepos].clipbbox;
808 gfxline_t p1,p2,p3,p4,p5;
809 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
810 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
811 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
812 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
813 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
815 /* the gradient starts at (-1.0,0.0), so move (0,0) to
816 the middle of the two control points */
818 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
819 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
820 m.tx = (x0 + x1)/2 - 0.5;
821 m.ty = (y0 + y1)/2 - 0.5;
823 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
827 GBool GFXOutputDev::useDrawForm()
829 infofeature("forms");
832 void GFXOutputDev::drawForm(Ref id)
834 msg("<error> drawForm not implemented");
836 GBool GFXOutputDev::needNonText()
840 void GFXOutputDev::endPage()
842 msg("<verbose> endPage (GfxOutputDev)");
844 device->endclip(device);
849 #define STROKE_FILL 1
850 #define STROKE_CLIP 2
851 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
853 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
854 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
855 double miterLimit = state->getMiterLimit();
856 double width = state->getTransformedLineWidth();
859 double opaq = state->getStrokeOpacity();
861 state->getFillRGB(&rgb);
863 state->getStrokeRGB(&rgb);
865 col.r = colToByte(rgb.r);
866 col.g = colToByte(rgb.g);
867 col.b = colToByte(rgb.b);
868 col.a = (unsigned char)(opaq*255);
870 gfx_capType capType = gfx_capRound;
871 if(lineCap == 0) capType = gfx_capButt;
872 else if(lineCap == 1) capType = gfx_capRound;
873 else if(lineCap == 2) capType = gfx_capSquare;
875 gfx_joinType joinType = gfx_joinRound;
876 if(lineJoin == 0) joinType = gfx_joinMiter;
877 else if(lineJoin == 1) joinType = gfx_joinRound;
878 else if(lineJoin == 2) joinType = gfx_joinBevel;
881 double dashphase = 0;
883 state->getLineDash(&ldash, &dashnum, &dashphase);
887 if(dashnum && ldash) {
888 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
890 msg("<trace> %d dashes", dashnum);
891 msg("<trace> | phase: %f", dashphase);
892 for(t=0;t<dashnum;t++) {
893 dash[t] = (float)ldash[t];
894 msg("<trace> | d%-3d: %f", t, ldash[t]);
897 if(getLogLevel() >= LOGLEVEL_TRACE) {
901 line2 = gfxtool_dash_line(line, dash, (float)dashphase);
904 msg("<trace> After dashing:");
907 if(getLogLevel() >= LOGLEVEL_TRACE) {
908 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
910 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
911 lineCap==0?"butt": (lineJoin==1?"round":"square"),
913 col.r,col.g,col.b,col.a
918 if(flags&STROKE_FILL) {
919 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
920 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
921 if(getLogLevel() >= LOGLEVEL_TRACE) {
922 dump_outline(gfxline);
925 msg("<warning> Empty polygon (resulting from stroked line)");
927 if(flags&STROKE_CLIP) {
928 device->startclip(device, gfxline);
929 states[statepos].clipping++;
931 device->fill(device, gfxline, &col);
936 if(flags&STROKE_CLIP)
937 msg("<error> Stroke&clip not supported at the same time");
938 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
945 gfxcolor_t getFillColor(GfxState * state)
948 double opaq = state->getFillOpacity();
949 state->getFillRGB(&rgb);
951 col.r = colToByte(rgb.r);
952 col.g = colToByte(rgb.g);
953 col.b = colToByte(rgb.b);
954 col.a = (unsigned char)(opaq*255);
958 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
960 gfxcolor_t col = getFillColor(state);
962 if(getLogLevel() >= LOGLEVEL_TRACE) {
963 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
966 device->fill(device, line, &col);
969 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
971 if(getLogLevel() >= LOGLEVEL_TRACE) {
974 gfxbbox_t bbox = gfxline_getbbox(line);
975 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
977 device->startclip(device, line);
978 states[statepos].clipping++;
981 void GFXOutputDev::clip(GfxState *state)
983 GfxPath * path = state->getPath();
985 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
986 if(config_optimize_polygons) {
987 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
991 clipToGfxLine(state, line);
995 void GFXOutputDev::eoClip(GfxState *state)
997 GfxPath * path = state->getPath();
998 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
999 clipToGfxLine(state, line);
1002 void GFXOutputDev::clipToStrokePath(GfxState *state)
1004 GfxPath * path = state->getPath();
1005 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1007 if(getLogLevel() >= LOGLEVEL_TRACE) {
1008 msg("<trace> cliptostrokepath");
1012 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1016 void GFXOutputDev::finish()
1018 if(outer_clip_box) {
1020 device->endclip(device);
1026 GFXOutputDev::~GFXOutputDev()
1031 free(this->pages); this->pages = 0;
1034 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1036 GBool GFXOutputDev::upsideDown()
1040 GBool GFXOutputDev::useDrawChar()
1045 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1046 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1048 static char tmp_printstr[4096];
1049 char* makeStringPrintable(char*str)
1051 int len = strlen(str);
1058 for(t=0;t<len;t++) {
1063 tmp_printstr[t] = c;
1066 tmp_printstr[len++] = '.';
1067 tmp_printstr[len++] = '.';
1068 tmp_printstr[len++] = '.';
1070 tmp_printstr[len] = 0;
1071 return tmp_printstr;
1073 #define INTERNAL_FONT_SIZE 1024.0
1074 void GFXOutputDev::updateFontMatrix(GfxState*state)
1076 double* ctm = state->getCTM();
1077 double fontSize = state->getFontSize();
1078 double*textMat = state->getTextMat();
1080 /* taking the absolute value of horizScaling seems to be required for
1081 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1082 double hscale = fabs(state->getHorizScaling());
1084 // from xpdf-3.02/SplashOutputDev:updateFont
1085 double mm11 = textMat[0] * fontSize * hscale;
1086 double mm12 = textMat[1] * fontSize * hscale;
1087 double mm21 = textMat[2] * fontSize;
1088 double mm22 = textMat[3] * fontSize;
1090 // multiply with ctm, like state->getFontTransMat() does
1091 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1092 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1093 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1094 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1095 this->current_font_matrix.tx = 0;
1096 this->current_font_matrix.ty = 0;
1099 void GFXOutputDev::beginString(GfxState *state, GString *s)
1101 int render = state->getRender();
1102 if(current_text_stroke) {
1103 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1106 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1109 static gfxline_t* mkEmptyGfxShape(double x, double y)
1111 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1112 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1116 static char isValidUnicode(int c)
1118 if(c>=32 && c<0x2fffe)
1123 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1124 double dx, double dy,
1125 double originX, double originY,
1126 CharCode charid, int nBytes, Unicode *_u, int uLen)
1128 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1129 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1133 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1135 int render = state->getRender();
1136 gfxcolor_t col = getFillColor(state);
1138 // check for invisible text -- this is used by Acrobat Capture
1139 if (render == RENDER_INVISIBLE) {
1141 if(!config_extrafontdata)
1145 GfxFont*font = state->getFont();
1147 if(font->getType() == fontType3) {
1148 /* type 3 chars are passed as graphics */
1149 msg("<debug> type3 char at %f/%f", x, y);
1153 Unicode u = uLen?(_u[0]):0;
1154 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d font=%08x",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
1156 gfxmatrix_t m = this->current_font_matrix;
1157 this->transformXY(state, x, y, &m.tx, &m.ty);
1159 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1160 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1162 msg("<debug> Drawing glyph %d as shape", charid);
1164 msg("<notice> Some texts will be rendered as shape");
1167 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1168 gfxline_t*tglyph = gfxline_clone(glyph);
1169 gfxline_transform(tglyph, &m);
1170 if((render&3) != RENDER_INVISIBLE) {
1171 gfxline_t*add = gfxline_clone(tglyph);
1172 current_text_stroke = gfxline_append(current_text_stroke, add);
1174 if(render&RENDER_CLIP) {
1175 gfxline_t*add = gfxline_clone(tglyph);
1176 current_text_clip = gfxline_append(current_text_clip, add);
1177 if(!current_text_clip) {
1178 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1181 gfxline_free(tglyph);
1185 void GFXOutputDev::endString(GfxState *state)
1187 int render = state->getRender();
1188 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1190 if(current_text_stroke) {
1191 /* fillstroke and stroke text rendering objects we can process right
1192 now (as there may be texts of other rendering modes in this
1193 text object)- clipping objects have to wait until endTextObject,
1195 device->setparameter(device, "mark","TXT");
1196 if((render&3) == RENDER_FILL) {
1197 fillGfxLine(state, current_text_stroke);
1198 gfxline_free(current_text_stroke);
1199 current_text_stroke = 0;
1200 } else if((render&3) == RENDER_FILLSTROKE) {
1201 fillGfxLine(state, current_text_stroke);
1202 strokeGfxline(state, current_text_stroke,0);
1203 gfxline_free(current_text_stroke);
1204 current_text_stroke = 0;
1205 } else if((render&3) == RENDER_STROKE) {
1206 strokeGfxline(state, current_text_stroke,0);
1207 gfxline_free(current_text_stroke);
1208 current_text_stroke = 0;
1210 device->setparameter(device, "mark","");
1214 void GFXOutputDev::endTextObject(GfxState *state)
1216 int render = state->getRender();
1217 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1219 if(current_text_clip) {
1220 device->setparameter(device, "mark","TXT");
1221 clipToGfxLine(state, current_text_clip);
1222 device->setparameter(device, "mark","");
1223 gfxline_free(current_text_clip);
1224 current_text_clip = 0;
1228 /* the logic seems to be as following:
1229 first, beginType3Char is called, with the charcode and the coordinates.
1230 if this function returns true, it already knew about the char and has now drawn it.
1231 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1232 called with some parameters.
1233 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1234 at the position first passed to beginType3Char). the char ends with endType3Char.
1236 The drawing operations between beginType3Char and endType3Char are somewhat different to
1237 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1238 color determines the color of a font)
1241 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1243 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1246 if(config_extrafontdata && current_fontinfo) {
1248 gfxmatrix_t m = this->current_font_matrix;
1249 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1250 m.m00*=INTERNAL_FONT_SIZE;
1251 m.m01*=INTERNAL_FONT_SIZE;
1252 m.m10*=INTERNAL_FONT_SIZE;
1253 m.m11*=INTERNAL_FONT_SIZE;
1255 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1256 msg("<error> Invalid charid %d for font", charid);
1259 gfxcolor_t col={0,0,0,0};
1260 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1261 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1265 /* the character itself is going to be passed using the draw functions */
1266 return gFalse; /* gTrue= is_in_cache? */
1269 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1271 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1274 void GFXOutputDev::endType3Char(GfxState *state)
1277 msg("<debug> endType3Char");
1280 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1282 this->currentpage = pageNum;
1284 int rot = doc->getPageRotate(1);
1285 gfxcolor_t white = {255,255,255,255};
1286 gfxcolor_t black = {255,0,0,0};
1288 gfxline_t clippath[5];
1290 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1291 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1292 Use CropBox, not MediaBox, as page size
1299 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1300 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1302 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1303 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1305 this->clipmovex = -(int)x1;
1306 this->clipmovey = -(int)y1;
1308 /* apply user clip box */
1309 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1310 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1311 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1312 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1313 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1314 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1317 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1319 msg("<notice> processing PDF page %d (%dx%d:%d:%d) (move:%d:%d)", pageNum, (int)x2-(int)x1,(int)y2-(int)y1, (int)x1, (int)y1, user_movex + clipmovex, user_movey + clipmovey);
1321 msg("<verbose> page is rotated %d degrees", rot);
1323 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1324 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1325 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1326 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1327 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1328 device->startclip(device, clippath); outer_clip_box = 1;
1329 if(!config_transparent) {
1330 device->fill(device, clippath, &white);
1332 states[statepos].clipbbox.xmin = x1;
1333 states[statepos].clipbbox.ymin = x1;
1334 states[statepos].clipbbox.xmax = x2;
1335 states[statepos].clipbbox.ymax = y2;
1339 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1341 double x1, y1, x2, y2;
1342 gfxline_t points[5];
1345 msg("<debug> drawlink");
1347 link->getRect(&x1, &y1, &x2, &y2);
1348 cvtUserToDev(x1, y1, &x, &y);
1349 points[0].type = gfx_moveTo;
1350 points[0].x = points[4].x = x + user_movex + clipmovex;
1351 points[0].y = points[4].y = y + user_movey + clipmovey;
1352 points[0].next = &points[1];
1353 cvtUserToDev(x2, y1, &x, &y);
1354 points[1].type = gfx_lineTo;
1355 points[1].x = x + user_movex + clipmovex;
1356 points[1].y = y + user_movey + clipmovey;
1357 points[1].next = &points[2];
1358 cvtUserToDev(x2, y2, &x, &y);
1359 points[2].type = gfx_lineTo;
1360 points[2].x = x + user_movex + clipmovex;
1361 points[2].y = y + user_movey + clipmovey;
1362 points[2].next = &points[3];
1363 cvtUserToDev(x1, y2, &x, &y);
1364 points[3].type = gfx_lineTo;
1365 points[3].x = x + user_movex + clipmovex;
1366 points[3].y = y + user_movey + clipmovey;
1367 points[3].next = &points[4];
1368 cvtUserToDev(x1, y1, &x, &y);
1369 points[4].type = gfx_lineTo;
1370 points[4].x = x + user_movex + clipmovex;
1371 points[4].y = y + user_movey + clipmovey;
1374 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1375 points[0].x, points[0].y,
1376 points[1].x, points[1].y,
1377 points[2].x, points[2].y,
1378 points[3].x, points[3].y);
1380 if(getLogLevel() >= LOGLEVEL_TRACE) {
1381 dump_outline(points);
1384 LinkAction*action=link->getAction();
1387 const char*type = "-?-";
1390 msg("<trace> drawlink action=%d", action->getKind());
1391 switch(action->getKind())
1395 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1396 LinkDest *dest=NULL;
1397 if (ha->getDest()==NULL)
1398 dest=catalog->findDest(ha->getNamedDest());
1399 else dest=ha->getDest();
1401 if (dest->isPageRef()){
1402 Ref pageref=dest->getPageRef();
1403 page=catalog->findPage(pageref.num,pageref.gen);
1405 else page=dest->getPageNum();
1406 sprintf(buf, "%d", page);
1413 LinkGoToR*l = (LinkGoToR*)action;
1414 GString*g = l->getFileName();
1416 s = strdup(g->getCString());
1418 /* if the GoToR link has no filename, then
1419 try to find a refernce in the *local*
1421 GString*g = l->getNamedDest();
1423 s = strdup(g->getCString());
1429 LinkNamed*l = (LinkNamed*)action;
1430 GString*name = l->getName();
1432 s = strdup(name->lowerCase()->getCString());
1433 named = name->getCString();
1436 if(strstr(s, "next") || strstr(s, "forward"))
1438 page = currentpage + 1;
1440 else if(strstr(s, "prev") || strstr(s, "back"))
1442 page = currentpage - 1;
1444 else if(strstr(s, "last") || strstr(s, "end"))
1446 if(pages && pagepos>0)
1447 page = pages[pagepos-1];
1449 else if(strstr(s, "first") || strstr(s, "top"))
1457 case actionLaunch: {
1459 LinkLaunch*l = (LinkLaunch*)action;
1460 GString * str = new GString(l->getFileName());
1461 GString * params = l->getParams();
1463 str->append(params);
1464 s = strdup(str->getCString());
1471 LinkURI*l = (LinkURI*)action;
1472 GString*g = l->getURI();
1474 url = g->getCString();
1479 case actionUnknown: {
1481 LinkUnknown*l = (LinkUnknown*)action;
1486 msg("<error> Unknown link type!");
1491 if(!s) s = strdup("-?-");
1493 msg("<trace> drawlink s=%s", s);
1495 if(!linkinfo && (page || s))
1497 msg("<notice> File contains links");
1505 for(t=1;t<=pagepos;t++) {
1506 if(pages[t]==page) {
1515 sprintf(buf, "page%d", lpage);
1516 device->drawlink(device, points, buf);
1520 device->drawlink(device, points, s);
1523 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1527 void GFXOutputDev::saveState(GfxState *state) {
1528 dbg("saveState");dbgindent+=2;
1530 msg("<trace> saveState");
1533 msg("<error> Too many nested states in pdf.");
1537 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1538 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1539 states[statepos].clipping = 0;
1540 states[statepos].clipbbox = states[statepos-1].clipbbox;
1543 void GFXOutputDev::restoreState(GfxState *state) {
1544 dbgindent-=2; dbg("restoreState");
1547 msg("<error> Invalid restoreState");
1550 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1551 states[statepos].clipping?" (end clipping)":"");
1552 if(states[statepos].softmask) {
1553 clearSoftMask(state);
1556 while(states[statepos].clipping) {
1557 device->endclip(device);
1558 states[statepos].clipping--;
1563 void GFXOutputDev::updateLineWidth(GfxState *state)
1565 double width = state->getTransformedLineWidth();
1566 //swfoutput_setlinewidth(&device, width);
1569 void GFXOutputDev::updateLineCap(GfxState *state)
1571 int c = state->getLineCap();
1574 void GFXOutputDev::updateLineJoin(GfxState *state)
1576 int j = state->getLineJoin();
1579 void GFXOutputDev::updateFillColor(GfxState *state)
1582 double opaq = state->getFillOpacity();
1583 state->getFillRGB(&rgb);
1585 void GFXOutputDev::updateFillOpacity(GfxState *state)
1588 double opaq = state->getFillOpacity();
1589 state->getFillRGB(&rgb);
1590 dbg("update fillopaq %f", opaq);
1592 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1594 double opaq = state->getFillOpacity();
1595 dbg("update strokeopaq %f", opaq);
1597 void GFXOutputDev::updateFillOverprint(GfxState *state)
1599 double opaq = state->getFillOverprint();
1600 dbg("update filloverprint %f", opaq);
1602 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1604 double opaq = state->getStrokeOverprint();
1605 dbg("update strokeoverprint %f", opaq);
1607 void GFXOutputDev::updateTransfer(GfxState *state)
1609 dbg("update transfer");
1613 void GFXOutputDev::updateStrokeColor(GfxState *state)
1616 double opaq = state->getStrokeOpacity();
1617 state->getStrokeRGB(&rgb);
1621 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1623 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1624 memset(font, 0, sizeof(gfxfont_t));
1626 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1627 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1628 font->id = strdup(getFontID(xpdffont));
1631 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1633 //printf("%d glyphs\n", font->num_glyphs);
1634 font->num_glyphs = 0;
1635 for(t=0;t<src->num_glyphs;t++) {
1636 if(src->glyphs[t]) {
1637 SplashPath*path = src->glyphs[t]->path;
1638 int len = path?path->getLength():0;
1639 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1640 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1641 src->glyphs[t]->glyphid = font->num_glyphs;
1642 glyph->unicode = src->glyphs[t]->unicode;
1643 if(glyph->unicode >= font->max_unicode)
1644 font->max_unicode = glyph->unicode+1;
1646 gfxdrawer_target_gfxline(&drawer);
1650 for(s=0;s<len;s++) {
1653 path->getPoint(s, &x, &y, &f);
1656 if(f&splashPathFirst) {
1657 drawer.moveTo(&drawer, x*scale, y*scale);
1659 if(f&splashPathCurve) {
1661 path->getPoint(++s, &x2, &y2, &f);
1662 if(f&splashPathCurve) {
1664 path->getPoint(++s, &x3, &y3, &f);
1665 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1667 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1670 drawer.lineTo(&drawer, x*scale, y*scale);
1672 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1673 // (f&splashPathFirst)?"first":"",
1674 // (f&splashPathLast)?"last":"");
1676 glyph->line = (gfxline_t*)drawer.result(&drawer);
1677 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1681 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1682 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1683 for(t=0;t<font->num_glyphs;t++) {
1684 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1685 font->unicode2glyph[font->glyphs[t].unicode] = t;
1689 msg("<trace> %d glyphs.", t, font->num_glyphs);
1693 void GFXOutputDev::updateFont(GfxState *state)
1695 GfxFont* gfxFont = state->getFont();
1699 char*id = getFontID(gfxFont);
1700 msg("<verbose> Updating font to %s", id);
1701 if(gfxFont->getType() == fontType3) {
1702 infofeature("Type3 fonts");
1703 if(!config_extrafontdata) {
1708 msg("<error> Internal Error: FontID is null");
1712 this->current_fontinfo = this->info->getFont(id);
1713 if(!this->current_fontinfo) {
1714 msg("<error> Internal Error: no fontinfo for font %s", id);
1717 if(!this->current_fontinfo->seen) {
1718 dumpFontInfo("<verbose>", gfxFont);
1721 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1723 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1724 font->id = strdup(id);
1725 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1727 device->addfont(device, font);
1729 current_gfxfont = font;
1732 updateFontMatrix(state);
1735 #define SQR(x) ((x)*(x))
1737 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1739 if((newwidth<2 || newheight<2) ||
1740 (width<=newwidth || height<=newheight))
1742 unsigned char*newdata;
1744 newdata= (unsigned char*)malloc(newwidth*newheight);
1745 double fx = (double)(width)/newwidth;
1746 double fy = (double)(height)/newheight;
1748 int blocksize = (int)(8192/(fx*fy));
1749 int r = 8192*256/palettesize;
1750 for(x=0;x<newwidth;x++) {
1751 double ex = px + fx;
1752 int fromx = (int)px;
1754 int xweight1 = (int)(((fromx+1)-px)*256);
1755 int xweight2 = (int)((ex-tox)*256);
1757 for(y=0;y<newheight;y++) {
1758 double ey = py + fy;
1759 int fromy = (int)py;
1761 int yweight1 = (int)(((fromy+1)-py)*256);
1762 int yweight2 = (int)((ey-toy)*256);
1765 for(xx=fromx;xx<=tox;xx++)
1766 for(yy=fromy;yy<=toy;yy++) {
1767 int b = 1-data[width*yy+xx];
1769 if(xx==fromx) weight = (weight*xweight1)/256;
1770 if(xx==tox) weight = (weight*xweight2)/256;
1771 if(yy==fromy) weight = (weight*yweight1)/256;
1772 if(yy==toy) weight = (weight*yweight2)/256;
1775 //if(a) a=(palettesize-1)*r/blocksize;
1776 newdata[y*newwidth+x] = (a*blocksize)/r;
1784 #define IMAGE_TYPE_JPEG 0
1785 #define IMAGE_TYPE_LOSSLESS 1
1787 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1788 double x1,double y1,
1789 double x2,double y2,
1790 double x3,double y3,
1791 double x4,double y4, int type)
1793 gfxcolor_t*newpic=0;
1795 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1796 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1798 gfxline_t p1,p2,p3,p4,p5;
1799 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1800 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1801 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1802 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1803 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1805 {p1.x = (int)(p1.x*20)/20.0;
1806 p1.y = (int)(p1.y*20)/20.0;
1807 p2.x = (int)(p2.x*20)/20.0;
1808 p2.y = (int)(p2.y*20)/20.0;
1809 p3.x = (int)(p3.x*20)/20.0;
1810 p3.y = (int)(p3.y*20)/20.0;
1811 p4.x = (int)(p4.x*20)/20.0;
1812 p4.y = (int)(p4.y*20)/20.0;
1813 p5.x = (int)(p5.x*20)/20.0;
1814 p5.y = (int)(p5.y*20)/20.0;
1818 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1819 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1824 img.data = (gfxcolor_t*)data;
1828 if(type == IMAGE_TYPE_JPEG)
1829 /* TODO: pass image_dpi to device instead */
1830 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1832 dev->fillbitmap(dev, &p1, &img, &m, 0);
1835 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1836 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1838 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1841 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
1842 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1844 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1848 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1849 int width, int height, GfxImageColorMap*colorMap, GBool invert,
1850 GBool inlineImg, int mask, int*maskColors,
1851 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1853 double x1,y1,x2,y2,x3,y3,x4,y4;
1854 ImageStream *imgStr;
1859 unsigned char* maskbitmap = 0;
1862 ncomps = colorMap->getNumPixelComps();
1863 bits = colorMap->getBits();
1868 unsigned char buf[8];
1869 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1871 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1872 imgMaskStr->reset();
1873 unsigned char pal[256];
1874 int n = 1 << colorMap->getBits();
1879 maskColorMap->getGray(pixBuf, &gray);
1880 pal[t] = colToByte(gray);
1882 for (y = 0; y < maskHeight; y++) {
1883 for (x = 0; x < maskWidth; x++) {
1884 imgMaskStr->getPixel(buf);
1885 maskbitmap[y*maskWidth+x] = pal[buf[0]];
1890 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1891 imgMaskStr->reset();
1892 for (y = 0; y < maskHeight; y++) {
1893 for (x = 0; x < maskWidth; x++) {
1894 imgMaskStr->getPixel(buf);
1896 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1904 imgStr = new ImageStream(str, width, ncomps,bits);
1907 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
1909 msg("<verbose> Ignoring %d by %d image", width, height);
1910 unsigned char buf[8];
1912 for (y = 0; y < height; ++y)
1913 for (x = 0; x < width; ++x) {
1914 imgStr->getPixel(buf);
1922 this->transformXY(state, 0, 1, &x1, &y1);
1923 this->transformXY(state, 0, 0, &x2, &y2);
1924 this->transformXY(state, 1, 0, &x3, &y3);
1925 this->transformXY(state, 1, 1, &x4, &y4);
1927 if(!pbminfo && !(str->getKind()==strDCT)) {
1929 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1933 msg("<verbose> drawing %d by %d masked picture", width, height);
1935 if(!jpeginfo && (str->getKind()==strDCT)) {
1936 msg("<notice> file contains jpeg pictures");
1941 unsigned char buf[8];
1943 unsigned char*pic = new unsigned char[width*height];
1944 gfxcolor_t pal[256];
1946 state->getFillRGB(&rgb);
1948 memset(pal,255,sizeof(pal));
1949 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1950 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1951 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1952 pal[0].a = 255; pal[1].a = 0;
1955 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1956 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1957 for (y = 0; y < height; ++y)
1958 for (x = 0; x < width; ++x)
1960 imgStr->getPixel(buf);
1963 pic[width*y+x] = buf[0];
1966 /* the size of the drawn image is added to the identifier
1967 as the same image may require different bitmaps if displayed
1968 at different sizes (due to antialiasing): */
1971 unsigned char*pic2 = 0;
1974 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1983 height = realheight;
1987 /* make a black/white palette */
1989 float r = 255./(float)(numpalette-1);
1991 for(t=0;t<numpalette;t++) {
1992 pal[t].r = colToByte(rgb.r);
1993 pal[t].g = colToByte(rgb.g);
1994 pal[t].b = colToByte(rgb.b);
1995 pal[t].a = (unsigned char)(t*r);
1999 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2000 for (y = 0; y < height; ++y) {
2001 for (x = 0; x < width; ++x) {
2002 pic2[width*y+x] = pal[pic[y*width+x]];
2005 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2009 if(maskbitmap) free(maskbitmap);
2015 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2016 gfxcolor_t*pic=new gfxcolor_t[width*height];
2017 for (y = 0; y < height; ++y) {
2018 for (x = 0; x < width; ++x) {
2019 imgStr->getPixel(pixBuf);
2020 colorMap->getRGB(pixBuf, &rgb);
2021 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2022 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2023 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2024 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2026 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2030 if(str->getKind()==strDCT)
2031 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2033 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2036 if(maskbitmap) free(maskbitmap);
2039 gfxcolor_t*pic=new gfxcolor_t[width*height];
2040 gfxcolor_t pal[256];
2041 int n = 1 << colorMap->getBits();
2043 for(t=0;t<256;t++) {
2045 colorMap->getRGB(pixBuf, &rgb);
2047 {/*if(maskColors && *maskColors==t) {
2048 msg("<notice> Color %d is transparent", t);
2049 if (imgData->maskColors) {
2051 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2052 if (pix[i] < imgData->maskColors[2*i] ||
2053 pix[i] > imgData->maskColors[2*i+1]) {
2068 pal[t].r = (unsigned char)(colToByte(rgb.r));
2069 pal[t].g = (unsigned char)(colToByte(rgb.g));
2070 pal[t].b = (unsigned char)(colToByte(rgb.b));
2071 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2074 for (y = 0; y < height; ++y) {
2075 for (x = 0; x < width; ++x) {
2076 imgStr->getPixel(pixBuf);
2077 pic[width*y+x] = pal[pixBuf[0]];
2081 if(maskWidth < width && maskHeight < height) {
2082 for(y = 0; y < height; y++) {
2083 for (x = 0; x < width; x++) {
2084 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2088 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2089 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2090 double dx = width / maskWidth;
2091 double dy = height / maskHeight;
2093 for(y = 0; y < maskHeight; y++) {
2095 for (x = 0; x < maskWidth; x++) {
2096 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2097 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2105 height = maskHeight;
2108 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2112 if(maskbitmap) free(maskbitmap);
2117 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2118 int width, int height, GBool invert,
2121 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2122 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2123 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2126 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2127 int width, int height, GfxImageColorMap *colorMap,
2128 int *maskColors, GBool inlineImg)
2130 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2131 colorMap?"colorMap":"no colorMap",
2132 maskColors?"maskColors":"no maskColors",
2134 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2135 colorMap?"colorMap":"no colorMap",
2136 maskColors?"maskColors":"no maskColors",
2139 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2140 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2141 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2144 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2145 int width, int height,
2146 GfxImageColorMap *colorMap,
2147 Stream *maskStr, int maskWidth, int maskHeight,
2150 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2151 colorMap?"colorMap":"no colorMap",
2152 maskWidth, maskHeight);
2153 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2154 colorMap?"colorMap":"no colorMap",
2155 maskWidth, maskHeight);
2157 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2158 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2159 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2162 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2163 int width, int height,
2164 GfxImageColorMap *colorMap,
2166 int maskWidth, int maskHeight,
2167 GfxImageColorMap *maskColorMap)
2169 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2170 colorMap?"colorMap":"no colorMap",
2171 maskWidth, maskHeight);
2172 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2173 colorMap?"colorMap":"no colorMap",
2174 maskWidth, maskHeight);
2176 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2177 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2178 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2181 void GFXOutputDev::stroke(GfxState *state)
2185 GfxPath * path = state->getPath();
2186 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2187 strokeGfxline(state, line, 0);
2191 void GFXOutputDev::fill(GfxState *state)
2193 gfxcolor_t col = getFillColor(state);
2194 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2196 GfxPath * path = state->getPath();
2197 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2198 if(config_optimize_polygons) {
2199 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2203 fillGfxLine(state, line);
2207 void GFXOutputDev::eoFill(GfxState *state)
2209 gfxcolor_t col = getFillColor(state);
2210 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2212 GfxPath * path = state->getPath();
2213 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2214 fillGfxLine(state, line);
2219 static const char* dirseparator()
2228 void addGlobalFont(const char*filename)
2230 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2231 memset(f, 0, sizeof(fontfile_t));
2232 f->filename = filename;
2233 int len = strlen(filename);
2234 char*r1 = strrchr(filename, '/');
2235 char*r2 = strrchr(filename, '\\');
2243 msg("<notice> Adding font \"%s\".", filename);
2244 if(global_fonts_next) {
2245 global_fonts_next->next = f;
2246 global_fonts_next = global_fonts_next->next;
2248 global_fonts_next = global_fonts = f;
2252 void addGlobalLanguageDir(const char*dir)
2254 msg("<notice> Adding %s to language pack directories", dir);
2257 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2258 strcpy(config_file, dir);
2259 strcat(config_file, dirseparator());
2260 strcat(config_file, "add-to-xpdfrc");
2262 fi = fopen(config_file, "rb");
2264 msg("<error> Could not open %s", config_file);
2267 globalParams->parseFile(new GString(config_file), fi);
2271 void addGlobalFontDir(const char*dirname)
2273 #ifdef HAVE_DIRENT_H
2274 msg("<notice> Adding %s to font directories", dirname);
2275 lastfontdir = strdup(dirname);
2276 DIR*dir = opendir(dirname);
2278 msg("<warning> Couldn't open directory %s", dirname);
2283 ent = readdir (dir);
2287 char*name = ent->d_name;
2293 if(!strncasecmp(&name[l-4], ".pfa", 4))
2295 if(!strncasecmp(&name[l-4], ".pfb", 4))
2297 if(!strncasecmp(&name[l-4], ".ttf", 4))
2300 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2301 strcpy(fontname, dirname);
2302 strcat(fontname, dirseparator());
2303 strcat(fontname, name);
2304 addGlobalFont(fontname);
2309 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2313 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2319 this->pagebuflen = 1024;
2320 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2321 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2323 while(pdfpage >= this->pagebuflen)
2325 int oldlen = this->pagebuflen;
2326 this->pagebuflen+=1024;
2327 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2328 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2331 this->pages[pdfpage] = outputpage;
2332 if(pdfpage>this->pagepos)
2333 this->pagepos = pdfpage;
2336 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2337 GfxColorSpace *blendingColorSpace,
2338 GBool isolated, GBool knockout,
2341 const char*colormodename = "";
2343 if(blendingColorSpace) {
2344 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2346 dbg("beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2347 msg("<verbose> beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2349 //states[statepos].createsoftmask |= forSoftMask;
2350 states[statepos].createsoftmask = forSoftMask;
2351 states[statepos].transparencygroup = !forSoftMask;
2352 states[statepos].isolated = isolated;
2354 states[statepos].olddevice = this->device;
2355 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2357 gfxdevice_record_init(this->device);
2359 /*if(!forSoftMask) { ////???
2360 state->setFillOpacity(0.0);
2365 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2368 dbg("endTransparencyGroup");
2369 msg("<verbose> endTransparencyGroup");
2371 gfxdevice_t*r = this->device;
2373 this->device = states[statepos].olddevice;
2375 gfxresult_t*recording = r->finish(r);
2376 if(states[statepos].createsoftmask) {
2377 states[statepos-1].softmaskrecording = recording;
2379 states[statepos-1].grouprecording = recording;
2382 states[statepos].createsoftmask = 0;
2383 states[statepos].transparencygroup = 0;
2387 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2389 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2390 "colordodge","colorburn","hardlight","softlight","difference",
2391 "exclusion","hue","saturation","color","luminosity"};
2393 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2394 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2396 if(state->getBlendMode() == gfxBlendNormal)
2397 infofeature("transparency groups");
2400 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2401 warnfeature(buffer, 0);
2404 gfxresult_t*grouprecording = states[statepos].grouprecording;
2406 int blendmode = state->getBlendMode();
2407 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2408 int alpha = state->getFillOpacity()*255;
2409 if(blendmode == gfxBlendMultiply && alpha>200)
2412 gfxdevice_ops_init(&ops, this->device, alpha);
2413 gfxresult_record_replay(grouprecording, &ops);
2416 grouprecording->destroy(grouprecording);
2418 states[statepos].grouprecording = 0;
2421 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2423 /* alpha = 1: retrieve mask values from alpha layer
2424 alpha = 0: retrieve mask values from luminance */
2425 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2426 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2427 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2428 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2430 infofeature("soft masks");
2432 warnfeature("soft masks from alpha channel",0);
2434 states[statepos].olddevice = this->device;
2435 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2436 gfxdevice_record_init(this->device);
2438 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2440 states[statepos].softmask = 1;
2441 states[statepos].softmask_alpha = alpha;
2444 static inline Guchar div255(int x) {
2445 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2448 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2450 if(c < min) c = min;
2451 if(c > max) c = max;
2455 void GFXOutputDev::clearSoftMask(GfxState *state)
2457 if(!states[statepos].softmask)
2459 states[statepos].softmask = 0;
2460 dbg("clearSoftMask statepos=%d", statepos);
2461 msg("<verbose> clearSoftMask");
2463 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2464 msg("<error> Error in softmask/tgroup ordering");
2468 gfxresult_t*mask = states[statepos].softmaskrecording;
2469 gfxresult_t*below = this->device->finish(this->device);
2470 this->device = states[statepos].olddevice;
2472 /* get outline of all objects below the soft mask */
2473 gfxdevice_t uniondev;
2474 gfxdevice_union_init(&uniondev, 0);
2475 gfxresult_record_replay(below, &uniondev);
2476 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2477 uniondev.finish(&uniondev);
2479 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2481 this->device->startclip(this->device, belowoutline);
2482 gfxresult_record_replay(below, this->device);
2483 gfxresult_record_replay(mask, this->device);
2484 this->device->endclip(this->device);
2485 gfxline_free(belowoutline);
2488 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2489 if(width<=0 || height<=0)
2492 gfxdevice_t belowrender;
2493 gfxdevice_render_init(&belowrender);
2494 if(states[statepos+1].isolated) {
2495 belowrender.setparameter(&belowrender, "fillwhite", "1");
2497 belowrender.setparameter(&belowrender, "antialize", "2");
2498 belowrender.startpage(&belowrender, width, height);
2499 gfxresult_record_replay(below, &belowrender);
2500 belowrender.endpage(&belowrender);
2501 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2502 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2503 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2505 gfxdevice_t maskrender;
2506 gfxdevice_render_init(&maskrender);
2507 maskrender.startpage(&maskrender, width, height);
2508 gfxresult_record_replay(mask, &maskrender);
2509 maskrender.endpage(&maskrender);
2510 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2511 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2513 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2514 msg("<fatal> Internal error in mask drawing");
2519 for(y=0;y<height;y++) {
2520 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2521 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2522 for(x=0;x<width;x++) {
2524 if(states[statepos].softmask_alpha) {
2527 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2530 l2->a = div255(alpha*l2->a);
2532 /* DON'T premultiply alpha- this is done by fillbitmap,
2533 depending on the output device */
2534 //l2->r = div255(alpha*l2->r);
2535 //l2->g = div255(alpha*l2->g);
2536 //l2->b = div255(alpha*l2->b);
2542 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2545 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2546 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2548 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2550 mask->destroy(mask);
2551 below->destroy(below);
2552 maskresult->destroy(maskresult);
2553 belowresult->destroy(belowresult);
2554 states[statepos].softmaskrecording = 0;
2559 // public: ~MemCheck()
2561 // delete globalParams;globalParams=0;
2562 // Object::memCheck(stderr);
2563 // gMemReport(stderr);