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
36 #ifdef HAVE_FONTCONFIG
37 #include <fontconfig.h>
42 #include <goo/GooString.h>
43 #include <goo/gfile.h>
58 #include "OutputDev.h"
61 #include "NameToUnicodeTable.h"
62 #include "GlobalParams.h"
63 #include "GFXOutputDev.h"
65 // swftools header files
67 #include "../gfxdevice.h"
68 #include "../gfxtools.h"
69 #include "../gfxfont.h"
70 #include "../gfxpoly.h"
71 #include "../devices/record.h"
72 #include "../devices/ops.h"
73 #include "../devices/polyops.h"
74 #include "../devices/render.h"
81 #define SQRT2 1.41421356237309504880
83 typedef struct _fontfile
86 int len; // basename length
88 struct _fontfile*next;
93 static fontfile_t* global_fonts = 0;
94 static fontfile_t* global_fonts_next = 0;
96 static int fontnum = 0;
100 static char* lastfontdir = 0;
111 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
112 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
113 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
114 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
115 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
116 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
117 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
118 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
119 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
120 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
121 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
122 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
123 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
124 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
127 static int verbose = 0;
128 static int dbgindent = 1;
129 static void dbg(const char*format, ...)
136 va_start(arglist, format);
137 vsprintf(buf, format, arglist);
140 while(l && buf[l-1]=='\n') {
145 int indent = dbgindent;
154 GFXOutputGlobals*gfxglobals=0;
156 GFXOutputGlobals::GFXOutputGlobals()
158 this->featurewarnings = 0;
160 this->textmodeinfo = 0;
164 GFXOutputGlobals::~GFXOutputGlobals()
166 feature_t*f = this->featurewarnings;
168 feature_t*next = f->next;
170 free(f->string);f->string =0;
176 this->featurewarnings = 0;
179 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
181 feature_t*f = gfxglobals->featurewarnings;
183 if(!strcmp(feature, f->string))
187 f = (feature_t*)malloc(sizeof(feature_t));
188 f->string = strdup(feature);
189 f->next = gfxglobals->featurewarnings;
190 gfxglobals->featurewarnings = f;
192 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
193 if(this->config_break_on_warning) {
194 msg("<fatal> Aborting conversion due to unsupported feature");
198 msg("<notice> File contains %s",feature);
201 void GFXOutputDev::warnfeature(const char*feature,char fully)
203 showfeature(feature,fully,1);
205 void GFXOutputDev::infofeature(const char*feature)
207 showfeature(feature,0,0);
210 GFXOutputState::GFXOutputState() {
212 this->createsoftmask = 0;
213 this->transparencygroup = 0;
215 this->grouprecording = 0;
219 GBool GFXOutputDev::interpretType3Chars()
224 typedef struct _drawnchar
242 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
243 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
248 free(chars);chars = 0;
255 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
259 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
262 chars[num_chars].x = x;
263 chars[num_chars].y = y;
264 chars[num_chars].color = color;
265 chars[num_chars].charid = charid;
269 char* writeOutStdFont(fontentry* f)
274 char* tmpFileName = mktmpname(namebuf1);
276 sprintf(namebuf2, "%s.afm", tmpFileName);
277 fi = fopen(namebuf2, "wb");
280 fwrite(f->afm, 1, f->afmlen, fi);
283 sprintf(namebuf2, "%s.pfb", tmpFileName);
284 fi = fopen(namebuf2, "wb");
287 fwrite(f->pfb, 1, f->pfblen, fi);
289 return strdup(namebuf2);
291 void unlinkfont(char* filename)
296 msg("<verbose> Removing temporary font file %s", filename);
299 if(!strncmp(&filename[l-4],".afm",4)) {
300 memcpy(&filename[l-4],".pfb",4); unlink(filename);
301 memcpy(&filename[l-4],".pfa",4); unlink(filename);
302 memcpy(&filename[l-4],".afm",4);
305 if(!strncmp(&filename[l-4],".pfa",4)) {
306 memcpy(&filename[l-4],".afm",4); unlink(filename);
307 memcpy(&filename[l-4],".pfa",4);
310 if(!strncmp(&filename[l-4],".pfb",4)) {
311 memcpy(&filename[l-4],".afm",4); unlink(filename);
312 memcpy(&filename[l-4],".pfb",4);
317 static int config_use_fontconfig = 1;
318 static int fcinitcalled = 0;
320 GFXGlobalParams::GFXGlobalParams()
321 : GlobalParams((char*)"")
323 //setupBaseFonts(char *dir); //not tested yet
325 GFXGlobalParams::~GFXGlobalParams()
327 msg("<verbose> Performing cleanups");
329 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
330 if(pdf2t1map[t].fullfilename) {
331 unlinkfont(pdf2t1map[t].fullfilename);
334 #ifdef HAVE_FONTCONFIG
335 if(config_use_fontconfig && fcinitcalled)
339 #ifdef HAVE_FONTCONFIG
340 static char fc_ismatch(FcPattern*match, char*family, char*style)
342 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
343 FcBool scalable=FcFalse, outline=FcFalse;
344 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
345 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
346 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
347 FcPatternGetBool(match, "outline", 0, &outline);
348 FcPatternGetBool(match, "scalable", 0, &scalable);
350 if(scalable!=FcTrue || outline!=FcTrue)
353 if (!strcasecmp(fcfamily, family)) {
354 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
357 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
363 char* fontconfig_searchForFont(char*name)
365 #ifdef HAVE_FONTCONFIG
366 if(!config_use_fontconfig)
369 // call init ony once
373 // check whether we have a config file
374 char* configfile = (char*)FcConfigFilename(0);
375 int configexists = 0;
376 FILE*fi = fopen(configfile, "rb");
378 configexists = 1;fclose(fi);
379 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
381 msg("<debug> Initializing FontConfig (no configfile)");
385 /* A fontconfig instance which didn't find a configfile is unbelievably
386 cranky, so let's just write out a small xml file and make fontconfig
388 FcConfig*c = FcConfigCreate();
390 char* tmpFileName = mktmpname(namebuf);
391 FILE*fi = fopen(tmpFileName, "wb");
392 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
394 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
396 fprintf(fi, "<dir>~/.fonts</dir>\n");
398 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
400 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
401 fprintf(fi, "</fontconfig>\n");
403 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
404 FcConfigBuildFonts(c);
405 FcConfigSetCurrent(c);
409 msg("<debug> FontConfig Initialization failed. Disabling.");
410 config_use_fontconfig = 0;
413 FcConfig * config = FcConfigGetCurrent();
415 msg("<debug> FontConfig Config Initialization failed. Disabling.");
416 config_use_fontconfig = 0;
419 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
420 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
421 if(!set || !set->nfont) {
422 msg("<debug> FontConfig has zero fonts. Disabling.");
423 config_use_fontconfig = 0;
427 if(getLogLevel() >= LOGLEVEL_TRACE) {
429 for(t=0;t<set->nfont;t++) {
430 char*fcfamily=0,*fcstyle=0,*filename=0;
431 FcBool scalable=FcFalse, outline=FcFalse;
432 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
433 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
434 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
435 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
436 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
437 if(scalable && outline) {
438 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
444 char*family = strdup(name);
446 char*dash = strchr(family, '-');
447 FcPattern*pattern = 0;
451 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
452 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
454 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
455 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
459 FcConfigSubstitute(0, pattern, FcMatchPattern);
460 FcDefaultSubstitute(pattern);
462 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
465 for(t=0;t<set->nfont;t++) {
466 FcPattern*match = set->fonts[t];
467 //FcPattern*match = FcFontMatch(0, pattern, &result);
468 if(fc_ismatch(match, family, style)) {
470 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
471 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
474 //FcPatternDestroy(match);
475 msg("<debug> fontconfig: returning filename %s", filename);
477 FcPatternDestroy(pattern);
478 FcFontSetDestroy(set);
479 return filename?strdup(filename):0;
484 FcPatternDestroy(pattern);
485 FcFontSetDestroy(set);
492 static DisplayFontParamKind detectFontType(const char*filename)
494 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
495 return displayFontTT;
496 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
497 return displayFontT1;
498 return displayFontTT;
501 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
503 msg("<verbose> looking for font %s in global params", fontName->getCString());
505 char*name = fontName->getCString();
507 /* see if it is a pdf standard font */
509 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
510 if(!strcmp(name, pdf2t1map[t].pdffont)) {
511 if(!pdf2t1map[t].fullfilename) {
512 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
513 if(!pdf2t1map[t].fullfilename) {
514 msg("<error> Couldn't save default font- is the Temp Directory writable?");
516 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
519 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
520 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
525 int bestlen = 0x7fffffff;
526 const char*bestfilename = 0;
528 fontfile_t*f = global_fonts;
530 if(strstr(f->filename, name)) {
531 if(f->len < bestlen) {
533 bestfilename = f->filename;
539 /* if we didn't find anything up to now, try looking for the
540 font via fontconfig */
543 filename = fontconfig_searchForFont(name);
545 filename = strdup(bestfilename);
549 DisplayFontParamKind kind = detectFontType(filename);
550 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
551 if(kind == displayFontTT) {
552 dfp->tt.fileName = new GString(filename);
554 dfp->t1.fileName = new GString(filename);
559 return GlobalParams::getDisplayFont(fontName);
562 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
565 gfxglobals = new GFXOutputGlobals();
569 this->xref = doc->getXRef();
571 this->type3active = 0;
574 this->user_movex = 0;
575 this->user_movey = 0;
578 this->user_clipx1 = 0;
579 this->user_clipy1 = 0;
580 this->user_clipx2 = 0;
581 this->user_clipy2 = 0;
582 this->current_fontinfo = 0;
583 this->current_text_stroke = 0;
584 this->current_text_clip = 0;
585 this->outer_clip_box = 0;
586 this->config_bigchar=0;
587 this->config_convertgradients=1;
588 this->config_break_on_warning=0;
589 this->config_remapunicode=0;
590 this->config_transparent=0;
591 this->config_extrafontdata = 0;
592 this->config_optimize_polygons = 0;
593 this->config_multiply = 1;
594 this->dashPattern = 0;
598 memset(states, 0, sizeof(states));
601 void GFXOutputDev::setParameter(const char*key, const char*value)
603 if(!strcmp(key,"breakonwarning")) {
604 this->config_break_on_warning = atoi(value);
605 } else if(!strcmp(key,"remapunicode")) {
606 this->config_remapunicode = atoi(value);
607 } else if(!strcmp(key,"transparent")) {
608 this->config_transparent = atoi(value);
609 } else if(!strcmp(key,"extrafontdata")) {
610 this->config_extrafontdata = atoi(value);
611 } else if(!strcmp(key,"convertgradients")) {
612 this->config_convertgradients = atoi(value);
613 } else if(!strcmp(key,"multiply")) {
614 this->config_multiply = atoi(value);
615 if(this->config_multiply<1)
616 this->config_multiply=1;
617 } else if(!strcmp(key,"optimize_polygons")) {
618 this->config_optimize_polygons = atoi(value);
622 void GFXOutputDev::setDevice(gfxdevice_t*dev)
627 void GFXOutputDev::setMove(int x,int y)
629 this->user_movex = x;
630 this->user_movey = y;
633 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
635 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
636 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
638 this->user_clipx1 = x1;
639 this->user_clipy1 = y1;
640 this->user_clipx2 = x2;
641 this->user_clipy2 = y2;
644 static char*getFontName(GfxFont*font)
647 GString*gstr = font->getName();
648 char* fname = gstr==0?0:gstr->getCString();
652 sprintf(buf, "UFONT%d", r->num);
653 fontid = strdup(buf);
655 fontid = strdup(fname);
659 char* plus = strchr(fontid, '+');
660 if(plus && plus < &fontid[strlen(fontid)-1]) {
661 fontname = strdup(plus+1);
663 fontname = strdup(fontid);
669 static void dumpFontInfo(const char*loglevel, GfxFont*font);
670 static int lastdumps[1024];
671 static int lastdumppos = 0;
676 static void showFontError(GfxFont*font, int nr)
680 for(t=0;t<lastdumppos;t++)
681 if(lastdumps[t] == r->num)
685 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
686 lastdumps[lastdumppos++] = r->num;
688 msg("<warning> The following font caused problems:");
690 msg("<warning> The following font caused problems (substituting):");
692 msg("<warning> The following Type 3 Font will be rendered as graphics:");
693 dumpFontInfo("<warning>", font);
696 static void dumpFontInfo(const char*loglevel, GfxFont*font)
698 char* id = getFontID(font);
699 char* name = getFontName(font);
700 Ref* r=font->getID();
701 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
703 GString*gstr = font->getTag();
705 msg("%s| Tag: %s", loglevel, id);
707 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
709 GfxFontType type=font->getType();
711 case fontUnknownType:
712 msg("%s| Type: unknown",loglevel);
715 msg("%s| Type: 1",loglevel);
718 msg("%s| Type: 1C",loglevel);
721 msg("%s| Type: 3",loglevel);
724 msg("%s| Type: TrueType",loglevel);
727 msg("%s| Type: CIDType0",loglevel);
730 msg("%s| Type: CIDType0C",loglevel);
733 msg("%s| Type: CIDType2",loglevel);
738 GBool embedded = font->getEmbeddedFontID(&embRef);
740 if(font->getEmbeddedFontName()) {
741 embeddedName = font->getEmbeddedFontName()->getCString();
744 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
746 gstr = font->getExtFontFile();
748 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
750 // Get font descriptor flags.
751 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
752 if(font->isSerif()) msg("%s| is serif", loglevel);
753 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
754 if(font->isItalic()) msg("%s| is italic", loglevel);
755 if(font->isBold()) msg("%s| is bold", loglevel);
761 //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");}
762 //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");}
764 void dump_outline(gfxline_t*line)
766 /*gfxbbox_t*r = gfxline_isrectangle(line);
768 printf("is not a rectangle\n");
770 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
774 if(line->type == gfx_moveTo) {
775 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
776 } else if(line->type == gfx_lineTo) {
777 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
778 } else if(line->type == gfx_splineTo) {
779 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
785 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
787 int num = path->getNumSubpaths();
790 double lastx=0,lasty=0,posx=0,posy=0;
793 msg("<warning> empty path");
797 gfxdrawer_target_gfxline(&draw);
799 for(t = 0; t < num; t++) {
800 GfxSubpath *subpath = path->getSubpath(t);
801 int subnum = subpath->getNumPoints();
802 double bx=0,by=0,cx=0,cy=0;
804 for(s=0;s<subnum;s++) {
807 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
810 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
811 draw.lineTo(&draw, lastx, lasty);
813 draw.moveTo(&draw, x,y);
818 } else if(subpath->getCurve(s) && cpos==0) {
822 } else if(subpath->getCurve(s) && cpos==1) {
830 draw.lineTo(&draw, x,y);
832 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
839 /* fix non-closed lines */
840 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
841 draw.lineTo(&draw, lastx, lasty);
843 gfxline_t*result = (gfxline_t*)draw.result(&draw);
845 gfxline_optimize(result);
850 GBool GFXOutputDev::useTilingPatternFill()
852 infofeature("tiled patterns");
853 // if(config_convertgradients)
857 GBool GFXOutputDev::useShadedFills()
859 infofeature("shaded fills");
860 if(config_convertgradients)
865 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
867 state->transform(x,y,nx,ny);
868 *nx += user_movex + clipmovex;
869 *ny += user_movey + clipmovey;
873 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
874 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
875 int paintType, Dict *resDict,
876 double *mat, double *bbox,
877 int x0, int y0, int x1, int y1,
878 double xStep, double yStep)
880 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
881 int paintType, Dict *resDict,
882 double *mat, double *bbox,
883 int x0, int y0, int x1, int y1,
884 double xStep, double yStep)
887 msg("<debug> tilingPatternFill");
888 infofeature("tiling pattern fills");
891 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
893 msg("<error> functionShadedFill not supported yet");
894 infofeature("function shaded fills");
897 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
901 colspace->getRGB(col, &rgb);
902 c.r = colToByte(rgb.r);
903 c.g = colToByte(rgb.g);
904 c.b = colToByte(rgb.b);
909 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
911 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
912 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
915 this->transformXY(state, x0,y0, &x0,&y0);
916 this->transformXY(state, x1,y1, &x1,&y1);
917 this->transformXY(state, x2,y2, &x2,&y2);
922 shading->getColor(0.0, &color0);
923 shading->getColor(0.5, &color1);
924 shading->getColor(1.0, &color2);
926 GfxColorSpace* colspace = shading->getColorSpace();
928 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
929 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
930 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
931 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
932 infofeature("radial shaded fills");
934 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
938 g[0].color = col2col(colspace, &color0);
939 g[1].color = col2col(colspace, &color1);
940 g[2].color = col2col(colspace, &color2);
945 gfxbbox_t b = states[statepos].clipbbox;
946 gfxline_t p1,p2,p3,p4,p5;
947 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
948 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
949 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
950 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
951 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
954 //m.m00 = (x3-x0); m.m10 = (x1-x0);
955 //m.m01 = (y3-y0); m.m11 = (y1-y0);
956 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
957 m.m00 = (x1-x0); m.m10 = (x2-x0);
958 m.m01 = (y1-y0); m.m11 = (y2-y0);
962 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
966 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
969 shading->getCoords(&x0,&y0,&x1,&y1);
970 this->transformXY(state, x0,y0,&x0,&y0);
971 this->transformXY(state, x1,y1,&x1,&y1);
976 shading->getColor(0.0, &color0);
977 shading->getColor(0.5, &color1);
978 shading->getColor(1.0, &color2);
980 GfxColorSpace* colspace = shading->getColorSpace();
982 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
983 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
984 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
985 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
987 infofeature("axial shaded fills");
989 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
993 g[0].color = col2col(colspace, &color0);
994 g[1].color = col2col(colspace, &color1);
995 g[2].color = col2col(colspace, &color2);
1000 double xMin,yMin,xMax,yMax;
1001 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1002 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1003 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1006 xMin = 1024; yMin = 1024;
1008 gfxbbox_t b = states[statepos].clipbbox;
1009 gfxline_t p1,p2,p3,p4,p5;
1010 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1011 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1012 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1013 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1014 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1016 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1017 the middle of the two control points */
1019 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1020 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1021 m.tx = (x0 + x1)/2 - 0.5;
1022 m.ty = (y0 + y1)/2 - 0.5;
1024 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1030 GBool GFXOutputDev::useDrawForm()
1032 infofeature("forms");
1035 void GFXOutputDev::drawForm(Ref id)
1037 msg("<error> drawForm not implemented");
1039 GBool GFXOutputDev::needNonText()
1043 void GFXOutputDev::endPage()
1045 msg("<verbose> endPage (GfxOutputDev)");
1046 if(outer_clip_box) {
1047 device->endclip(device);
1050 this->dashPattern = 0;
1051 /* notice: we're not fully done yet with this page- there might still be
1052 a few calls to drawLink() yet to come */
1055 static inline double sqr(double x) {return x*x;}
1057 #define STROKE_FILL 1
1058 #define STROKE_CLIP 2
1059 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1061 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1062 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1063 double miterLimit = state->getMiterLimit();
1064 double width = state->getTransformedLineWidth();
1067 double opaq = state->getStrokeOpacity();
1069 state->getFillRGB(&rgb);
1071 state->getStrokeRGB(&rgb);
1073 col.r = colToByte(rgb.r);
1074 col.g = colToByte(rgb.g);
1075 col.b = colToByte(rgb.b);
1076 col.a = (unsigned char)(opaq*255);
1078 gfx_capType capType = gfx_capRound;
1079 if(lineCap == 0) capType = gfx_capButt;
1080 else if(lineCap == 1) capType = gfx_capRound;
1081 else if(lineCap == 2) capType = gfx_capSquare;
1083 gfx_joinType joinType = gfx_joinRound;
1084 if(lineJoin == 0) joinType = gfx_joinMiter;
1085 else if(lineJoin == 1) joinType = gfx_joinRound;
1086 else if(lineJoin == 2) joinType = gfx_joinBevel;
1088 gfxline_t*line2 = 0;
1090 if(this->dashLength && this->dashPattern) {
1091 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1094 /* try to find out how much the transformation matrix would
1095 stretch the dashes, and factor that into the dash lengths.
1096 This is not the entirely correct approach- it would be
1097 better to first convert the path to an unscaled version,
1098 then apply dashing, and then transform the path using
1099 the current transformation matrix. However there are few
1100 PDFs which actually stretch a dashed path in a non-orthonormal
1102 double tx1, ty1, tx2, ty2;
1103 this->transformXY(state, 0, 0, &tx1, &ty1);
1104 this->transformXY(state, 1, 1, &tx2, &ty2);
1105 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1107 msg("<trace> %d dashes", this->dashLength);
1108 msg("<trace> | phase: %f", this->dashStart);
1109 for(t=0;t<this->dashLength;t++) {
1110 dash[t] = (float)this->dashPattern[t] * f;
1113 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1115 dash[this->dashLength] = -1;
1116 if(getLogLevel() >= LOGLEVEL_TRACE) {
1120 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1123 msg("<trace> After dashing:");
1126 if(getLogLevel() >= LOGLEVEL_TRACE) {
1127 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1129 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1130 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1132 col.r,col.g,col.b,col.a
1137 if(flags&STROKE_FILL) {
1138 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1139 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1140 if(getLogLevel() >= LOGLEVEL_TRACE) {
1141 dump_outline(gfxline);
1144 msg("<warning> Empty polygon (resulting from stroked line)");
1146 if(flags&STROKE_CLIP) {
1147 device->startclip(device, gfxline);
1148 states[statepos].clipping++;
1150 device->fill(device, gfxline, &col);
1152 gfxline_free(gfxline);
1155 if(flags&STROKE_CLIP)
1156 msg("<error> Stroke&clip not supported at the same time");
1157 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1161 gfxline_free(line2);
1164 gfxcolor_t getFillColor(GfxState * state)
1167 double opaq = state->getFillOpacity();
1168 state->getFillRGB(&rgb);
1170 col.r = colToByte(rgb.r);
1171 col.g = colToByte(rgb.g);
1172 col.b = colToByte(rgb.b);
1173 col.a = (unsigned char)(opaq*255);
1177 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1179 gfxcolor_t col = getFillColor(state);
1181 if(getLogLevel() >= LOGLEVEL_TRACE) {
1182 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1185 device->fill(device, line, &col);
1188 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1190 if(getLogLevel() >= LOGLEVEL_TRACE) {
1193 gfxbbox_t bbox = gfxline_getbbox(line);
1194 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1196 device->startclip(device, line);
1197 states[statepos].clipping++;
1200 void GFXOutputDev::clip(GfxState *state)
1202 GfxPath * path = state->getPath();
1203 msg("<trace> clip");
1204 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1205 if(config_optimize_polygons) {
1206 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1210 clipToGfxLine(state, line);
1214 void GFXOutputDev::eoClip(GfxState *state)
1216 GfxPath * path = state->getPath();
1217 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1218 clipToGfxLine(state, line);
1221 void GFXOutputDev::clipToStrokePath(GfxState *state)
1223 GfxPath * path = state->getPath();
1224 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1226 if(getLogLevel() >= LOGLEVEL_TRACE) {
1227 double width = state->getTransformedLineWidth();
1228 msg("<trace> cliptostrokepath width=%f", width);
1232 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1236 void GFXOutputDev::finish()
1238 if(outer_clip_box) {
1240 device->endclip(device);
1246 GFXOutputDev::~GFXOutputDev()
1249 if(this->dashPattern) {
1250 free(this->dashPattern);this->dashPattern = 0;
1253 GBool GFXOutputDev::upsideDown()
1257 GBool GFXOutputDev::useDrawChar()
1262 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1263 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1265 static char tmp_printstr[4096];
1266 char* makeStringPrintable(char*str)
1268 int len = strlen(str);
1275 for(t=0;t<len;t++) {
1280 tmp_printstr[t] = c;
1283 tmp_printstr[len++] = '.';
1284 tmp_printstr[len++] = '.';
1285 tmp_printstr[len++] = '.';
1287 tmp_printstr[len] = 0;
1288 return tmp_printstr;
1290 void GFXOutputDev::updateFontMatrix(GfxState*state)
1292 double* ctm = state->getCTM();
1293 double fontSize = state->getFontSize();
1294 double*textMat = state->getTextMat();
1296 /* taking the absolute value of horizScaling seems to be required for
1297 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1298 double hscale = fabs(state->getHorizScaling());
1300 // from xpdf-3.02/SplashOutputDev:updateFont
1301 double mm11 = textMat[0] * fontSize * hscale;
1302 double mm12 = textMat[1] * fontSize * hscale;
1303 double mm21 = textMat[2] * fontSize;
1304 double mm22 = textMat[3] * fontSize;
1306 // multiply with ctm, like state->getFontTransMat() does
1307 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1308 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1309 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1310 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1311 this->current_font_matrix.tx = 0;
1312 this->current_font_matrix.ty = 0;
1315 void GFXOutputDev::beginString(GfxState *state, GString *s)
1317 int render = state->getRender();
1318 if(current_text_stroke) {
1319 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1322 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1325 static gfxline_t* mkEmptyGfxShape(double x, double y)
1327 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1328 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1332 static char isValidUnicode(int c)
1334 if(c>=32 && c<0x2fffe)
1339 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1340 double dx, double dy,
1341 double originX, double originY,
1342 CharCode charid, int nBytes, Unicode *_u, int uLen)
1344 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1345 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1349 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1351 int render = state->getRender();
1352 gfxcolor_t col = getFillColor(state);
1354 // check for invisible text -- this is used by Acrobat Capture
1355 if (render == RENDER_INVISIBLE) {
1357 if(!config_extrafontdata)
1361 GfxFont*font = state->getFont();
1363 if(font->getType() == fontType3) {
1364 /* type 3 chars are passed as graphics */
1365 msg("<debug> type3 char at %f/%f", x, y);
1369 Unicode u = uLen?(_u[0]):0;
1370 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);
1372 gfxmatrix_t m = this->current_font_matrix;
1373 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1374 //m.tx += originX; m.ty += originY;
1376 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1377 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1379 msg("<debug> Drawing glyph %d as shape", charid);
1380 if(!gfxglobals->textmodeinfo) {
1381 msg("<notice> Some texts will be rendered as shape");
1382 gfxglobals->textmodeinfo = 1;
1384 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1385 gfxline_t*tglyph = gfxline_clone(glyph);
1386 gfxline_transform(tglyph, &m);
1387 if((render&3) != RENDER_INVISIBLE) {
1388 gfxline_t*add = gfxline_clone(tglyph);
1389 current_text_stroke = gfxline_append(current_text_stroke, add);
1391 if(render&RENDER_CLIP) {
1392 gfxline_t*add = gfxline_clone(tglyph);
1393 current_text_clip = gfxline_append(current_text_clip, add);
1394 if(!current_text_clip) {
1395 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1398 gfxline_free(tglyph);
1402 void GFXOutputDev::endString(GfxState *state)
1404 int render = state->getRender();
1405 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1407 if(current_text_stroke) {
1408 /* fillstroke and stroke text rendering objects we can process right
1409 now (as there may be texts of other rendering modes in this
1410 text object)- clipping objects have to wait until endTextObject,
1412 device->setparameter(device, "mark","TXT");
1413 if((render&3) == RENDER_FILL) {
1414 fillGfxLine(state, current_text_stroke);
1415 gfxline_free(current_text_stroke);
1416 current_text_stroke = 0;
1417 } else if((render&3) == RENDER_FILLSTROKE) {
1418 fillGfxLine(state, current_text_stroke);
1419 strokeGfxline(state, current_text_stroke,0);
1420 gfxline_free(current_text_stroke);
1421 current_text_stroke = 0;
1422 } else if((render&3) == RENDER_STROKE) {
1423 strokeGfxline(state, current_text_stroke,0);
1424 gfxline_free(current_text_stroke);
1425 current_text_stroke = 0;
1427 device->setparameter(device, "mark","");
1431 void GFXOutputDev::endTextObject(GfxState *state)
1433 int render = state->getRender();
1434 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1436 if(current_text_clip) {
1437 device->setparameter(device, "mark","TXT");
1438 clipToGfxLine(state, current_text_clip);
1439 device->setparameter(device, "mark","");
1440 gfxline_free(current_text_clip);
1441 current_text_clip = 0;
1445 /* the logic seems to be as following:
1446 first, beginType3Char is called, with the charcode and the coordinates.
1447 if this function returns true, it already knew about the char and has now drawn it.
1448 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1449 called with some parameters.
1450 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1451 at the position first passed to beginType3Char). the char ends with endType3Char.
1453 The drawing operations between beginType3Char and endType3Char are somewhat different to
1454 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1455 color determines the color of a font)
1458 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1460 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1463 if(config_extrafontdata && current_fontinfo) {
1465 gfxmatrix_t m = this->current_font_matrix;
1466 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1467 m.m00*=INTERNAL_FONT_SIZE;
1468 m.m01*=INTERNAL_FONT_SIZE;
1469 m.m10*=INTERNAL_FONT_SIZE;
1470 m.m11*=INTERNAL_FONT_SIZE;
1472 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1473 msg("<error> Invalid charid %d for font", charid);
1476 gfxcolor_t col={0,0,0,0};
1477 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1478 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1482 /* the character itself is going to be passed using the draw functions */
1483 return gFalse; /* gTrue= is_in_cache? */
1486 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1488 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1491 void GFXOutputDev::endType3Char(GfxState *state)
1494 msg("<debug> endType3Char");
1497 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1499 this->currentpage = pageNum;
1501 int rot = doc->getPageRotate(1);
1502 gfxcolor_t white = {255,255,255,255};
1503 gfxcolor_t black = {255,0,0,0};
1505 gfxline_t clippath[5];
1507 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1508 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1509 Use CropBox, not MediaBox, as page size
1516 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1517 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1519 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1520 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1522 this->clipmovex = -(int)x1;
1523 this->clipmovey = -(int)y1;
1525 /* apply user clip box */
1526 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1527 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1528 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1529 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1530 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1531 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1534 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1536 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);
1538 msg("<verbose> page is rotated %d degrees", rot);
1540 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1541 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1542 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1543 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1544 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1545 device->startclip(device, clippath); outer_clip_box = 1;
1546 if(!config_transparent) {
1547 device->fill(device, clippath, &white);
1549 states[statepos].clipbbox.xmin = x1;
1550 states[statepos].clipbbox.ymin = x1;
1551 states[statepos].clipbbox.xmax = x2;
1552 states[statepos].clipbbox.ymax = y2;
1554 this->dashPattern = 0;
1555 this->dashLength = 0;
1556 this->dashStart = 0;
1560 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1562 double x1, y1, x2, y2;
1563 gfxline_t points[5];
1566 msg("<debug> drawlink");
1568 link->getRect(&x1, &y1, &x2, &y2);
1569 cvtUserToDev(x1, y1, &x, &y);
1570 points[0].type = gfx_moveTo;
1571 points[0].x = points[4].x = x + user_movex + clipmovex;
1572 points[0].y = points[4].y = y + user_movey + clipmovey;
1573 points[0].next = &points[1];
1574 cvtUserToDev(x2, y1, &x, &y);
1575 points[1].type = gfx_lineTo;
1576 points[1].x = x + user_movex + clipmovex;
1577 points[1].y = y + user_movey + clipmovey;
1578 points[1].next = &points[2];
1579 cvtUserToDev(x2, y2, &x, &y);
1580 points[2].type = gfx_lineTo;
1581 points[2].x = x + user_movex + clipmovex;
1582 points[2].y = y + user_movey + clipmovey;
1583 points[2].next = &points[3];
1584 cvtUserToDev(x1, y2, &x, &y);
1585 points[3].type = gfx_lineTo;
1586 points[3].x = x + user_movex + clipmovex;
1587 points[3].y = y + user_movey + clipmovey;
1588 points[3].next = &points[4];
1589 cvtUserToDev(x1, y1, &x, &y);
1590 points[4].type = gfx_lineTo;
1591 points[4].x = x + user_movex + clipmovex;
1592 points[4].y = y + user_movey + clipmovey;
1595 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1596 points[0].x, points[0].y,
1597 points[1].x, points[1].y,
1598 points[2].x, points[2].y,
1599 points[3].x, points[3].y);
1601 if(getLogLevel() >= LOGLEVEL_TRACE) {
1602 dump_outline(points);
1605 LinkAction*action=link->getAction();
1608 const char*type = "-?-";
1611 msg("<trace> drawlink action=%d", action->getKind());
1612 switch(action->getKind())
1616 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1617 LinkDest *dest=NULL;
1618 if (ha->getDest()==NULL)
1619 dest=catalog->findDest(ha->getNamedDest());
1620 else dest=ha->getDest();
1622 if (dest->isPageRef()){
1623 Ref pageref=dest->getPageRef();
1624 page=catalog->findPage(pageref.num,pageref.gen);
1626 else page=dest->getPageNum();
1627 sprintf(buf, "%d", page);
1634 LinkGoToR*l = (LinkGoToR*)action;
1635 GString*g = l->getFileName();
1637 s = strdup(g->getCString());
1639 /* if the GoToR link has no filename, then
1640 try to find a refernce in the *local*
1642 GString*g = l->getNamedDest();
1644 s = strdup(g->getCString());
1650 LinkNamed*l = (LinkNamed*)action;
1651 GString*name = l->getName();
1653 s = strdup(name->lowerCase()->getCString());
1654 named = name->getCString();
1657 if(strstr(s, "next") || strstr(s, "forward"))
1659 page = currentpage + 1;
1661 else if(strstr(s, "prev") || strstr(s, "back"))
1663 page = currentpage - 1;
1665 else if(strstr(s, "last") || strstr(s, "end"))
1667 if(this->page2page && this->num_pages) {
1668 page = this->page2page[this->num_pages-1];
1671 else if(strstr(s, "first") || strstr(s, "top"))
1679 case actionLaunch: {
1681 LinkLaunch*l = (LinkLaunch*)action;
1682 GString * str = new GString(l->getFileName());
1683 GString * params = l->getParams();
1685 str->append(params);
1686 s = strdup(str->getCString());
1693 LinkURI*l = (LinkURI*)action;
1694 GString*g = l->getURI();
1696 url = g->getCString();
1701 case actionUnknown: {
1703 LinkUnknown*l = (LinkUnknown*)action;
1708 msg("<error> Unknown link type!");
1713 if(!s) s = strdup("-?-");
1715 msg("<trace> drawlink s=%s", s);
1717 if(!gfxglobals->linkinfo && (page || s))
1719 msg("<notice> File contains links");
1720 gfxglobals->linkinfo = 1;
1726 for(t=1;t<=this->num_pages;t++) {
1727 if(this->page2page[t]==page) {
1737 sprintf(buf, "page%d", lpage);
1738 device->drawlink(device, points, buf);
1742 device->drawlink(device, points, s);
1745 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1749 void GFXOutputDev::saveState(GfxState *state) {
1750 dbg("saveState %08x", state); dbgindent+=2;
1752 msg("<trace> saveState %08x", state);
1755 msg("<fatal> Too many nested states in pdf.");
1759 states[statepos].state = state;
1760 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1761 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1762 states[statepos].clipping = 0;
1763 states[statepos].olddevice = 0;
1764 states[statepos].clipbbox = states[statepos-1].clipbbox;
1767 void GFXOutputDev::restoreState(GfxState *state) {
1768 dbgindent-=2; dbg("restoreState %08x", state);
1771 msg("<fatal> Invalid restoreState");
1774 msg("<trace> restoreState %08x%s%s", state,
1775 states[statepos].softmask?" (end softmask)":"",
1776 states[statepos].clipping?" (end clipping)":"");
1777 if(states[statepos].softmask) {
1778 clearSoftMask(state);
1782 while(states[statepos].clipping) {
1783 device->endclip(device);
1784 states[statepos].clipping--;
1786 if(states[statepos].state!=state) {
1787 msg("<fatal> bad state nesting");
1790 states[statepos].state=0;
1794 void GFXOutputDev::updateLineDash(GfxState *state)
1796 if(this->dashPattern) {
1797 free(this->dashPattern);this->dashPattern = 0;
1799 double *pattern = 0;
1800 state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1801 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1802 if(!this->dashLength) {
1803 this->dashPattern = 0;
1805 double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1806 memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1807 this->dashPattern = p;
1811 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1813 this->page2page = page2page;
1814 this->num_pages = num_pages;
1817 void GFXOutputDev::updateLineWidth(GfxState *state)
1819 double width = state->getTransformedLineWidth();
1822 void GFXOutputDev::updateLineCap(GfxState *state)
1824 int c = state->getLineCap();
1827 void GFXOutputDev::updateLineJoin(GfxState *state)
1829 int j = state->getLineJoin();
1832 void GFXOutputDev::updateFillColor(GfxState *state)
1835 double opaq = state->getFillOpacity();
1836 state->getFillRGB(&rgb);
1838 void GFXOutputDev::updateFillOpacity(GfxState *state)
1841 double opaq = state->getFillOpacity();
1842 state->getFillRGB(&rgb);
1843 dbg("update fillopaq %f", opaq);
1845 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1847 double opaq = state->getFillOpacity();
1848 dbg("update strokeopaq %f", opaq);
1850 void GFXOutputDev::updateFillOverprint(GfxState *state)
1852 double opaq = state->getFillOverprint();
1853 dbg("update filloverprint %f", opaq);
1855 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1857 double opaq = state->getStrokeOverprint();
1858 dbg("update strokeoverprint %f", opaq);
1860 void GFXOutputDev::updateTransfer(GfxState *state)
1862 dbg("update transfer");
1866 void GFXOutputDev::updateStrokeColor(GfxState *state)
1869 double opaq = state->getStrokeOpacity();
1870 state->getStrokeRGB(&rgb);
1873 void GFXOutputDev::updateFont(GfxState *state)
1875 GfxFont* gfxFont = state->getFont();
1879 char*id = getFontID(gfxFont);
1880 msg("<verbose> Updating font to %s", id);
1881 if(gfxFont->getType() == fontType3) {
1882 infofeature("Type3 fonts");
1883 if(!config_extrafontdata) {
1888 msg("<error> Internal Error: FontID is null");
1892 this->current_fontinfo = this->info->getFont(id);
1894 if(!this->current_fontinfo) {
1895 msg("<error> Internal Error: no fontinfo for font %s", id);
1898 if(!this->current_fontinfo->seen) {
1899 dumpFontInfo("<verbose>", gfxFont);
1902 current_gfxfont = this->current_fontinfo->getGfxFont();
1903 device->addfont(device, current_gfxfont);
1906 updateFontMatrix(state);
1909 #define SQR(x) ((x)*(x))
1911 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1913 if((newwidth<1 || newheight<1) ||
1914 (width<=newwidth || height<=newheight))
1916 unsigned char*newdata;
1918 newdata= (unsigned char*)malloc(newwidth*newheight);
1919 double fx = ((double)width)/newwidth;
1920 double fy = ((double)height)/newheight;
1922 int blocksize = (int)(8192/(fx*fy));
1923 int r = 8192*256/palettesize;
1924 for(x=0;x<newwidth;x++) {
1925 double ex = px + fx;
1926 int fromx = (int)px;
1928 int xweight1 = (int)((1-(px-fromx))*256);
1929 int xweight2 = (int)((ex-tox)*256);
1931 for(y=0;y<newheight;y++) {
1932 double ey = py + fy;
1933 int fromy = (int)py;
1935 int yweight1 = (int)((1-(py-fromy))*256);
1936 int yweight2 = (int)((ey-toy)*256);
1943 for(xx=fromx;xx<=tox;xx++)
1944 for(yy=fromy;yy<=toy;yy++) {
1945 int b = 1-data[width*yy+xx];
1947 if(xx==fromx) weight = (weight*xweight1)/256;
1948 if(xx==tox) weight = (weight*xweight2)/256;
1949 if(yy==fromy) weight = (weight*yweight1)/256;
1950 if(yy==toy) weight = (weight*yweight2)/256;
1953 //if(a) a=(palettesize-1)*r/blocksize;
1954 newdata[y*newwidth+x] = (a*blocksize)/r;
1962 #define IMAGE_TYPE_JPEG 0
1963 #define IMAGE_TYPE_LOSSLESS 1
1965 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1966 double x1,double y1,
1967 double x2,double y2,
1968 double x3,double y3,
1969 double x4,double y4, int type, int multiply)
1971 gfxcolor_t*newpic=0;
1973 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1974 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1976 gfxline_t p1,p2,p3,p4,p5;
1977 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1978 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1979 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1980 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1981 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1983 {p1.x = (int)(p1.x*20)/20.0;
1984 p1.y = (int)(p1.y*20)/20.0;
1985 p2.x = (int)(p2.x*20)/20.0;
1986 p2.y = (int)(p2.y*20)/20.0;
1987 p3.x = (int)(p3.x*20)/20.0;
1988 p3.y = (int)(p3.y*20)/20.0;
1989 p4.x = (int)(p4.x*20)/20.0;
1990 p4.y = (int)(p4.y*20)/20.0;
1991 p5.x = (int)(p5.x*20)/20.0;
1992 p5.y = (int)(p5.y*20)/20.0;
1996 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1997 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1999 m.tx = p1.x - 0.5*multiply;
2000 m.ty = p1.y - 0.5*multiply;
2003 img.data = (gfxcolor_t*)data;
2007 if(type == IMAGE_TYPE_JPEG)
2008 /* TODO: pass image_dpi to device instead */
2009 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2012 dev->fillbitmap(dev, &p1, &img, &m, 0);
2015 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2016 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2018 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2021 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2022 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2024 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2028 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2029 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2030 GBool inlineImg, int mask, int*maskColors,
2031 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2033 /* the code in this function is *old*. It's not pretty, but it works. */
2035 double x1,y1,x2,y2,x3,y3,x4,y4;
2036 ImageStream *imgStr;
2041 unsigned char* maskbitmap = 0;
2044 ncomps = colorMap->getNumPixelComps();
2045 bits = colorMap->getBits();
2050 unsigned char buf[8];
2051 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2053 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2054 imgMaskStr->reset();
2055 unsigned char pal[256];
2056 int n = 1 << colorMap->getBits();
2061 maskColorMap->getGray(pixBuf, &gray);
2062 pal[t] = colToByte(gray);
2064 for (y = 0; y < maskHeight; y++) {
2065 for (x = 0; x < maskWidth; x++) {
2066 imgMaskStr->getPixel(buf);
2067 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2072 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2073 imgMaskStr->reset();
2074 for (y = 0; y < maskHeight; y++) {
2075 for (x = 0; x < maskWidth; x++) {
2076 imgMaskStr->getPixel(buf);
2078 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2086 imgStr = new ImageStream(str, width, ncomps,bits);
2089 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2091 msg("<verbose> Ignoring %d by %d image", width, height);
2092 unsigned char buf[8];
2094 for (y = 0; y < height; ++y)
2095 for (x = 0; x < width; ++x) {
2096 imgStr->getPixel(buf);
2104 this->transformXY(state, 0, 1, &x1, &y1);
2105 this->transformXY(state, 0, 0, &x2, &y2);
2106 this->transformXY(state, 1, 0, &x3, &y3);
2107 this->transformXY(state, 1, 1, &x4, &y4);
2110 /* as type 3 bitmaps are antialized, we need to place them
2111 at integer coordinates, otherwise flash player's antializing
2112 will kick in and make everything blurry */
2113 x1 = (int)(x1);y1 = (int)(y1);
2114 x2 = (int)(x2);y2 = (int)(y2);
2115 x3 = (int)(x3);y3 = (int)(y3);
2116 x4 = (int)(x4);y4 = (int)(y4);
2119 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2121 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2122 gfxglobals->pbminfo = 1;
2125 msg("<verbose> drawing %d by %d masked picture", width, height);
2127 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2128 msg("<notice> File contains jpeg pictures");
2129 gfxglobals->jpeginfo = 1;
2133 unsigned char buf[8];
2135 unsigned char*pic = new unsigned char[width*height];
2136 gfxcolor_t pal[256];
2138 state->getFillRGB(&rgb);
2140 memset(pal,255,sizeof(pal));
2141 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2142 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2143 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2144 pal[0].a = 255; pal[1].a = 0;
2147 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2148 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2149 for (y = 0; y < height; ++y)
2150 for (x = 0; x < width; ++x)
2152 imgStr->getPixel(buf);
2155 pic[width*y+x] = buf[0];
2159 unsigned char*pic2 = 0;
2162 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2171 height = realheight;
2175 /* make a black/white palette */
2177 float r = 255./(float)(numpalette-1);
2179 for(t=0;t<numpalette;t++) {
2180 pal[t].r = colToByte(rgb.r);
2181 pal[t].g = colToByte(rgb.g);
2182 pal[t].b = colToByte(rgb.b);
2183 pal[t].a = (unsigned char)(t*r);
2188 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2189 for (y = 0; y < height; ++y) {
2190 for (x = 0; x < width; ++x) {
2191 pic2[width*y+x] = pal[pic[y*width+x]];
2194 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2198 if(maskbitmap) free(maskbitmap);
2204 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2205 gfxcolor_t*pic=new gfxcolor_t[width*height];
2206 for (y = 0; y < height; ++y) {
2207 for (x = 0; x < width; ++x) {
2208 imgStr->getPixel(pixBuf);
2209 colorMap->getRGB(pixBuf, &rgb);
2210 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2211 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2212 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2213 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2215 int x1 = x*maskWidth/width;
2216 int y1 = y*maskHeight/height;
2217 int x2 = (x+1)*maskWidth/width;
2218 int y2 = (y+1)*maskHeight/height;
2220 unsigned int alpha=0;
2221 unsigned int count=0;
2222 for(xx=x1;xx<x2;xx++)
2223 for(yy=y1;yy<y2;yy++) {
2224 alpha += maskbitmap[yy*maskWidth+xx];
2228 pic[width*y+x].a = alpha / count;
2230 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2235 if(str->getKind()==strDCT)
2236 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2238 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2241 if(maskbitmap) free(maskbitmap);
2244 gfxcolor_t*pic=new gfxcolor_t[width*height];
2245 gfxcolor_t pal[256];
2246 int n = 1 << colorMap->getBits();
2248 for(t=0;t<256;t++) {
2250 colorMap->getRGB(pixBuf, &rgb);
2252 {/*if(maskColors && *maskColors==t) {
2253 msg("<notice> Color %d is transparent", t);
2254 if (imgData->maskColors) {
2256 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2257 if (pix[i] < imgData->maskColors[2*i] ||
2258 pix[i] > imgData->maskColors[2*i+1]) {
2273 pal[t].r = (unsigned char)(colToByte(rgb.r));
2274 pal[t].g = (unsigned char)(colToByte(rgb.g));
2275 pal[t].b = (unsigned char)(colToByte(rgb.b));
2276 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2279 for (y = 0; y < height; ++y) {
2280 for (x = 0; x < width; ++x) {
2281 imgStr->getPixel(pixBuf);
2282 pic[width*y+x] = pal[pixBuf[0]];
2286 if(maskWidth < width && maskHeight < height) {
2287 for(y = 0; y < height; y++) {
2288 for (x = 0; x < width; x++) {
2289 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2293 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2294 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2295 double dx = width / maskWidth;
2296 double dy = height / maskHeight;
2298 for(y = 0; y < maskHeight; y++) {
2300 for (x = 0; x < maskWidth; x++) {
2301 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2302 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2310 height = maskHeight;
2313 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2317 if(maskbitmap) free(maskbitmap);
2322 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2323 int width, int height, GBool invert,
2326 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2327 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2328 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2331 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2332 int width, int height, GfxImageColorMap *colorMap,
2333 int *maskColors, GBool inlineImg)
2335 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2336 colorMap?"colorMap":"no colorMap",
2337 maskColors?"maskColors":"no maskColors",
2339 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2340 colorMap?"colorMap":"no colorMap",
2341 maskColors?"maskColors":"no maskColors",
2344 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2345 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2346 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2349 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2350 int width, int height,
2351 GfxImageColorMap *colorMap,
2352 Stream *maskStr, int maskWidth, int maskHeight,
2355 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2356 colorMap?"colorMap":"no colorMap",
2357 maskWidth, maskHeight);
2358 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2359 colorMap?"colorMap":"no colorMap",
2360 maskWidth, maskHeight);
2362 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2363 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2364 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2367 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2368 int width, int height,
2369 GfxImageColorMap *colorMap,
2371 int maskWidth, int maskHeight,
2372 GfxImageColorMap *maskColorMap)
2374 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2375 colorMap?"colorMap":"no colorMap",
2376 maskWidth, maskHeight);
2377 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2378 colorMap?"colorMap":"no colorMap",
2379 maskWidth, maskHeight);
2381 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2382 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2383 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2386 void GFXOutputDev::stroke(GfxState *state)
2390 GfxPath * path = state->getPath();
2391 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2392 strokeGfxline(state, line, 0);
2396 void GFXOutputDev::fill(GfxState *state)
2398 gfxcolor_t col = getFillColor(state);
2399 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2401 GfxPath * path = state->getPath();
2402 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2403 if(config_optimize_polygons) {
2404 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2408 fillGfxLine(state, line);
2412 void GFXOutputDev::eoFill(GfxState *state)
2414 gfxcolor_t col = getFillColor(state);
2415 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2417 GfxPath * path = state->getPath();
2418 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2419 fillGfxLine(state, line);
2424 static const char* dirseparator()
2433 void addGlobalFont(const char*filename)
2435 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2436 memset(f, 0, sizeof(fontfile_t));
2437 f->filename = filename;
2438 int len = strlen(filename);
2439 char*r1 = strrchr(filename, '/');
2440 char*r2 = strrchr(filename, '\\');
2448 msg("<notice> Adding font \"%s\".", filename);
2449 if(global_fonts_next) {
2450 global_fonts_next->next = f;
2451 global_fonts_next = global_fonts_next->next;
2453 global_fonts_next = global_fonts = f;
2457 void addGlobalLanguageDir(const char*dir)
2459 msg("<notice> Adding %s to language pack directories", dir);
2462 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2463 strcpy(config_file, dir);
2464 strcat(config_file, dirseparator());
2465 strcat(config_file, "add-to-xpdfrc");
2467 fi = fopen(config_file, "rb");
2469 msg("<error> Could not open %s", config_file);
2472 globalParams->parseFile(new GString(config_file), fi);
2476 void addGlobalFontDir(const char*dirname)
2478 #ifdef HAVE_DIRENT_H
2479 msg("<notice> Adding %s to font directories", dirname);
2480 lastfontdir = strdup(dirname);
2481 DIR*dir = opendir(dirname);
2483 msg("<warning> Couldn't open directory %s", dirname);
2488 ent = readdir (dir);
2492 char*name = ent->d_name;
2498 if(!strncasecmp(&name[l-4], ".pfa", 4))
2500 if(!strncasecmp(&name[l-4], ".pfb", 4))
2502 if(!strncasecmp(&name[l-4], ".ttf", 4))
2505 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2506 strcpy(fontname, dirname);
2507 strcat(fontname, dirseparator());
2508 strcat(fontname, name);
2509 addGlobalFont(fontname);
2514 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2518 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2519 GfxColorSpace *blendingColorSpace,
2520 GBool isolated, GBool knockout,
2523 const char*colormodename = "";
2525 if(blendingColorSpace) {
2526 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2528 dbg("beginTransparencyGroup device=%08x %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2529 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);
2531 //states[statepos].createsoftmask |= forSoftMask;
2532 states[statepos].createsoftmask = forSoftMask;
2533 states[statepos].transparencygroup = !forSoftMask;
2534 states[statepos].isolated = isolated;
2536 states[statepos].olddevice = this->device;
2537 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2538 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2540 gfxdevice_record_init(this->device);
2542 /*if(!forSoftMask) { ////???
2543 state->setFillOpacity(0.0);
2548 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2551 gfxdevice_t*r = this->device;
2553 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2555 this->device = states[statepos].olddevice;
2557 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2558 /* if these errors occur more often, we should build a seperate
2559 transparency group stack, like xpdf/SplashOutputDev.cc does */
2560 restoreState(state);
2561 this->device = states[statepos].olddevice;
2563 states[statepos].olddevice = 0;
2565 gfxresult_t*recording = r->finish(r);
2567 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2568 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2570 if(states[statepos].createsoftmask) {
2571 states[statepos-1].softmaskrecording = recording;
2573 states[statepos-1].grouprecording = recording;
2576 states[statepos].createsoftmask = 0;
2577 states[statepos].transparencygroup = 0;
2581 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2583 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2584 "colordodge","colorburn","hardlight","softlight","difference",
2585 "exclusion","hue","saturation","color","luminosity"};
2587 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2588 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2590 if(state->getBlendMode() == gfxBlendNormal)
2591 infofeature("transparency groups");
2594 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2595 warnfeature(buffer, 0);
2598 gfxresult_t*grouprecording = states[statepos].grouprecording;
2600 int blendmode = state->getBlendMode();
2601 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2602 int alpha = (int)(state->getFillOpacity()*255);
2603 if(blendmode == gfxBlendMultiply && alpha>200)
2606 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2607 gfxdevice_ops_init(&ops, this->device, alpha);
2608 gfxresult_record_replay(grouprecording, &ops);
2611 grouprecording->destroy(grouprecording);
2613 states[statepos].grouprecording = 0;
2616 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2618 if(states[statepos].softmask) {
2619 /* shouldn't happen, but *does* happen */
2620 clearSoftMask(state);
2623 /* alpha = 1: retrieve mask values from alpha layer
2624 alpha = 0: retrieve mask values from luminance */
2626 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2627 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2628 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2629 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2631 infofeature("soft masks");
2633 warnfeature("soft masks from alpha channel",0);
2635 if(states[statepos].olddevice) {
2636 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2639 states[statepos].olddevice = this->device;
2640 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2641 gfxdevice_record_init(this->device);
2643 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2645 states[statepos].softmask = 1;
2646 states[statepos].softmask_alpha = alpha;
2649 static inline Guchar div255(int x) {
2650 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2653 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2655 if(c < min) c = min;
2656 if(c > max) c = max;
2660 void GFXOutputDev::clearSoftMask(GfxState *state)
2662 if(!states[statepos].softmask)
2664 states[statepos].softmask = 0;
2665 dbg("clearSoftMask statepos=%d", statepos);
2666 msg("<verbose> clearSoftMask statepos=%d", statepos);
2668 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2669 msg("<error> Error in softmask/tgroup ordering");
2673 gfxresult_t*mask = states[statepos].softmaskrecording;
2674 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2675 this->device = states[statepos].olddevice;
2677 /* get outline of all objects below the soft mask */
2678 gfxdevice_t uniondev;
2679 gfxdevice_union_init(&uniondev, 0);
2680 gfxresult_record_replay(below, &uniondev);
2681 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2682 uniondev.finish(&uniondev);
2683 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2684 gfxline_free(belowoutline);belowoutline=0;
2686 this->device->startclip(this->device, belowoutline);
2687 gfxresult_record_replay(below, this->device);
2688 gfxresult_record_replay(mask, this->device);
2689 this->device->endclip(this->device);
2692 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2693 if(width<=0 || height<=0)
2696 gfxdevice_t belowrender;
2697 gfxdevice_render_init(&belowrender);
2698 if(states[statepos+1].isolated) {
2699 belowrender.setparameter(&belowrender, "fillwhite", "1");
2701 belowrender.setparameter(&belowrender, "antialize", "2");
2702 belowrender.startpage(&belowrender, width, height);
2703 gfxresult_record_replay(below, &belowrender);
2704 belowrender.endpage(&belowrender);
2705 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2706 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2707 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2709 gfxdevice_t maskrender;
2710 gfxdevice_render_init(&maskrender);
2711 maskrender.startpage(&maskrender, width, height);
2712 gfxresult_record_replay(mask, &maskrender);
2713 maskrender.endpage(&maskrender);
2714 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2715 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2717 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2718 msg("<fatal> Internal error in mask drawing");
2723 for(y=0;y<height;y++) {
2724 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2725 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2726 for(x=0;x<width;x++) {
2728 if(states[statepos].softmask_alpha) {
2731 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2734 l2->a = div255(alpha*l2->a);
2736 /* DON'T premultiply alpha- this is done by fillbitmap,
2737 depending on the output device */
2738 //l2->r = div255(alpha*l2->r);
2739 //l2->g = div255(alpha*l2->g);
2740 //l2->b = div255(alpha*l2->b);
2746 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2749 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2750 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2752 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2754 mask->destroy(mask);
2755 below->destroy(below);
2756 maskresult->destroy(maskresult);
2757 belowresult->destroy(belowresult);
2758 states[statepos].softmaskrecording = 0;
2763 // public: ~MemCheck()
2765 // delete globalParams;globalParams=0;
2766 // Object::memCheck(stderr);
2767 // gMemReport(stderr);