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;
155 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
157 feature_t*f = this->featurewarnings;
159 if(!strcmp(feature, f->string))
163 f = (feature_t*)malloc(sizeof(feature_t));
164 f->string = strdup(feature);
165 f->next = this->featurewarnings;
166 this->featurewarnings = f;
168 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
169 if(this->config_break_on_warning) {
170 msg("<fatal> Aborting conversion due to unsupported feature");
174 msg("<notice> File contains %s",feature);
177 void GFXOutputDev::warnfeature(const char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(const char*feature)
183 showfeature(feature,0,0);
186 GFXOutputState::GFXOutputState() {
188 this->createsoftmask = 0;
189 this->transparencygroup = 0;
191 this->grouprecording = 0;
195 GBool GFXOutputDev::interpretType3Chars()
200 typedef struct _drawnchar
218 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
219 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224 free(chars);chars = 0;
231 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
235 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
238 chars[num_chars].x = x;
239 chars[num_chars].y = y;
240 chars[num_chars].color = color;
241 chars[num_chars].charid = charid;
245 char* writeOutStdFont(fontentry* f)
250 char* tmpFileName = mktmpname(namebuf1);
252 sprintf(namebuf2, "%s.afm", tmpFileName);
253 fi = fopen(namebuf2, "wb");
256 fwrite(f->afm, 1, f->afmlen, fi);
259 sprintf(namebuf2, "%s.pfb", tmpFileName);
260 fi = fopen(namebuf2, "wb");
263 fwrite(f->pfb, 1, f->pfblen, fi);
265 return strdup(namebuf2);
267 void unlinkfont(char* filename)
272 msg("<verbose> Removing temporary font file %s", filename);
275 if(!strncmp(&filename[l-4],".afm",4)) {
276 memcpy(&filename[l-4],".pfb",4); unlink(filename);
277 memcpy(&filename[l-4],".pfa",4); unlink(filename);
278 memcpy(&filename[l-4],".afm",4);
281 if(!strncmp(&filename[l-4],".pfa",4)) {
282 memcpy(&filename[l-4],".afm",4); unlink(filename);
283 memcpy(&filename[l-4],".pfa",4);
286 if(!strncmp(&filename[l-4],".pfb",4)) {
287 memcpy(&filename[l-4],".afm",4); unlink(filename);
288 memcpy(&filename[l-4],".pfb",4);
293 static int config_use_fontconfig = 1;
294 static int fcinitcalled = 0;
296 GFXGlobalParams::GFXGlobalParams()
299 //setupBaseFonts(char *dir); //not tested yet
301 GFXGlobalParams::~GFXGlobalParams()
303 msg("<verbose> Performing cleanups");
305 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
306 if(pdf2t1map[t].fullfilename) {
307 unlinkfont(pdf2t1map[t].fullfilename);
310 #ifdef HAVE_FONTCONFIG
311 if(config_use_fontconfig && fcinitcalled)
315 #ifdef HAVE_FONTCONFIG
316 static char fc_ismatch(FcPattern*match, char*family, char*style)
318 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
319 FcBool scalable=FcFalse, outline=FcFalse;
320 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
321 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
322 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
323 FcPatternGetBool(match, "outline", 0, &outline);
324 FcPatternGetBool(match, "scalable", 0, &scalable);
326 if(scalable!=FcTrue || outline!=FcTrue)
329 if (!strcasecmp(fcfamily, family)) {
330 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
333 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
339 char* fontconfig_searchForFont(char*name)
341 #ifdef HAVE_FONTCONFIG
342 if(!config_use_fontconfig)
345 // call init ony once
349 // check whether we have a config file
350 char* configfile = (char*)FcConfigFilename(0);
351 int configexists = 0;
352 FILE*fi = fopen(configfile, "rb");
354 configexists = 1;fclose(fi);
355 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
357 msg("<debug> Initializing FontConfig (no configfile)");
361 /* A fontconfig instance which didn't find a configfile is unbelievably
362 cranky, so let's just write out a small xml file and make fontconfig
364 FcConfig*c = FcConfigCreate();
366 char* tmpFileName = mktmpname(namebuf);
367 FILE*fi = fopen(tmpFileName, "wb");
368 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
370 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
372 fprintf(fi, "<dir>~/.fonts</dir>\n");
374 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
376 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
377 fprintf(fi, "</fontconfig>\n");
379 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
380 FcConfigBuildFonts(c);
381 FcConfigSetCurrent(c);
385 msg("<debug> FontConfig Initialization failed. Disabling.");
386 config_use_fontconfig = 0;
389 FcConfig * config = FcConfigGetCurrent();
391 msg("<debug> FontConfig Config Initialization failed. Disabling.");
392 config_use_fontconfig = 0;
395 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
396 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
397 if(!set || !set->nfont) {
398 msg("<debug> FontConfig has zero fonts. Disabling.");
399 config_use_fontconfig = 0;
403 if(getLogLevel() >= LOGLEVEL_TRACE) {
405 for(t=0;t<set->nfont;t++) {
406 char*fcfamily=0,*fcstyle=0,*filename=0;
407 FcBool scalable=FcFalse, outline=FcFalse;
408 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
409 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
410 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
411 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
412 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
413 if(scalable && outline) {
414 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
420 char*family = strdup(name);
422 char*dash = strchr(family, '-');
423 FcPattern*pattern = 0;
427 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
428 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
430 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
431 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
435 FcConfigSubstitute(0, pattern, FcMatchPattern);
436 FcDefaultSubstitute(pattern);
438 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
441 for(t=0;t<set->nfont;t++) {
442 FcPattern*match = set->fonts[t];
443 //FcPattern*match = FcFontMatch(0, pattern, &result);
444 if(fc_ismatch(match, family, style)) {
446 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
447 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
450 //FcPatternDestroy(match);
451 msg("<debug> fontconfig: returning filename %s", filename);
453 FcPatternDestroy(pattern);
454 FcFontSetDestroy(set);
455 return filename?strdup(filename):0;
460 FcPatternDestroy(pattern);
461 FcFontSetDestroy(set);
468 static DisplayFontParamKind detectFontType(const char*filename)
470 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
471 return displayFontTT;
472 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
473 return displayFontT1;
474 return displayFontTT;
477 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
479 msg("<verbose> looking for font %s in global params", fontName->getCString());
481 char*name = fontName->getCString();
483 /* see if it is a pdf standard font */
485 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
486 if(!strcmp(name, pdf2t1map[t].pdffont)) {
487 if(!pdf2t1map[t].fullfilename) {
488 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
489 if(!pdf2t1map[t].fullfilename) {
490 msg("<error> Couldn't save default font- is the Temp Directory writable?");
492 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
495 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
496 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
501 int bestlen = 0x7fffffff;
502 const char*bestfilename = 0;
504 fontfile_t*f = global_fonts;
506 if(strstr(f->filename, name)) {
507 if(f->len < bestlen) {
509 bestfilename = f->filename;
515 /* if we didn't find anything up to now, try looking for the
516 font via fontconfig */
519 filename = fontconfig_searchForFont(name);
521 filename = strdup(bestfilename);
525 DisplayFontParamKind kind = detectFontType(filename);
526 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
527 if(kind == displayFontTT) {
528 dfp->tt.fileName = new GString(filename);
530 dfp->t1.fileName = new GString(filename);
535 return GlobalParams::getDisplayFont(fontName);
538 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
542 this->xref = doc->getXRef();
545 this->textmodeinfo = 0;
548 this->type3active = 0;
551 this->substitutepos = 0;
552 this->type3Warning = 0;
553 this->user_movex = 0;
554 this->user_movey = 0;
557 this->user_clipx1 = 0;
558 this->user_clipy1 = 0;
559 this->user_clipx2 = 0;
560 this->user_clipy2 = 0;
561 this->current_text_stroke = 0;
562 this->current_text_clip = 0;
563 this->outer_clip_box = 0;
565 this->pagebuflen = 0;
567 this->config_bigchar=0;
568 this->config_convertgradients=1;
569 this->config_break_on_warning=0;
570 this->config_remapunicode=0;
571 this->config_transparent=0;
572 this->config_extrafontdata = 0;
573 this->config_fontquality = 10;
574 this->config_optimize_polygons = 0;
576 this->gfxfontlist = gfxfontlist_create();
578 memset(states, 0, sizeof(states));
579 this->featurewarnings = 0;
582 void GFXOutputDev::setParameter(const char*key, const char*value)
584 if(!strcmp(key,"breakonwarning")) {
585 this->config_break_on_warning = atoi(value);
586 } else if(!strcmp(key,"remapunicode")) {
587 this->config_remapunicode = atoi(value);
588 } else if(!strcmp(key,"transparent")) {
589 this->config_transparent = atoi(value);
590 } else if(!strcmp(key,"extrafontdata")) {
591 this->config_extrafontdata = atoi(value);
592 } else if(!strcmp(key,"convertgradients")) {
593 this->config_convertgradients = atoi(value);
594 } else if(!strcmp(key,"optimize_polygons")) {
595 this->config_optimize_polygons = atoi(value);
596 } else if(!strcmp(key,"bigchar")) {
597 this->config_bigchar = atoi(value);
598 } else if(!strcmp(key,"fontquality")) {
599 this->config_fontquality = atof(value);
600 if(this->config_fontquality<=1)
601 this->config_fontquality=1;
606 void GFXOutputDev::setDevice(gfxdevice_t*dev)
611 void GFXOutputDev::setMove(int x,int y)
613 this->user_movex = x;
614 this->user_movey = y;
617 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
619 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
620 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
622 this->user_clipx1 = x1;
623 this->user_clipy1 = y1;
624 this->user_clipx2 = x2;
625 this->user_clipy2 = y2;
628 static char*getFontName(GfxFont*font)
631 GString*gstr = font->getName();
632 char* fname = gstr==0?0:gstr->getCString();
636 sprintf(buf, "UFONT%d", r->num);
637 fontid = strdup(buf);
639 fontid = strdup(fname);
643 char* plus = strchr(fontid, '+');
644 if(plus && plus < &fontid[strlen(fontid)-1]) {
645 fontname = strdup(plus+1);
647 fontname = strdup(fontid);
653 static void dumpFontInfo(const char*loglevel, GfxFont*font);
654 static int lastdumps[1024];
655 static int lastdumppos = 0;
660 static void showFontError(GfxFont*font, int nr)
664 for(t=0;t<lastdumppos;t++)
665 if(lastdumps[t] == r->num)
669 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
670 lastdumps[lastdumppos++] = r->num;
672 msg("<warning> The following font caused problems:");
674 msg("<warning> The following font caused problems (substituting):");
676 msg("<warning> The following Type 3 Font will be rendered as graphics:");
677 dumpFontInfo("<warning>", font);
680 static void dumpFontInfo(const char*loglevel, GfxFont*font)
682 char* id = getFontID(font);
683 char* name = getFontName(font);
684 Ref* r=font->getID();
685 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
687 GString*gstr = font->getTag();
689 msg("%s| Tag: %s", loglevel, id);
691 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
693 GfxFontType type=font->getType();
695 case fontUnknownType:
696 msg("%s| Type: unknown",loglevel);
699 msg("%s| Type: 1",loglevel);
702 msg("%s| Type: 1C",loglevel);
705 msg("%s| Type: 3",loglevel);
708 msg("%s| Type: TrueType",loglevel);
711 msg("%s| Type: CIDType0",loglevel);
714 msg("%s| Type: CIDType0C",loglevel);
717 msg("%s| Type: CIDType2",loglevel);
722 GBool embedded = font->getEmbeddedFontID(&embRef);
724 if(font->getEmbeddedFontName()) {
725 embeddedName = font->getEmbeddedFontName()->getCString();
728 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
730 gstr = font->getExtFontFile();
732 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
734 // Get font descriptor flags.
735 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
736 if(font->isSerif()) msg("%s| is serif", loglevel);
737 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
738 if(font->isItalic()) msg("%s| is italic", loglevel);
739 if(font->isBold()) msg("%s| is bold", loglevel);
745 //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");}
746 //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");}
748 void dump_outline(gfxline_t*line)
751 if(line->type == gfx_moveTo) {
752 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
753 } else if(line->type == gfx_lineTo) {
754 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
755 } else if(line->type == gfx_splineTo) {
756 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
762 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
764 int num = path->getNumSubpaths();
767 double lastx=0,lasty=0,posx=0,posy=0;
770 msg("<warning> empty path");
774 gfxdrawer_target_gfxline(&draw);
776 for(t = 0; t < num; t++) {
777 GfxSubpath *subpath = path->getSubpath(t);
778 int subnum = subpath->getNumPoints();
779 double bx=0,by=0,cx=0,cy=0;
781 for(s=0;s<subnum;s++) {
784 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
787 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
788 draw.lineTo(&draw, lastx, lasty);
790 draw.moveTo(&draw, x,y);
795 } else if(subpath->getCurve(s) && cpos==0) {
799 } else if(subpath->getCurve(s) && cpos==1) {
807 draw.lineTo(&draw, x,y);
809 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
816 /* fix non-closed lines */
817 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
818 draw.lineTo(&draw, lastx, lasty);
820 gfxline_t*result = (gfxline_t*)draw.result(&draw);
822 gfxline_optimize(result);
827 GBool GFXOutputDev::useTilingPatternFill()
829 infofeature("tiled patterns");
830 // if(config_convertgradients)
834 GBool GFXOutputDev::useShadedFills()
836 infofeature("shaded fills");
837 if(config_convertgradients)
842 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
844 state->transform(x,y,nx,ny);
845 *nx += user_movex + clipmovex;
846 *ny += user_movey + clipmovey;
850 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
851 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
852 int paintType, Dict *resDict,
853 double *mat, double *bbox,
854 int x0, int y0, int x1, int y1,
855 double xStep, double yStep)
857 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
858 int paintType, Dict *resDict,
859 double *mat, double *bbox,
860 int x0, int y0, int x1, int y1,
861 double xStep, double yStep)
864 msg("<debug> tilingPatternFill");
865 infofeature("tiling pattern fills");
868 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
870 msg("<error> functionShadedFill not supported yet");
871 infofeature("function shaded fills");
874 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
878 colspace->getRGB(col, &rgb);
879 c.r = colToByte(rgb.r);
880 c.g = colToByte(rgb.g);
881 c.b = colToByte(rgb.b);
886 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
888 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
889 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
892 this->transformXY(state, x0,y0, &x0,&y0);
893 this->transformXY(state, x1,y1, &x1,&y1);
894 this->transformXY(state, x2,y2, &x2,&y2);
899 shading->getColor(0.0, &color0);
900 shading->getColor(0.5, &color1);
901 shading->getColor(1.0, &color2);
903 GfxColorSpace* colspace = shading->getColorSpace();
905 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
906 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
907 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
908 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
909 infofeature("radial shaded fills");
911 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
915 g[0].color = col2col(colspace, &color0);
916 g[1].color = col2col(colspace, &color1);
917 g[2].color = col2col(colspace, &color2);
922 gfxbbox_t b = states[statepos].clipbbox;
923 gfxline_t p1,p2,p3,p4,p5;
924 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
925 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
926 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
927 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
928 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
931 //m.m00 = (x3-x0); m.m10 = (x1-x0);
932 //m.m01 = (y3-y0); m.m11 = (y1-y0);
933 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
934 m.m00 = (x1-x0); m.m10 = (x2-x0);
935 m.m01 = (y1-y0); m.m11 = (y2-y0);
939 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
943 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
946 shading->getCoords(&x0,&y0,&x1,&y1);
947 this->transformXY(state, x0,y0,&x0,&y0);
948 this->transformXY(state, x1,y1,&x1,&y1);
953 shading->getColor(0.0, &color0);
954 shading->getColor(0.5, &color1);
955 shading->getColor(1.0, &color2);
957 GfxColorSpace* colspace = shading->getColorSpace();
959 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
960 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
961 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
962 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
964 infofeature("axial shaded fills");
966 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
970 g[0].color = col2col(colspace, &color0);
971 g[1].color = col2col(colspace, &color1);
972 g[2].color = col2col(colspace, &color2);
977 double xMin,yMin,xMax,yMax;
978 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
979 this->transformXY(state, xMin, yMin, &xMin, &yMin);
980 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
983 xMin = 1024; yMin = 1024;
985 gfxbbox_t b = states[statepos].clipbbox;
986 gfxline_t p1,p2,p3,p4,p5;
987 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
988 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
989 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
990 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
991 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
993 /* the gradient starts at (-1.0,0.0), so move (0,0) to
994 the middle of the two control points */
996 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
997 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
998 m.tx = (x0 + x1)/2 - 0.5;
999 m.ty = (y0 + y1)/2 - 0.5;
1001 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1005 GBool GFXOutputDev::useDrawForm()
1007 infofeature("forms");
1010 void GFXOutputDev::drawForm(Ref id)
1012 msg("<error> drawForm not implemented");
1014 GBool GFXOutputDev::needNonText()
1018 void GFXOutputDev::endPage()
1020 msg("<verbose> endPage (GfxOutputDev)");
1021 if(outer_clip_box) {
1022 device->endclip(device);
1025 this->dashPattern = 0;
1026 /* notice: we're not fully done yet with this page- there might still be
1027 a few calls to drawLink() yet to come */
1030 static inline double sqr(double x) {return x*x;}
1032 #define STROKE_FILL 1
1033 #define STROKE_CLIP 2
1034 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1036 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1037 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1038 double miterLimit = state->getMiterLimit();
1039 double width = state->getTransformedLineWidth();
1042 double opaq = state->getStrokeOpacity();
1044 state->getFillRGB(&rgb);
1046 state->getStrokeRGB(&rgb);
1048 col.r = colToByte(rgb.r);
1049 col.g = colToByte(rgb.g);
1050 col.b = colToByte(rgb.b);
1051 col.a = (unsigned char)(opaq*255);
1053 gfx_capType capType = gfx_capRound;
1054 if(lineCap == 0) capType = gfx_capButt;
1055 else if(lineCap == 1) capType = gfx_capRound;
1056 else if(lineCap == 2) capType = gfx_capSquare;
1058 gfx_joinType joinType = gfx_joinRound;
1059 if(lineJoin == 0) joinType = gfx_joinMiter;
1060 else if(lineJoin == 1) joinType = gfx_joinRound;
1061 else if(lineJoin == 2) joinType = gfx_joinBevel;
1063 gfxline_t*line2 = 0;
1065 if(this->dashLength && this->dashPattern) {
1066 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1069 /* try to find out how much the transformation matrix would
1070 stretch the dashes, and factor that into the dash lengths.
1071 This is not the entirely correct approach- it would be
1072 better to first convert the path to an unscaled version,
1073 then apply dashing, and then transform the path using
1074 the current transformation matrix. However there are few
1075 PDFs which actually stretch a dashed path in a non-orthonormal
1077 double tx1, ty1, tx2, ty2;
1078 this->transformXY(state, 0, 0, &tx1, &ty1);
1079 this->transformXY(state, 1, 1, &tx2, &ty2);
1080 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1082 msg("<trace> %d dashes", this->dashLength);
1083 msg("<trace> | phase: %f", this->dashStart);
1084 for(t=0;t<this->dashLength;t++) {
1085 dash[t] = (float)this->dashPattern[t] * f;
1086 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1088 dash[this->dashLength] = -1;
1089 if(getLogLevel() >= LOGLEVEL_TRACE) {
1093 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1096 msg("<trace> After dashing:");
1099 if(getLogLevel() >= LOGLEVEL_TRACE) {
1100 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1102 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1103 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1105 col.r,col.g,col.b,col.a
1110 if(flags&STROKE_FILL) {
1111 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1112 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1113 if(getLogLevel() >= LOGLEVEL_TRACE) {
1114 dump_outline(gfxline);
1117 msg("<warning> Empty polygon (resulting from stroked line)");
1119 if(flags&STROKE_CLIP) {
1120 device->startclip(device, gfxline);
1121 states[statepos].clipping++;
1123 device->fill(device, gfxline, &col);
1125 gfxline_free(gfxline);
1128 if(flags&STROKE_CLIP)
1129 msg("<error> Stroke&clip not supported at the same time");
1130 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1134 gfxline_free(line2);
1137 gfxcolor_t getFillColor(GfxState * state)
1140 double opaq = state->getFillOpacity();
1141 state->getFillRGB(&rgb);
1143 col.r = colToByte(rgb.r);
1144 col.g = colToByte(rgb.g);
1145 col.b = colToByte(rgb.b);
1146 col.a = (unsigned char)(opaq*255);
1150 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1152 gfxcolor_t col = getFillColor(state);
1154 if(getLogLevel() >= LOGLEVEL_TRACE) {
1155 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1158 device->fill(device, line, &col);
1161 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1163 if(getLogLevel() >= LOGLEVEL_TRACE) {
1166 gfxbbox_t bbox = gfxline_getbbox(line);
1167 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1169 device->startclip(device, line);
1170 states[statepos].clipping++;
1173 void GFXOutputDev::clip(GfxState *state)
1175 GfxPath * path = state->getPath();
1176 msg("<trace> clip");
1177 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1178 if(config_optimize_polygons) {
1179 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1183 clipToGfxLine(state, line);
1187 void GFXOutputDev::eoClip(GfxState *state)
1189 GfxPath * path = state->getPath();
1190 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1191 clipToGfxLine(state, line);
1194 void GFXOutputDev::clipToStrokePath(GfxState *state)
1196 GfxPath * path = state->getPath();
1197 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1199 if(getLogLevel() >= LOGLEVEL_TRACE) {
1200 double width = state->getTransformedLineWidth();
1201 msg("<trace> cliptostrokepath width=%f", width);
1205 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1209 void GFXOutputDev::finish()
1211 if(outer_clip_box) {
1213 device->endclip(device);
1219 GFXOutputDev::~GFXOutputDev()
1224 free(this->pages); this->pages = 0;
1227 feature_t*f = this->featurewarnings;
1229 feature_t*next = f->next;
1231 free(f->string);f->string =0;
1237 this->featurewarnings = 0;
1239 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1241 GBool GFXOutputDev::upsideDown()
1245 GBool GFXOutputDev::useDrawChar()
1250 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1251 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1253 static char tmp_printstr[4096];
1254 char* makeStringPrintable(char*str)
1256 int len = strlen(str);
1263 for(t=0;t<len;t++) {
1268 tmp_printstr[t] = c;
1271 tmp_printstr[len++] = '.';
1272 tmp_printstr[len++] = '.';
1273 tmp_printstr[len++] = '.';
1275 tmp_printstr[len] = 0;
1276 return tmp_printstr;
1278 #define INTERNAL_FONT_SIZE 1024.0
1279 void GFXOutputDev::updateFontMatrix(GfxState*state)
1281 double* ctm = state->getCTM();
1282 double fontSize = state->getFontSize();
1283 double*textMat = state->getTextMat();
1285 /* taking the absolute value of horizScaling seems to be required for
1286 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1287 double hscale = fabs(state->getHorizScaling());
1289 // from xpdf-3.02/SplashOutputDev:updateFont
1290 double mm11 = textMat[0] * fontSize * hscale;
1291 double mm12 = textMat[1] * fontSize * hscale;
1292 double mm21 = textMat[2] * fontSize;
1293 double mm22 = textMat[3] * fontSize;
1295 // multiply with ctm, like state->getFontTransMat() does
1296 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1297 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1298 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1299 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1300 this->current_font_matrix.tx = 0;
1301 this->current_font_matrix.ty = 0;
1304 void GFXOutputDev::beginString(GfxState *state, GString *s)
1306 int render = state->getRender();
1307 if(current_text_stroke) {
1308 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1311 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1314 static gfxline_t* mkEmptyGfxShape(double x, double y)
1316 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1317 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1321 static char isValidUnicode(int c)
1323 if(c>=32 && c<0x2fffe)
1328 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1329 double dx, double dy,
1330 double originX, double originY,
1331 CharCode charid, int nBytes, Unicode *_u, int uLen)
1333 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1334 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1338 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1340 int render = state->getRender();
1341 gfxcolor_t col = getFillColor(state);
1343 // check for invisible text -- this is used by Acrobat Capture
1344 if (render == RENDER_INVISIBLE) {
1346 if(!config_extrafontdata)
1350 GfxFont*font = state->getFont();
1352 if(font->getType() == fontType3) {
1353 /* type 3 chars are passed as graphics */
1354 msg("<debug> type3 char at %f/%f", x, y);
1358 Unicode u = uLen?(_u[0]):0;
1359 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);
1361 gfxmatrix_t m = this->current_font_matrix;
1362 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1363 m.tx += originX; m.ty += originY;
1365 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1366 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1368 msg("<debug> Drawing glyph %d as shape", charid);
1370 msg("<notice> Some texts will be rendered as shape");
1373 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1374 gfxline_t*tglyph = gfxline_clone(glyph);
1375 gfxline_transform(tglyph, &m);
1376 if((render&3) != RENDER_INVISIBLE) {
1377 gfxline_t*add = gfxline_clone(tglyph);
1378 current_text_stroke = gfxline_append(current_text_stroke, add);
1380 if(render&RENDER_CLIP) {
1381 gfxline_t*add = gfxline_clone(tglyph);
1382 current_text_clip = gfxline_append(current_text_clip, add);
1383 if(!current_text_clip) {
1384 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1387 gfxline_free(tglyph);
1391 void GFXOutputDev::endString(GfxState *state)
1393 int render = state->getRender();
1394 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1396 if(current_text_stroke) {
1397 /* fillstroke and stroke text rendering objects we can process right
1398 now (as there may be texts of other rendering modes in this
1399 text object)- clipping objects have to wait until endTextObject,
1401 device->setparameter(device, "mark","TXT");
1402 if((render&3) == RENDER_FILL) {
1403 fillGfxLine(state, current_text_stroke);
1404 gfxline_free(current_text_stroke);
1405 current_text_stroke = 0;
1406 } else if((render&3) == RENDER_FILLSTROKE) {
1407 fillGfxLine(state, current_text_stroke);
1408 strokeGfxline(state, current_text_stroke,0);
1409 gfxline_free(current_text_stroke);
1410 current_text_stroke = 0;
1411 } else if((render&3) == RENDER_STROKE) {
1412 strokeGfxline(state, current_text_stroke,0);
1413 gfxline_free(current_text_stroke);
1414 current_text_stroke = 0;
1416 device->setparameter(device, "mark","");
1420 void GFXOutputDev::endTextObject(GfxState *state)
1422 int render = state->getRender();
1423 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1425 if(current_text_clip) {
1426 device->setparameter(device, "mark","TXT");
1427 clipToGfxLine(state, current_text_clip);
1428 device->setparameter(device, "mark","");
1429 gfxline_free(current_text_clip);
1430 current_text_clip = 0;
1434 /* the logic seems to be as following:
1435 first, beginType3Char is called, with the charcode and the coordinates.
1436 if this function returns true, it already knew about the char and has now drawn it.
1437 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1438 called with some parameters.
1439 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1440 at the position first passed to beginType3Char). the char ends with endType3Char.
1442 The drawing operations between beginType3Char and endType3Char are somewhat different to
1443 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1444 color determines the color of a font)
1447 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1449 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1452 if(config_extrafontdata && current_fontinfo) {
1454 gfxmatrix_t m = this->current_font_matrix;
1455 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1456 m.m00*=INTERNAL_FONT_SIZE;
1457 m.m01*=INTERNAL_FONT_SIZE;
1458 m.m10*=INTERNAL_FONT_SIZE;
1459 m.m11*=INTERNAL_FONT_SIZE;
1461 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1462 msg("<error> Invalid charid %d for font", charid);
1465 gfxcolor_t col={0,0,0,0};
1466 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1467 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1471 /* the character itself is going to be passed using the draw functions */
1472 return gFalse; /* gTrue= is_in_cache? */
1475 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1477 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1480 void GFXOutputDev::endType3Char(GfxState *state)
1483 msg("<debug> endType3Char");
1486 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1488 this->currentpage = pageNum;
1490 int rot = doc->getPageRotate(1);
1491 gfxcolor_t white = {255,255,255,255};
1492 gfxcolor_t black = {255,0,0,0};
1494 gfxline_t clippath[5];
1496 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1497 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1498 Use CropBox, not MediaBox, as page size
1505 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1506 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1508 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1509 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1511 this->clipmovex = -(int)x1;
1512 this->clipmovey = -(int)y1;
1514 /* apply user clip box */
1515 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1516 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1517 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1518 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1519 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1520 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1523 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1525 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);
1527 msg("<verbose> page is rotated %d degrees", rot);
1529 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1530 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1531 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1532 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1533 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1534 device->startclip(device, clippath); outer_clip_box = 1;
1535 if(!config_transparent) {
1536 device->fill(device, clippath, &white);
1538 states[statepos].clipbbox.xmin = x1;
1539 states[statepos].clipbbox.ymin = x1;
1540 states[statepos].clipbbox.xmax = x2;
1541 states[statepos].clipbbox.ymax = y2;
1543 this->dashPattern = 0;
1544 this->dashLength = 0;
1545 this->dashStart = 0;
1549 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1551 double x1, y1, x2, y2;
1552 gfxline_t points[5];
1555 msg("<debug> drawlink");
1557 link->getRect(&x1, &y1, &x2, &y2);
1558 cvtUserToDev(x1, y1, &x, &y);
1559 points[0].type = gfx_moveTo;
1560 points[0].x = points[4].x = x + user_movex + clipmovex;
1561 points[0].y = points[4].y = y + user_movey + clipmovey;
1562 points[0].next = &points[1];
1563 cvtUserToDev(x2, y1, &x, &y);
1564 points[1].type = gfx_lineTo;
1565 points[1].x = x + user_movex + clipmovex;
1566 points[1].y = y + user_movey + clipmovey;
1567 points[1].next = &points[2];
1568 cvtUserToDev(x2, y2, &x, &y);
1569 points[2].type = gfx_lineTo;
1570 points[2].x = x + user_movex + clipmovex;
1571 points[2].y = y + user_movey + clipmovey;
1572 points[2].next = &points[3];
1573 cvtUserToDev(x1, y2, &x, &y);
1574 points[3].type = gfx_lineTo;
1575 points[3].x = x + user_movex + clipmovex;
1576 points[3].y = y + user_movey + clipmovey;
1577 points[3].next = &points[4];
1578 cvtUserToDev(x1, y1, &x, &y);
1579 points[4].type = gfx_lineTo;
1580 points[4].x = x + user_movex + clipmovex;
1581 points[4].y = y + user_movey + clipmovey;
1584 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1585 points[0].x, points[0].y,
1586 points[1].x, points[1].y,
1587 points[2].x, points[2].y,
1588 points[3].x, points[3].y);
1590 if(getLogLevel() >= LOGLEVEL_TRACE) {
1591 dump_outline(points);
1594 LinkAction*action=link->getAction();
1597 const char*type = "-?-";
1600 msg("<trace> drawlink action=%d", action->getKind());
1601 switch(action->getKind())
1605 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1606 LinkDest *dest=NULL;
1607 if (ha->getDest()==NULL)
1608 dest=catalog->findDest(ha->getNamedDest());
1609 else dest=ha->getDest();
1611 if (dest->isPageRef()){
1612 Ref pageref=dest->getPageRef();
1613 page=catalog->findPage(pageref.num,pageref.gen);
1615 else page=dest->getPageNum();
1616 sprintf(buf, "%d", page);
1623 LinkGoToR*l = (LinkGoToR*)action;
1624 GString*g = l->getFileName();
1626 s = strdup(g->getCString());
1628 /* if the GoToR link has no filename, then
1629 try to find a refernce in the *local*
1631 GString*g = l->getNamedDest();
1633 s = strdup(g->getCString());
1639 LinkNamed*l = (LinkNamed*)action;
1640 GString*name = l->getName();
1642 s = strdup(name->lowerCase()->getCString());
1643 named = name->getCString();
1646 if(strstr(s, "next") || strstr(s, "forward"))
1648 page = currentpage + 1;
1650 else if(strstr(s, "prev") || strstr(s, "back"))
1652 page = currentpage - 1;
1654 else if(strstr(s, "last") || strstr(s, "end"))
1656 if(pages && pagepos>0)
1657 page = pages[pagepos-1];
1659 else if(strstr(s, "first") || strstr(s, "top"))
1667 case actionLaunch: {
1669 LinkLaunch*l = (LinkLaunch*)action;
1670 GString * str = new GString(l->getFileName());
1671 GString * params = l->getParams();
1673 str->append(params);
1674 s = strdup(str->getCString());
1681 LinkURI*l = (LinkURI*)action;
1682 GString*g = l->getURI();
1684 url = g->getCString();
1689 case actionUnknown: {
1691 LinkUnknown*l = (LinkUnknown*)action;
1696 msg("<error> Unknown link type!");
1701 if(!s) s = strdup("-?-");
1703 msg("<trace> drawlink s=%s", s);
1705 if(!linkinfo && (page || s))
1707 msg("<notice> File contains links");
1715 for(t=1;t<=pagepos;t++) {
1716 if(pages[t]==page) {
1725 sprintf(buf, "page%d", lpage);
1726 device->drawlink(device, points, buf);
1730 device->drawlink(device, points, s);
1733 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1737 void GFXOutputDev::saveState(GfxState *state) {
1738 dbg("saveState %08x", state); dbgindent+=2;
1740 msg("<trace> saveState %08x", state);
1743 msg("<fatal> Too many nested states in pdf.");
1747 states[statepos].state = state;
1748 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1749 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1750 states[statepos].clipping = 0;
1751 states[statepos].olddevice = 0;
1752 states[statepos].clipbbox = states[statepos-1].clipbbox;
1755 void GFXOutputDev::restoreState(GfxState *state) {
1756 dbgindent-=2; dbg("restoreState %08x", state);
1759 msg("<fatal> Invalid restoreState");
1762 msg("<trace> restoreState %08x%s%s", state,
1763 states[statepos].softmask?" (end softmask)":"",
1764 states[statepos].clipping?" (end clipping)":"");
1765 if(states[statepos].softmask) {
1766 clearSoftMask(state);
1770 while(states[statepos].clipping) {
1771 device->endclip(device);
1772 states[statepos].clipping--;
1774 if(states[statepos].state!=state) {
1775 msg("<fatal> bad state nesting");
1778 states[statepos].state=0;
1782 void GFXOutputDev::updateLineDash(GfxState *state)
1784 state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1785 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1786 if(!this->dashLength) {
1787 this->dashPattern = 0;
1791 void GFXOutputDev::updateLineWidth(GfxState *state)
1793 double width = state->getTransformedLineWidth();
1796 void GFXOutputDev::updateLineCap(GfxState *state)
1798 int c = state->getLineCap();
1801 void GFXOutputDev::updateLineJoin(GfxState *state)
1803 int j = state->getLineJoin();
1806 void GFXOutputDev::updateFillColor(GfxState *state)
1809 double opaq = state->getFillOpacity();
1810 state->getFillRGB(&rgb);
1812 void GFXOutputDev::updateFillOpacity(GfxState *state)
1815 double opaq = state->getFillOpacity();
1816 state->getFillRGB(&rgb);
1817 dbg("update fillopaq %f", opaq);
1819 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1821 double opaq = state->getFillOpacity();
1822 dbg("update strokeopaq %f", opaq);
1824 void GFXOutputDev::updateFillOverprint(GfxState *state)
1826 double opaq = state->getFillOverprint();
1827 dbg("update filloverprint %f", opaq);
1829 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1831 double opaq = state->getStrokeOverprint();
1832 dbg("update strokeoverprint %f", opaq);
1834 void GFXOutputDev::updateTransfer(GfxState *state)
1836 dbg("update transfer");
1840 void GFXOutputDev::updateStrokeColor(GfxState *state)
1843 double opaq = state->getStrokeOpacity();
1844 state->getStrokeRGB(&rgb);
1848 gfxfont_t* GFXOutputDev::createGfxFont(GfxFont*xpdffont, FontInfo*src)
1850 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1851 memset(font, 0, sizeof(gfxfont_t));
1853 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1854 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1858 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1860 //printf("%d glyphs\n", font->num_glyphs);
1861 font->num_glyphs = 0;
1862 font->ascent = fabs(src->descender);
1863 font->descent = fabs(src->ascender);
1865 for(t=0;t<src->num_glyphs;t++) {
1866 if(src->glyphs[t]) {
1867 SplashPath*path = src->glyphs[t]->path;
1868 int len = path?path->getLength():0;
1869 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1870 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1871 src->glyphs[t]->glyphid = font->num_glyphs;
1872 glyph->unicode = src->glyphs[t]->unicode;
1873 if(glyph->unicode >= font->max_unicode)
1874 font->max_unicode = glyph->unicode+1;
1876 gfxdrawer_target_gfxline(&drawer);
1880 for(s=0;s<len;s++) {
1883 path->getPoint(s, &x, &y, &f);
1886 if(f&splashPathFirst) {
1887 drawer.moveTo(&drawer, x*scale, y*scale);
1889 if(f&splashPathCurve) {
1891 path->getPoint(++s, &x2, &y2, &f);
1892 if(f&splashPathCurve) {
1894 path->getPoint(++s, &x3, &y3, &f);
1895 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1897 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1900 drawer.lineTo(&drawer, x*scale, y*scale);
1902 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1903 // (f&splashPathFirst)?"first":"",
1904 // (f&splashPathLast)?"last":"");
1907 glyph->line = (gfxline_t*)drawer.result(&drawer);
1908 if(src->glyphs[t]->advance>0) {
1909 glyph->advance = src->glyphs[t]->advance;
1911 msg("<warning> Approximating advance value for glyph %d", t);
1912 glyph->advance = xmax*scale;
1914 if(this->config_bigchar) {
1915 double max = src->glyphs[t]->advance_max;
1916 if(max>0 && max > glyph->advance) {
1917 glyph->advance = max;
1924 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1925 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1926 for(t=0;t<font->num_glyphs;t++) {
1927 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1928 font->unicode2glyph[font->glyphs[t].unicode] = t;
1932 msg("<trace> %d glyphs.", t, font->num_glyphs);
1936 void GFXOutputDev::updateFont(GfxState *state)
1938 GfxFont* gfxFont = state->getFont();
1942 char*id = getFontID(gfxFont);
1943 msg("<verbose> Updating font to %s", id);
1944 if(gfxFont->getType() == fontType3) {
1945 infofeature("Type3 fonts");
1946 if(!config_extrafontdata) {
1951 msg("<error> Internal Error: FontID is null");
1955 this->current_fontinfo = this->info->getFont(id);
1956 if(!this->current_fontinfo) {
1957 msg("<error> Internal Error: no fontinfo for font %s", id);
1960 if(!this->current_fontinfo->seen) {
1961 dumpFontInfo("<verbose>", gfxFont);
1964 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1966 font = this->createGfxFont(gfxFont, current_fontinfo);
1967 font->id = strdup(id);
1968 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1970 device->addfont(device, font);
1972 current_gfxfont = font;
1975 updateFontMatrix(state);
1978 #define SQR(x) ((x)*(x))
1980 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1982 if((newwidth<1 || newheight<1) ||
1983 (width<=newwidth || height<=newheight))
1985 unsigned char*newdata;
1987 newdata= (unsigned char*)malloc(newwidth*newheight);
1988 double fx = ((double)width)/newwidth;
1989 double fy = ((double)height)/newheight;
1991 int blocksize = (int)(8192/(fx*fy));
1992 int r = 8192*256/palettesize;
1993 for(x=0;x<newwidth;x++) {
1994 double ex = px + fx;
1995 int fromx = (int)px;
1997 int xweight1 = (int)((1-(px-fromx))*256);
1998 int xweight2 = (int)((ex-tox)*256);
2000 for(y=0;y<newheight;y++) {
2001 double ey = py + fy;
2002 int fromy = (int)py;
2004 int yweight1 = (int)((1-(py-fromy))*256);
2005 int yweight2 = (int)((ey-toy)*256);
2012 for(xx=fromx;xx<=tox;xx++)
2013 for(yy=fromy;yy<=toy;yy++) {
2014 int b = 1-data[width*yy+xx];
2016 if(xx==fromx) weight = (weight*xweight1)/256;
2017 if(xx==tox) weight = (weight*xweight2)/256;
2018 if(yy==fromy) weight = (weight*yweight1)/256;
2019 if(yy==toy) weight = (weight*yweight2)/256;
2022 //if(a) a=(palettesize-1)*r/blocksize;
2023 newdata[y*newwidth+x] = (a*blocksize)/r;
2031 #define IMAGE_TYPE_JPEG 0
2032 #define IMAGE_TYPE_LOSSLESS 1
2034 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2035 double x1,double y1,
2036 double x2,double y2,
2037 double x3,double y3,
2038 double x4,double y4, int type)
2040 gfxcolor_t*newpic=0;
2042 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2043 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2045 gfxline_t p1,p2,p3,p4,p5;
2046 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2047 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2048 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2049 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2050 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2052 {p1.x = (int)(p1.x*20)/20.0;
2053 p1.y = (int)(p1.y*20)/20.0;
2054 p2.x = (int)(p2.x*20)/20.0;
2055 p2.y = (int)(p2.y*20)/20.0;
2056 p3.x = (int)(p3.x*20)/20.0;
2057 p3.y = (int)(p3.y*20)/20.0;
2058 p4.x = (int)(p4.x*20)/20.0;
2059 p4.y = (int)(p4.y*20)/20.0;
2060 p5.x = (int)(p5.x*20)/20.0;
2061 p5.y = (int)(p5.y*20)/20.0;
2065 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2066 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2071 img.data = (gfxcolor_t*)data;
2075 if(type == IMAGE_TYPE_JPEG)
2076 /* TODO: pass image_dpi to device instead */
2077 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2080 dev->fillbitmap(dev, &p1, &img, &m, 0);
2083 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2084 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2086 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2089 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2090 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2092 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2096 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2097 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2098 GBool inlineImg, int mask, int*maskColors,
2099 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2101 /* the code in this function is *old*. It's not pretty, but it works. */
2103 double x1,y1,x2,y2,x3,y3,x4,y4;
2104 ImageStream *imgStr;
2109 unsigned char* maskbitmap = 0;
2112 ncomps = colorMap->getNumPixelComps();
2113 bits = colorMap->getBits();
2118 unsigned char buf[8];
2119 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2121 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2122 imgMaskStr->reset();
2123 unsigned char pal[256];
2124 int n = 1 << colorMap->getBits();
2129 maskColorMap->getGray(pixBuf, &gray);
2130 pal[t] = colToByte(gray);
2132 for (y = 0; y < maskHeight; y++) {
2133 for (x = 0; x < maskWidth; x++) {
2134 imgMaskStr->getPixel(buf);
2135 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2140 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2141 imgMaskStr->reset();
2142 for (y = 0; y < maskHeight; y++) {
2143 for (x = 0; x < maskWidth; x++) {
2144 imgMaskStr->getPixel(buf);
2146 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2154 imgStr = new ImageStream(str, width, ncomps,bits);
2157 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2159 msg("<verbose> Ignoring %d by %d image", width, height);
2160 unsigned char buf[8];
2162 for (y = 0; y < height; ++y)
2163 for (x = 0; x < width; ++x) {
2164 imgStr->getPixel(buf);
2172 this->transformXY(state, 0, 1, &x1, &y1);
2173 this->transformXY(state, 0, 0, &x2, &y2);
2174 this->transformXY(state, 1, 0, &x3, &y3);
2175 this->transformXY(state, 1, 1, &x4, &y4);
2178 /* as type 3 bitmaps are antialized, we need to place them
2179 at integer coordinates, otherwise flash player's antializing
2180 will kick in and make everything blurry */
2181 x1 = (int)(x1);y1 = (int)(y1);
2182 x2 = (int)(x2);y2 = (int)(y2);
2183 x3 = (int)(x3);y3 = (int)(y3);
2184 x4 = (int)(x4);y4 = (int)(y4);
2187 if(!pbminfo && !(str->getKind()==strDCT)) {
2189 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2193 msg("<verbose> drawing %d by %d masked picture", width, height);
2195 if(!jpeginfo && (str->getKind()==strDCT)) {
2196 msg("<notice> File contains jpeg pictures");
2201 unsigned char buf[8];
2203 unsigned char*pic = new unsigned char[width*height];
2204 gfxcolor_t pal[256];
2206 state->getFillRGB(&rgb);
2208 memset(pal,255,sizeof(pal));
2209 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2210 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2211 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2212 pal[0].a = 255; pal[1].a = 0;
2215 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2216 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2217 for (y = 0; y < height; ++y)
2218 for (x = 0; x < width; ++x)
2220 imgStr->getPixel(buf);
2223 pic[width*y+x] = buf[0];
2227 unsigned char*pic2 = 0;
2230 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2239 height = realheight;
2243 /* make a black/white palette */
2245 float r = 255./(float)(numpalette-1);
2247 for(t=0;t<numpalette;t++) {
2248 pal[t].r = colToByte(rgb.r);
2249 pal[t].g = colToByte(rgb.g);
2250 pal[t].b = colToByte(rgb.b);
2251 pal[t].a = (unsigned char)(t*r);
2256 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2257 for (y = 0; y < height; ++y) {
2258 for (x = 0; x < width; ++x) {
2259 pic2[width*y+x] = pal[pic[y*width+x]];
2262 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2266 if(maskbitmap) free(maskbitmap);
2272 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2273 gfxcolor_t*pic=new gfxcolor_t[width*height];
2274 for (y = 0; y < height; ++y) {
2275 for (x = 0; x < width; ++x) {
2276 imgStr->getPixel(pixBuf);
2277 colorMap->getRGB(pixBuf, &rgb);
2278 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2279 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2280 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2281 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2283 int x1 = x*maskWidth/width;
2284 int y1 = y*maskHeight/height;
2285 int x2 = (x+1)*maskWidth/width;
2286 int y2 = (y+1)*maskHeight/height;
2288 unsigned int alpha=0;
2289 unsigned int count=0;
2290 for(xx=x1;xx<x2;xx++)
2291 for(yy=y1;yy<y2;yy++) {
2292 alpha += maskbitmap[yy*maskWidth+xx];
2296 pic[width*y+x].a = alpha / count;
2298 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2303 if(str->getKind()==strDCT)
2304 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2306 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2309 if(maskbitmap) free(maskbitmap);
2312 gfxcolor_t*pic=new gfxcolor_t[width*height];
2313 gfxcolor_t pal[256];
2314 int n = 1 << colorMap->getBits();
2316 for(t=0;t<256;t++) {
2318 colorMap->getRGB(pixBuf, &rgb);
2320 {/*if(maskColors && *maskColors==t) {
2321 msg("<notice> Color %d is transparent", t);
2322 if (imgData->maskColors) {
2324 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2325 if (pix[i] < imgData->maskColors[2*i] ||
2326 pix[i] > imgData->maskColors[2*i+1]) {
2341 pal[t].r = (unsigned char)(colToByte(rgb.r));
2342 pal[t].g = (unsigned char)(colToByte(rgb.g));
2343 pal[t].b = (unsigned char)(colToByte(rgb.b));
2344 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2347 for (y = 0; y < height; ++y) {
2348 for (x = 0; x < width; ++x) {
2349 imgStr->getPixel(pixBuf);
2350 pic[width*y+x] = pal[pixBuf[0]];
2354 if(maskWidth < width && maskHeight < height) {
2355 for(y = 0; y < height; y++) {
2356 for (x = 0; x < width; x++) {
2357 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2361 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2362 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2363 double dx = width / maskWidth;
2364 double dy = height / maskHeight;
2366 for(y = 0; y < maskHeight; y++) {
2368 for (x = 0; x < maskWidth; x++) {
2369 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2370 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2378 height = maskHeight;
2381 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2385 if(maskbitmap) free(maskbitmap);
2390 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2391 int width, int height, GBool invert,
2394 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2395 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2396 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2399 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2400 int width, int height, GfxImageColorMap *colorMap,
2401 int *maskColors, GBool inlineImg)
2403 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2404 colorMap?"colorMap":"no colorMap",
2405 maskColors?"maskColors":"no maskColors",
2407 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2408 colorMap?"colorMap":"no colorMap",
2409 maskColors?"maskColors":"no maskColors",
2412 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2413 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2414 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2417 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2418 int width, int height,
2419 GfxImageColorMap *colorMap,
2420 Stream *maskStr, int maskWidth, int maskHeight,
2423 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2424 colorMap?"colorMap":"no colorMap",
2425 maskWidth, maskHeight);
2426 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2427 colorMap?"colorMap":"no colorMap",
2428 maskWidth, maskHeight);
2430 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2431 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2432 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2435 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2436 int width, int height,
2437 GfxImageColorMap *colorMap,
2439 int maskWidth, int maskHeight,
2440 GfxImageColorMap *maskColorMap)
2442 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2443 colorMap?"colorMap":"no colorMap",
2444 maskWidth, maskHeight);
2445 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2446 colorMap?"colorMap":"no colorMap",
2447 maskWidth, maskHeight);
2449 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2450 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2451 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2454 void GFXOutputDev::stroke(GfxState *state)
2458 GfxPath * path = state->getPath();
2459 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2460 strokeGfxline(state, line, 0);
2464 void GFXOutputDev::fill(GfxState *state)
2466 gfxcolor_t col = getFillColor(state);
2467 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2469 GfxPath * path = state->getPath();
2470 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2471 if(config_optimize_polygons) {
2472 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2476 fillGfxLine(state, line);
2480 void GFXOutputDev::eoFill(GfxState *state)
2482 gfxcolor_t col = getFillColor(state);
2483 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2485 GfxPath * path = state->getPath();
2486 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2487 fillGfxLine(state, line);
2492 static const char* dirseparator()
2501 void addGlobalFont(const char*filename)
2503 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2504 memset(f, 0, sizeof(fontfile_t));
2505 f->filename = filename;
2506 int len = strlen(filename);
2507 char*r1 = strrchr(filename, '/');
2508 char*r2 = strrchr(filename, '\\');
2516 msg("<notice> Adding font \"%s\".", filename);
2517 if(global_fonts_next) {
2518 global_fonts_next->next = f;
2519 global_fonts_next = global_fonts_next->next;
2521 global_fonts_next = global_fonts = f;
2525 void addGlobalLanguageDir(const char*dir)
2527 msg("<notice> Adding %s to language pack directories", dir);
2530 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2531 strcpy(config_file, dir);
2532 strcat(config_file, dirseparator());
2533 strcat(config_file, "add-to-xpdfrc");
2535 fi = fopen(config_file, "rb");
2537 msg("<error> Could not open %s", config_file);
2540 globalParams->parseFile(new GString(config_file), fi);
2544 void addGlobalFontDir(const char*dirname)
2546 #ifdef HAVE_DIRENT_H
2547 msg("<notice> Adding %s to font directories", dirname);
2548 lastfontdir = strdup(dirname);
2549 DIR*dir = opendir(dirname);
2551 msg("<warning> Couldn't open directory %s", dirname);
2556 ent = readdir (dir);
2560 char*name = ent->d_name;
2566 if(!strncasecmp(&name[l-4], ".pfa", 4))
2568 if(!strncasecmp(&name[l-4], ".pfb", 4))
2570 if(!strncasecmp(&name[l-4], ".ttf", 4))
2573 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2574 strcpy(fontname, dirname);
2575 strcat(fontname, dirseparator());
2576 strcat(fontname, name);
2577 addGlobalFont(fontname);
2582 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2586 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2592 this->pagebuflen = 1024;
2593 if(pdfpage > this->pagebuflen)
2594 this->pagebuflen = pdfpage+1;
2595 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2596 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2599 while(pdfpage >= this->pagebuflen)
2601 int oldlen = this->pagebuflen;
2602 this->pagebuflen+=1024;
2603 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2604 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2607 this->pages[pdfpage] = outputpage;
2608 if(pdfpage>this->pagepos)
2609 this->pagepos = pdfpage;
2612 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2613 GfxColorSpace *blendingColorSpace,
2614 GBool isolated, GBool knockout,
2617 const char*colormodename = "";
2619 if(blendingColorSpace) {
2620 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2622 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);
2623 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);
2625 //states[statepos].createsoftmask |= forSoftMask;
2626 states[statepos].createsoftmask = forSoftMask;
2627 states[statepos].transparencygroup = !forSoftMask;
2628 states[statepos].isolated = isolated;
2630 states[statepos].olddevice = this->device;
2631 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2632 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2634 gfxdevice_record_init(this->device);
2636 /*if(!forSoftMask) { ////???
2637 state->setFillOpacity(0.0);
2642 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2645 gfxdevice_t*r = this->device;
2647 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2649 this->device = states[statepos].olddevice;
2651 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2652 /* if these errors occur more often, we should build a seperate
2653 transparency group stack, like xpdf/SplashOutputDev.cc does */
2654 restoreState(state);
2655 this->device = states[statepos].olddevice;
2657 states[statepos].olddevice = 0;
2659 gfxresult_t*recording = r->finish(r);
2661 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2662 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2664 if(states[statepos].createsoftmask) {
2665 states[statepos-1].softmaskrecording = recording;
2667 states[statepos-1].grouprecording = recording;
2670 states[statepos].createsoftmask = 0;
2671 states[statepos].transparencygroup = 0;
2675 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2677 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2678 "colordodge","colorburn","hardlight","softlight","difference",
2679 "exclusion","hue","saturation","color","luminosity"};
2681 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2682 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2684 if(state->getBlendMode() == gfxBlendNormal)
2685 infofeature("transparency groups");
2688 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2689 warnfeature(buffer, 0);
2692 gfxresult_t*grouprecording = states[statepos].grouprecording;
2694 int blendmode = state->getBlendMode();
2695 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2696 int alpha = (int)(state->getFillOpacity()*255);
2697 if(blendmode == gfxBlendMultiply && alpha>200)
2700 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2701 gfxdevice_ops_init(&ops, this->device, alpha);
2702 gfxresult_record_replay(grouprecording, &ops);
2705 grouprecording->destroy(grouprecording);
2707 states[statepos].grouprecording = 0;
2710 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2712 if(states[statepos].softmask) {
2713 /* shouldn't happen, but *does* happen */
2714 clearSoftMask(state);
2717 /* alpha = 1: retrieve mask values from alpha layer
2718 alpha = 0: retrieve mask values from luminance */
2720 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2721 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2722 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2723 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2725 infofeature("soft masks");
2727 warnfeature("soft masks from alpha channel",0);
2729 if(states[statepos].olddevice) {
2730 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2733 states[statepos].olddevice = this->device;
2734 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2735 gfxdevice_record_init(this->device);
2737 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2739 states[statepos].softmask = 1;
2740 states[statepos].softmask_alpha = alpha;
2743 static inline Guchar div255(int x) {
2744 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2747 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2749 if(c < min) c = min;
2750 if(c > max) c = max;
2754 void GFXOutputDev::clearSoftMask(GfxState *state)
2756 if(!states[statepos].softmask)
2758 states[statepos].softmask = 0;
2759 dbg("clearSoftMask statepos=%d", statepos);
2760 msg("<verbose> clearSoftMask statepos=%d", statepos);
2762 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2763 msg("<error> Error in softmask/tgroup ordering");
2767 gfxresult_t*mask = states[statepos].softmaskrecording;
2768 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2769 this->device = states[statepos].olddevice;
2771 /* get outline of all objects below the soft mask */
2772 gfxdevice_t uniondev;
2773 gfxdevice_union_init(&uniondev, 0);
2774 gfxresult_record_replay(below, &uniondev);
2775 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2776 uniondev.finish(&uniondev);
2777 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2778 gfxline_free(belowoutline);belowoutline=0;
2780 this->device->startclip(this->device, belowoutline);
2781 gfxresult_record_replay(below, this->device);
2782 gfxresult_record_replay(mask, this->device);
2783 this->device->endclip(this->device);
2786 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2787 if(width<=0 || height<=0)
2790 gfxdevice_t belowrender;
2791 gfxdevice_render_init(&belowrender);
2792 if(states[statepos+1].isolated) {
2793 belowrender.setparameter(&belowrender, "fillwhite", "1");
2795 belowrender.setparameter(&belowrender, "antialize", "2");
2796 belowrender.startpage(&belowrender, width, height);
2797 gfxresult_record_replay(below, &belowrender);
2798 belowrender.endpage(&belowrender);
2799 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2800 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2801 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2803 gfxdevice_t maskrender;
2804 gfxdevice_render_init(&maskrender);
2805 maskrender.startpage(&maskrender, width, height);
2806 gfxresult_record_replay(mask, &maskrender);
2807 maskrender.endpage(&maskrender);
2808 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2809 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2811 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2812 msg("<fatal> Internal error in mask drawing");
2817 for(y=0;y<height;y++) {
2818 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2819 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2820 for(x=0;x<width;x++) {
2822 if(states[statepos].softmask_alpha) {
2825 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2828 l2->a = div255(alpha*l2->a);
2830 /* DON'T premultiply alpha- this is done by fillbitmap,
2831 depending on the output device */
2832 //l2->r = div255(alpha*l2->r);
2833 //l2->g = div255(alpha*l2->g);
2834 //l2->b = div255(alpha*l2->b);
2840 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2843 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2844 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2846 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2848 mask->destroy(mask);
2849 below->destroy(below);
2850 maskresult->destroy(maskresult);
2851 belowresult->destroy(belowresult);
2852 states[statepos].softmaskrecording = 0;
2857 // public: ~MemCheck()
2859 // delete globalParams;globalParams=0;
2860 // Object::memCheck(stderr);
2861 // gMemReport(stderr);