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 fontfile_t* global_fontdirs = 0;
97 static fontfile_t* global_fontdirs_next = 0;
99 static int fontnum = 0;
112 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
113 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
114 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
115 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
116 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
117 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
118 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
119 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
120 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
121 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
122 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
123 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
124 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
125 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
128 static int verbose = 0;
129 static int dbgindent = 1;
130 static void dbg(const char*format, ...)
137 va_start(arglist, format);
138 vsprintf(buf, format, arglist);
141 while(l && buf[l-1]=='\n') {
146 int indent = dbgindent;
155 GFXOutputGlobals*gfxglobals=0;
157 GFXOutputGlobals::GFXOutputGlobals()
159 this->featurewarnings = 0;
161 this->textmodeinfo = 0;
165 GFXOutputGlobals::~GFXOutputGlobals()
167 feature_t*f = this->featurewarnings;
169 feature_t*next = f->next;
171 free(f->string);f->string =0;
177 this->featurewarnings = 0;
180 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
182 feature_t*f = gfxglobals->featurewarnings;
184 if(!strcmp(feature, f->string))
188 f = (feature_t*)malloc(sizeof(feature_t));
189 f->string = strdup(feature);
190 f->next = gfxglobals->featurewarnings;
191 gfxglobals->featurewarnings = f;
193 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
194 if(this->config_break_on_warning) {
195 msg("<fatal> Aborting conversion due to unsupported feature");
199 msg("<notice> File contains %s",feature);
202 void GFXOutputDev::warnfeature(const char*feature,char fully)
204 showfeature(feature,fully,1);
206 void GFXOutputDev::infofeature(const char*feature)
208 showfeature(feature,0,0);
211 GFXOutputState::GFXOutputState() {
213 this->createsoftmask = 0;
214 this->transparencygroup = 0;
216 this->grouprecording = 0;
220 GBool GFXOutputDev::interpretType3Chars()
225 typedef struct _drawnchar
243 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
244 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
249 free(chars);chars = 0;
256 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
260 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
263 chars[num_chars].x = x;
264 chars[num_chars].y = y;
265 chars[num_chars].color = color;
266 chars[num_chars].charid = charid;
270 char* writeOutStdFont(fontentry* f)
275 char* tmpFileName = mktmpname(namebuf1);
277 sprintf(namebuf2, "%s.afm", tmpFileName);
278 fi = fopen(namebuf2, "wb");
281 fwrite(f->afm, 1, f->afmlen, fi);
284 sprintf(namebuf2, "%s.pfb", tmpFileName);
285 fi = fopen(namebuf2, "wb");
288 fwrite(f->pfb, 1, f->pfblen, fi);
290 return strdup(namebuf2);
292 void unlinkfont(char* filename)
297 msg("<verbose> Removing temporary font file %s", filename);
300 if(!strncmp(&filename[l-4],".afm",4)) {
301 memcpy(&filename[l-4],".pfb",4); unlink(filename);
302 memcpy(&filename[l-4],".pfa",4); unlink(filename);
303 memcpy(&filename[l-4],".afm",4);
306 if(!strncmp(&filename[l-4],".pfa",4)) {
307 memcpy(&filename[l-4],".afm",4); unlink(filename);
308 memcpy(&filename[l-4],".pfa",4);
311 if(!strncmp(&filename[l-4],".pfb",4)) {
312 memcpy(&filename[l-4],".afm",4); unlink(filename);
313 memcpy(&filename[l-4],".pfb",4);
318 static int config_use_fontconfig = 1;
319 static int fcinitcalled = 0;
321 GFXGlobalParams::GFXGlobalParams()
322 : GlobalParams((char*)"")
324 //setupBaseFonts(char *dir); //not tested yet
326 GFXGlobalParams::~GFXGlobalParams()
328 msg("<verbose> Performing cleanups");
330 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
331 if(pdf2t1map[t].fullfilename) {
332 unlinkfont(pdf2t1map[t].fullfilename);
335 #ifdef HAVE_FONTCONFIG
336 if(config_use_fontconfig && fcinitcalled)
340 #ifdef HAVE_FONTCONFIG
341 static char fc_ismatch(FcPattern*match, char*family, char*style)
343 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
344 FcBool scalable=FcFalse, outline=FcFalse;
345 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
346 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
347 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
348 FcPatternGetBool(match, "outline", 0, &outline);
349 FcPatternGetBool(match, "scalable", 0, &scalable);
351 if(scalable!=FcTrue || outline!=FcTrue)
354 if (!strcasecmp(fcfamily, family)) {
355 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
358 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
364 char* fontconfig_searchForFont(char*name)
366 #ifdef HAVE_FONTCONFIG
367 if(!config_use_fontconfig)
370 // call init ony once
374 // check whether we have a config file
375 char* configfile = (char*)FcConfigFilename(0);
376 int configexists = 0;
377 FILE*fi = fopen(configfile, "rb");
379 configexists = 1;fclose(fi);
380 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
382 msg("<debug> Initializing FontConfig (no configfile)");
386 /* A fontconfig instance which didn't find a configfile is unbelievably
387 cranky, so let's just write out a small xml file and make fontconfig
389 FcConfig*c = FcConfigCreate();
391 char* tmpFileName = mktmpname(namebuf);
392 FILE*fi = fopen(tmpFileName, "wb");
393 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
395 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
397 fprintf(fi, "<dir>~/.fonts</dir>\n");
399 /* add external font dirs to fontconfig's config. Maybe fc will make more out
401 FIXME: we don't do that yet if there's a system config file
403 fontfile_t*fd = global_fontdirs;
405 fprintf(fi, "<dir>%s</dir>\n", fd->filename);
410 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
412 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
413 fprintf(fi, "</fontconfig>\n");
415 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
416 FcConfigBuildFonts(c);
417 FcConfigSetCurrent(c);
421 msg("<debug> FontConfig Initialization failed. Disabling.");
422 config_use_fontconfig = 0;
425 FcConfig * config = FcConfigGetCurrent();
427 msg("<debug> FontConfig Config Initialization failed. Disabling.");
428 config_use_fontconfig = 0;
431 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
432 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
433 if(!set || !set->nfont) {
434 msg("<debug> FontConfig has zero fonts. Disabling.");
435 config_use_fontconfig = 0;
439 if(getLogLevel() >= LOGLEVEL_TRACE) {
441 for(t=0;t<set->nfont;t++) {
442 char*fcfamily=0,*fcstyle=0,*filename=0;
443 FcBool scalable=FcFalse, outline=FcFalse;
444 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
445 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
446 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
447 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
448 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
449 if(scalable && outline) {
450 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
456 char*family = strdup(name);
458 char*dash = strchr(family, '-');
459 FcPattern*pattern = 0;
463 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
464 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
466 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
467 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
471 FcConfigSubstitute(0, pattern, FcMatchPattern);
472 FcDefaultSubstitute(pattern);
474 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
477 for(t=0;t<set->nfont;t++) {
478 FcPattern*match = set->fonts[t];
479 //FcPattern*match = FcFontMatch(0, pattern, &result);
480 if(fc_ismatch(match, family, style)) {
482 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
483 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
486 //FcPatternDestroy(match);
487 msg("<debug> fontconfig: returning filename %s", filename);
489 FcPatternDestroy(pattern);
490 FcFontSetDestroy(set);
491 return filename?strdup(filename):0;
496 FcPatternDestroy(pattern);
497 FcFontSetDestroy(set);
504 static DisplayFontParamKind detectFontType(const char*filename)
506 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
507 return displayFontTT;
508 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
509 return displayFontT1;
510 return displayFontTT;
513 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
515 msg("<verbose> looking for font %s in global params", fontName->getCString());
517 char*name = fontName->getCString();
519 /* see if it is a pdf standard font */
521 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
522 if(!strcmp(name, pdf2t1map[t].pdffont)) {
523 if(!pdf2t1map[t].fullfilename) {
524 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
525 if(!pdf2t1map[t].fullfilename) {
526 msg("<error> Couldn't save default font- is the Temp Directory writable?");
528 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
531 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
532 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
537 int bestlen = 0x7fffffff;
538 const char*bestfilename = 0;
540 #ifndef HAVE_FONTCONFIG
541 /* if we don't have fontconfig, try a simple approach */
542 fontfile_t*f = global_fonts;
544 if(strstr(f->filename, name)) {
545 if(f->len < bestlen) {
547 bestfilename = f->filename;
554 /* if we didn't find anything up to now, try looking for the
555 font via fontconfig */
558 filename = fontconfig_searchForFont(name);
560 filename = strdup(bestfilename);
564 msg("<verbose> Font %s maps to %s\n", name, filename);
565 DisplayFontParamKind kind = detectFontType(filename);
566 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
567 if(kind == displayFontTT) {
568 dfp->tt.fileName = new GString(filename);
570 dfp->t1.fileName = new GString(filename);
575 return GlobalParams::getDisplayFont(fontName);
578 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
581 gfxglobals = new GFXOutputGlobals();
585 this->xref = doc->getXRef();
587 this->type3active = 0;
590 this->user_movex = 0;
591 this->user_movey = 0;
594 this->user_clipx1 = 0;
595 this->user_clipy1 = 0;
596 this->user_clipx2 = 0;
597 this->user_clipy2 = 0;
598 this->current_fontinfo = 0;
599 this->current_text_stroke = 0;
600 this->current_text_clip = 0;
601 this->outer_clip_box = 0;
602 this->config_bigchar=0;
603 this->config_convertgradients=1;
604 this->config_break_on_warning=0;
605 this->config_remapunicode=0;
606 this->config_transparent=0;
607 this->config_extrafontdata = 0;
608 this->config_optimize_polygons = 0;
609 this->config_multiply = 1;
610 this->dashPattern = 0;
614 memset(states, 0, sizeof(states));
617 void GFXOutputDev::setParameter(const char*key, const char*value)
619 if(!strcmp(key,"breakonwarning")) {
620 this->config_break_on_warning = atoi(value);
621 } else if(!strcmp(key,"remapunicode")) {
622 this->config_remapunicode = atoi(value);
623 } else if(!strcmp(key,"transparent")) {
624 this->config_transparent = atoi(value);
625 } else if(!strcmp(key,"extrafontdata")) {
626 this->config_extrafontdata = atoi(value);
627 } else if(!strcmp(key,"convertgradients")) {
628 this->config_convertgradients = atoi(value);
629 } else if(!strcmp(key,"multiply")) {
630 this->config_multiply = atoi(value);
631 if(this->config_multiply<1)
632 this->config_multiply=1;
633 } else if(!strcmp(key,"optimize_polygons")) {
634 this->config_optimize_polygons = atoi(value);
638 void GFXOutputDev::setDevice(gfxdevice_t*dev)
643 void GFXOutputDev::setMove(int x,int y)
645 this->user_movex = x;
646 this->user_movey = y;
649 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
651 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
652 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
654 this->user_clipx1 = x1;
655 this->user_clipy1 = y1;
656 this->user_clipx2 = x2;
657 this->user_clipy2 = y2;
660 static char*getFontName(GfxFont*font)
663 GString*gstr = font->getName();
664 char* fname = gstr==0?0:gstr->getCString();
668 sprintf(buf, "UFONT%d", r->num);
669 fontid = strdup(buf);
671 fontid = strdup(fname);
675 char* plus = strchr(fontid, '+');
676 if(plus && plus < &fontid[strlen(fontid)-1]) {
677 fontname = strdup(plus+1);
679 fontname = strdup(fontid);
685 static void dumpFontInfo(const char*loglevel, GfxFont*font);
686 static int lastdumps[1024];
687 static int lastdumppos = 0;
692 static void showFontError(GfxFont*font, int nr)
696 for(t=0;t<lastdumppos;t++)
697 if(lastdumps[t] == r->num)
701 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
702 lastdumps[lastdumppos++] = r->num;
704 msg("<warning> The following font caused problems:");
706 msg("<warning> The following font caused problems (substituting):");
708 msg("<warning> The following Type 3 Font will be rendered as graphics:");
709 dumpFontInfo("<warning>", font);
712 static void dumpFontInfo(const char*loglevel, GfxFont*font)
714 char* id = getFontID(font);
715 char* name = getFontName(font);
716 Ref* r=font->getID();
717 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
719 GString*gstr = font->getTag();
721 msg("%s| Tag: %s", loglevel, id);
723 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
725 GfxFontType type=font->getType();
727 case fontUnknownType:
728 msg("%s| Type: unknown",loglevel);
731 msg("%s| Type: 1",loglevel);
734 msg("%s| Type: 1C",loglevel);
737 msg("%s| Type: 3",loglevel);
740 msg("%s| Type: TrueType",loglevel);
743 msg("%s| Type: CIDType0",loglevel);
746 msg("%s| Type: CIDType0C",loglevel);
749 msg("%s| Type: CIDType2",loglevel);
754 GBool embedded = font->getEmbeddedFontID(&embRef);
756 if(font->getEmbeddedFontName()) {
757 embeddedName = font->getEmbeddedFontName()->getCString();
760 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
762 gstr = font->getExtFontFile();
764 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
766 // Get font descriptor flags.
767 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
768 if(font->isSerif()) msg("%s| is serif", loglevel);
769 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
770 if(font->isItalic()) msg("%s| is italic", loglevel);
771 if(font->isBold()) msg("%s| is bold", loglevel);
777 //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");}
778 //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");}
780 void dump_outline(gfxline_t*line)
782 /*gfxbbox_t*r = gfxline_isrectangle(line);
784 printf("is not a rectangle\n");
786 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
790 if(line->type == gfx_moveTo) {
791 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
792 } else if(line->type == gfx_lineTo) {
793 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
794 } else if(line->type == gfx_splineTo) {
795 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
801 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
803 int num = path->getNumSubpaths();
806 double lastx=0,lasty=0,posx=0,posy=0;
809 msg("<warning> empty path");
813 gfxdrawer_target_gfxline(&draw);
815 for(t = 0; t < num; t++) {
816 GfxSubpath *subpath = path->getSubpath(t);
817 int subnum = subpath->getNumPoints();
818 double bx=0,by=0,cx=0,cy=0;
820 for(s=0;s<subnum;s++) {
823 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
826 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
827 draw.lineTo(&draw, lastx, lasty);
829 draw.moveTo(&draw, x,y);
834 } else if(subpath->getCurve(s) && cpos==0) {
838 } else if(subpath->getCurve(s) && cpos==1) {
846 draw.lineTo(&draw, x,y);
848 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
855 /* fix non-closed lines */
856 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
857 draw.lineTo(&draw, lastx, lasty);
859 gfxline_t*result = (gfxline_t*)draw.result(&draw);
861 gfxline_optimize(result);
866 GBool GFXOutputDev::useTilingPatternFill()
868 infofeature("tiled patterns");
869 // if(config_convertgradients)
873 GBool GFXOutputDev::useShadedFills()
875 infofeature("shaded fills");
876 if(config_convertgradients)
881 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
883 state->transform(x,y,nx,ny);
884 *nx += user_movex + clipmovex;
885 *ny += user_movey + clipmovey;
889 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
890 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
891 int paintType, Dict *resDict,
892 double *mat, double *bbox,
893 int x0, int y0, int x1, int y1,
894 double xStep, double yStep)
896 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
897 int paintType, Dict *resDict,
898 double *mat, double *bbox,
899 int x0, int y0, int x1, int y1,
900 double xStep, double yStep)
903 msg("<debug> tilingPatternFill");
904 infofeature("tiling pattern fills");
907 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
909 msg("<error> functionShadedFill not supported yet");
910 infofeature("function shaded fills");
913 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
917 colspace->getRGB(col, &rgb);
918 c.r = colToByte(rgb.r);
919 c.g = colToByte(rgb.g);
920 c.b = colToByte(rgb.b);
925 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
927 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
928 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
931 this->transformXY(state, x0,y0, &x0,&y0);
932 this->transformXY(state, x1,y1, &x1,&y1);
933 this->transformXY(state, x2,y2, &x2,&y2);
938 shading->getColor(0.0, &color0);
939 shading->getColor(0.5, &color1);
940 shading->getColor(1.0, &color2);
942 GfxColorSpace* colspace = shading->getColorSpace();
944 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
945 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
946 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
947 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
948 infofeature("radial shaded fills");
950 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
954 g[0].color = col2col(colspace, &color0);
955 g[1].color = col2col(colspace, &color1);
956 g[2].color = col2col(colspace, &color2);
961 gfxbbox_t b = states[statepos].clipbbox;
962 gfxline_t p1,p2,p3,p4,p5;
963 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
964 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
965 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
966 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
967 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
970 //m.m00 = (x3-x0); m.m10 = (x1-x0);
971 //m.m01 = (y3-y0); m.m11 = (y1-y0);
972 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
973 m.m00 = (x1-x0); m.m10 = (x2-x0);
974 m.m01 = (y1-y0); m.m11 = (y2-y0);
978 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
982 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
985 shading->getCoords(&x0,&y0,&x1,&y1);
986 this->transformXY(state, x0,y0,&x0,&y0);
987 this->transformXY(state, x1,y1,&x1,&y1);
992 shading->getColor(0.0, &color0);
993 shading->getColor(0.5, &color1);
994 shading->getColor(1.0, &color2);
996 GfxColorSpace* colspace = shading->getColorSpace();
998 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
999 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1000 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1001 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1003 infofeature("axial shaded fills");
1005 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1009 g[0].color = col2col(colspace, &color0);
1010 g[1].color = col2col(colspace, &color1);
1011 g[2].color = col2col(colspace, &color2);
1016 double xMin,yMin,xMax,yMax;
1017 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1018 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1019 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1022 xMin = 1024; yMin = 1024;
1024 gfxbbox_t b = states[statepos].clipbbox;
1025 gfxline_t p1,p2,p3,p4,p5;
1026 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1027 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1028 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1029 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1030 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1032 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1033 the middle of the two control points */
1035 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1036 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1037 m.tx = (x0 + x1)/2 - 0.5;
1038 m.ty = (y0 + y1)/2 - 0.5;
1040 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1046 GBool GFXOutputDev::useDrawForm()
1048 infofeature("forms");
1051 void GFXOutputDev::drawForm(Ref id)
1053 msg("<error> drawForm not implemented");
1055 GBool GFXOutputDev::needNonText()
1059 void GFXOutputDev::endPage()
1061 msg("<verbose> endPage (GfxOutputDev)");
1062 if(outer_clip_box) {
1063 device->endclip(device);
1066 this->dashPattern = 0;
1067 /* notice: we're not fully done yet with this page- there might still be
1068 a few calls to drawLink() yet to come */
1071 static inline double sqr(double x) {return x*x;}
1073 #define STROKE_FILL 1
1074 #define STROKE_CLIP 2
1075 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1077 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1078 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1079 double miterLimit = state->getMiterLimit();
1080 double width = state->getTransformedLineWidth();
1083 double opaq = state->getStrokeOpacity();
1085 state->getFillRGB(&rgb);
1087 state->getStrokeRGB(&rgb);
1089 col.r = colToByte(rgb.r);
1090 col.g = colToByte(rgb.g);
1091 col.b = colToByte(rgb.b);
1092 col.a = (unsigned char)(opaq*255);
1094 gfx_capType capType = gfx_capRound;
1095 if(lineCap == 0) capType = gfx_capButt;
1096 else if(lineCap == 1) capType = gfx_capRound;
1097 else if(lineCap == 2) capType = gfx_capSquare;
1099 gfx_joinType joinType = gfx_joinRound;
1100 if(lineJoin == 0) joinType = gfx_joinMiter;
1101 else if(lineJoin == 1) joinType = gfx_joinRound;
1102 else if(lineJoin == 2) joinType = gfx_joinBevel;
1104 gfxline_t*line2 = 0;
1106 if(this->dashLength && this->dashPattern) {
1107 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1110 /* try to find out how much the transformation matrix would
1111 stretch the dashes, and factor that into the dash lengths.
1112 This is not the entirely correct approach- it would be
1113 better to first convert the path to an unscaled version,
1114 then apply dashing, and then transform the path using
1115 the current transformation matrix. However there are few
1116 PDFs which actually stretch a dashed path in a non-orthonormal
1118 double tx1, ty1, tx2, ty2;
1119 this->transformXY(state, 0, 0, &tx1, &ty1);
1120 this->transformXY(state, 1, 1, &tx2, &ty2);
1121 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1123 msg("<trace> %d dashes", this->dashLength);
1124 msg("<trace> | phase: %f", this->dashStart);
1125 for(t=0;t<this->dashLength;t++) {
1126 dash[t] = (float)this->dashPattern[t] * f;
1129 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1131 dash[this->dashLength] = -1;
1132 if(getLogLevel() >= LOGLEVEL_TRACE) {
1136 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1139 msg("<trace> After dashing:");
1142 if(getLogLevel() >= LOGLEVEL_TRACE) {
1143 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1145 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1146 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1148 col.r,col.g,col.b,col.a
1153 if(flags&STROKE_FILL) {
1154 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1155 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1156 if(getLogLevel() >= LOGLEVEL_TRACE) {
1157 dump_outline(gfxline);
1160 msg("<warning> Empty polygon (resulting from stroked line)");
1162 if(flags&STROKE_CLIP) {
1163 device->startclip(device, gfxline);
1164 states[statepos].clipping++;
1166 device->fill(device, gfxline, &col);
1168 gfxline_free(gfxline);
1171 if(flags&STROKE_CLIP)
1172 msg("<error> Stroke&clip not supported at the same time");
1173 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1177 gfxline_free(line2);
1180 gfxcolor_t getFillColor(GfxState * state)
1183 double opaq = state->getFillOpacity();
1184 state->getFillRGB(&rgb);
1186 col.r = colToByte(rgb.r);
1187 col.g = colToByte(rgb.g);
1188 col.b = colToByte(rgb.b);
1189 col.a = (unsigned char)(opaq*255);
1193 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1195 gfxcolor_t col = getFillColor(state);
1197 if(getLogLevel() >= LOGLEVEL_TRACE) {
1198 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1201 device->fill(device, line, &col);
1204 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1206 if(getLogLevel() >= LOGLEVEL_TRACE) {
1209 gfxbbox_t bbox = gfxline_getbbox(line);
1210 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1212 device->startclip(device, line);
1213 states[statepos].clipping++;
1216 void GFXOutputDev::clip(GfxState *state)
1218 GfxPath * path = state->getPath();
1219 msg("<trace> clip");
1220 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1221 if(config_optimize_polygons) {
1222 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1226 clipToGfxLine(state, line);
1230 void GFXOutputDev::eoClip(GfxState *state)
1232 GfxPath * path = state->getPath();
1233 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1234 clipToGfxLine(state, line);
1237 void GFXOutputDev::clipToStrokePath(GfxState *state)
1239 GfxPath * path = state->getPath();
1240 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1242 if(getLogLevel() >= LOGLEVEL_TRACE) {
1243 double width = state->getTransformedLineWidth();
1244 msg("<trace> cliptostrokepath width=%f", width);
1248 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1252 void GFXOutputDev::finish()
1254 if(outer_clip_box) {
1256 device->endclip(device);
1262 GFXOutputDev::~GFXOutputDev()
1265 if(this->dashPattern) {
1266 free(this->dashPattern);this->dashPattern = 0;
1269 GBool GFXOutputDev::upsideDown()
1273 GBool GFXOutputDev::useDrawChar()
1278 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1279 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1281 static char tmp_printstr[4096];
1282 char* makeStringPrintable(char*str)
1284 int len = strlen(str);
1291 for(t=0;t<len;t++) {
1296 tmp_printstr[t] = c;
1299 tmp_printstr[len++] = '.';
1300 tmp_printstr[len++] = '.';
1301 tmp_printstr[len++] = '.';
1303 tmp_printstr[len] = 0;
1304 return tmp_printstr;
1306 void GFXOutputDev::updateFontMatrix(GfxState*state)
1308 double* ctm = state->getCTM();
1309 double fontSize = state->getFontSize();
1310 double*textMat = state->getTextMat();
1312 /* taking the absolute value of horizScaling seems to be required for
1313 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1314 double hscale = fabs(state->getHorizScaling());
1316 // from xpdf-3.02/SplashOutputDev:updateFont
1317 double mm11 = textMat[0] * fontSize * hscale;
1318 double mm12 = textMat[1] * fontSize * hscale;
1319 double mm21 = textMat[2] * fontSize;
1320 double mm22 = textMat[3] * fontSize;
1322 // multiply with ctm, like state->getFontTransMat() does
1323 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1324 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1325 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1326 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1327 this->current_font_matrix.tx = 0;
1328 this->current_font_matrix.ty = 0;
1331 void GFXOutputDev::beginString(GfxState *state, GString *s)
1333 int render = state->getRender();
1334 if(current_text_stroke) {
1335 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1338 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1341 static gfxline_t* mkEmptyGfxShape(double x, double y)
1343 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1344 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1348 static char isValidUnicode(int c)
1350 if(c>=32 && c<0x2fffe)
1355 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1356 double dx, double dy,
1357 double originX, double originY,
1358 CharCode charid, int nBytes, Unicode *_u, int uLen)
1360 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1361 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1365 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1367 int render = state->getRender();
1368 gfxcolor_t col = getFillColor(state);
1370 // check for invisible text -- this is used by Acrobat Capture
1371 if (render == RENDER_INVISIBLE) {
1373 if(!config_extrafontdata)
1377 GfxFont*font = state->getFont();
1379 if(font->getType() == fontType3) {
1380 /* type 3 chars are passed as graphics */
1381 msg("<debug> type3 char at %f/%f", x, y);
1385 Unicode u = uLen?(_u[0]):0;
1386 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);
1388 gfxmatrix_t m = this->current_font_matrix;
1389 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1390 //m.tx += originX; m.ty += originY;
1392 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1393 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1395 msg("<debug> Drawing glyph %d as shape", charid);
1396 if(!gfxglobals->textmodeinfo) {
1397 msg("<notice> Some texts will be rendered as shape");
1398 gfxglobals->textmodeinfo = 1;
1400 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1401 gfxline_t*tglyph = gfxline_clone(glyph);
1402 gfxline_transform(tglyph, &m);
1403 if((render&3) != RENDER_INVISIBLE) {
1404 gfxline_t*add = gfxline_clone(tglyph);
1405 current_text_stroke = gfxline_append(current_text_stroke, add);
1407 if(render&RENDER_CLIP) {
1408 gfxline_t*add = gfxline_clone(tglyph);
1409 current_text_clip = gfxline_append(current_text_clip, add);
1410 if(!current_text_clip) {
1411 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1414 gfxline_free(tglyph);
1418 void GFXOutputDev::endString(GfxState *state)
1420 int render = state->getRender();
1421 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1423 if(current_text_stroke) {
1424 /* fillstroke and stroke text rendering objects we can process right
1425 now (as there may be texts of other rendering modes in this
1426 text object)- clipping objects have to wait until endTextObject,
1428 device->setparameter(device, "mark","TXT");
1429 if((render&3) == RENDER_FILL) {
1430 fillGfxLine(state, current_text_stroke);
1431 gfxline_free(current_text_stroke);
1432 current_text_stroke = 0;
1433 } else if((render&3) == RENDER_FILLSTROKE) {
1434 fillGfxLine(state, current_text_stroke);
1435 strokeGfxline(state, current_text_stroke,0);
1436 gfxline_free(current_text_stroke);
1437 current_text_stroke = 0;
1438 } else if((render&3) == RENDER_STROKE) {
1439 strokeGfxline(state, current_text_stroke,0);
1440 gfxline_free(current_text_stroke);
1441 current_text_stroke = 0;
1443 device->setparameter(device, "mark","");
1447 void GFXOutputDev::endTextObject(GfxState *state)
1449 int render = state->getRender();
1450 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1452 if(current_text_clip) {
1453 device->setparameter(device, "mark","TXT");
1454 clipToGfxLine(state, current_text_clip);
1455 device->setparameter(device, "mark","");
1456 gfxline_free(current_text_clip);
1457 current_text_clip = 0;
1461 /* the logic seems to be as following:
1462 first, beginType3Char is called, with the charcode and the coordinates.
1463 if this function returns true, it already knew about the char and has now drawn it.
1464 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1465 called with some parameters.
1466 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1467 at the position first passed to beginType3Char). the char ends with endType3Char.
1469 The drawing operations between beginType3Char and endType3Char are somewhat different to
1470 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1471 color determines the color of a font)
1474 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1476 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1479 if(config_extrafontdata && current_fontinfo) {
1481 gfxmatrix_t m = this->current_font_matrix;
1482 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1483 m.m00*=INTERNAL_FONT_SIZE;
1484 m.m01*=INTERNAL_FONT_SIZE;
1485 m.m10*=INTERNAL_FONT_SIZE;
1486 m.m11*=INTERNAL_FONT_SIZE;
1488 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1489 msg("<error> Invalid charid %d for font", charid);
1492 gfxcolor_t col={0,0,0,0};
1493 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1494 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1498 /* the character itself is going to be passed using the draw functions */
1499 return gFalse; /* gTrue= is_in_cache? */
1502 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1504 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1507 void GFXOutputDev::endType3Char(GfxState *state)
1510 msg("<debug> endType3Char");
1513 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1515 this->currentpage = pageNum;
1517 int rot = doc->getPageRotate(1);
1518 gfxcolor_t white = {255,255,255,255};
1519 gfxcolor_t black = {255,0,0,0};
1521 gfxline_t clippath[5];
1523 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1524 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1525 Use CropBox, not MediaBox, as page size
1532 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1533 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1535 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1536 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1538 this->clipmovex = -(int)x1;
1539 this->clipmovey = -(int)y1;
1541 /* apply user clip box */
1542 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1543 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1544 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1545 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1546 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1547 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1550 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1552 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);
1554 msg("<verbose> page is rotated %d degrees", rot);
1556 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1557 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1558 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1559 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1560 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1561 device->startclip(device, clippath); outer_clip_box = 1;
1562 if(!config_transparent) {
1563 device->fill(device, clippath, &white);
1565 states[statepos].clipbbox.xmin = x1;
1566 states[statepos].clipbbox.ymin = x1;
1567 states[statepos].clipbbox.xmax = x2;
1568 states[statepos].clipbbox.ymax = y2;
1570 this->dashPattern = 0;
1571 this->dashLength = 0;
1572 this->dashStart = 0;
1576 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1578 double x1, y1, x2, y2;
1579 gfxline_t points[5];
1582 msg("<debug> drawlink");
1584 link->getRect(&x1, &y1, &x2, &y2);
1585 cvtUserToDev(x1, y1, &x, &y);
1586 points[0].type = gfx_moveTo;
1587 points[0].x = points[4].x = x + user_movex + clipmovex;
1588 points[0].y = points[4].y = y + user_movey + clipmovey;
1589 points[0].next = &points[1];
1590 cvtUserToDev(x2, y1, &x, &y);
1591 points[1].type = gfx_lineTo;
1592 points[1].x = x + user_movex + clipmovex;
1593 points[1].y = y + user_movey + clipmovey;
1594 points[1].next = &points[2];
1595 cvtUserToDev(x2, y2, &x, &y);
1596 points[2].type = gfx_lineTo;
1597 points[2].x = x + user_movex + clipmovex;
1598 points[2].y = y + user_movey + clipmovey;
1599 points[2].next = &points[3];
1600 cvtUserToDev(x1, y2, &x, &y);
1601 points[3].type = gfx_lineTo;
1602 points[3].x = x + user_movex + clipmovex;
1603 points[3].y = y + user_movey + clipmovey;
1604 points[3].next = &points[4];
1605 cvtUserToDev(x1, y1, &x, &y);
1606 points[4].type = gfx_lineTo;
1607 points[4].x = x + user_movex + clipmovex;
1608 points[4].y = y + user_movey + clipmovey;
1611 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1612 points[0].x, points[0].y,
1613 points[1].x, points[1].y,
1614 points[2].x, points[2].y,
1615 points[3].x, points[3].y);
1617 if(getLogLevel() >= LOGLEVEL_TRACE) {
1618 dump_outline(points);
1621 LinkAction*action=link->getAction();
1624 const char*type = "-?-";
1627 msg("<trace> drawlink action=%d", action->getKind());
1628 switch(action->getKind())
1632 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1633 LinkDest *dest=NULL;
1634 if (ha->getDest()==NULL)
1635 dest=catalog->findDest(ha->getNamedDest());
1636 else dest=ha->getDest();
1638 if (dest->isPageRef()){
1639 Ref pageref=dest->getPageRef();
1640 page=catalog->findPage(pageref.num,pageref.gen);
1642 else page=dest->getPageNum();
1643 sprintf(buf, "%d", page);
1650 LinkGoToR*l = (LinkGoToR*)action;
1651 GString*g = l->getFileName();
1653 s = strdup(g->getCString());
1655 /* if the GoToR link has no filename, then
1656 try to find a refernce in the *local*
1658 GString*g = l->getNamedDest();
1660 s = strdup(g->getCString());
1666 LinkNamed*l = (LinkNamed*)action;
1667 GString*name = l->getName();
1669 s = strdup(name->lowerCase()->getCString());
1670 named = name->getCString();
1673 if(strstr(s, "next") || strstr(s, "forward"))
1675 page = currentpage + 1;
1677 else if(strstr(s, "prev") || strstr(s, "back"))
1679 page = currentpage - 1;
1681 else if(strstr(s, "last") || strstr(s, "end"))
1683 if(this->page2page && this->num_pages) {
1684 page = this->page2page[this->num_pages-1];
1687 else if(strstr(s, "first") || strstr(s, "top"))
1695 case actionLaunch: {
1697 LinkLaunch*l = (LinkLaunch*)action;
1698 GString * str = new GString(l->getFileName());
1699 GString * params = l->getParams();
1701 str->append(params);
1702 s = strdup(str->getCString());
1709 LinkURI*l = (LinkURI*)action;
1710 GString*g = l->getURI();
1712 url = g->getCString();
1717 case actionUnknown: {
1719 LinkUnknown*l = (LinkUnknown*)action;
1724 msg("<error> Unknown link type!");
1729 if(!s) s = strdup("-?-");
1731 msg("<trace> drawlink s=%s", s);
1733 if(!gfxglobals->linkinfo && (page || s))
1735 msg("<notice> File contains links");
1736 gfxglobals->linkinfo = 1;
1742 for(t=1;t<=this->num_pages;t++) {
1743 if(this->page2page[t]==page) {
1753 sprintf(buf, "page%d", lpage);
1754 device->drawlink(device, points, buf);
1758 device->drawlink(device, points, s);
1761 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1765 void GFXOutputDev::saveState(GfxState *state) {
1766 dbg("saveState %08x", state); dbgindent+=2;
1768 msg("<trace> saveState %08x", state);
1771 msg("<fatal> Too many nested states in pdf.");
1775 states[statepos].state = state;
1776 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1777 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1778 states[statepos].clipping = 0;
1779 states[statepos].olddevice = 0;
1780 states[statepos].clipbbox = states[statepos-1].clipbbox;
1783 void GFXOutputDev::restoreState(GfxState *state) {
1784 dbgindent-=2; dbg("restoreState %08x", state);
1787 msg("<fatal> Invalid restoreState");
1790 msg("<trace> restoreState %08x%s%s", state,
1791 states[statepos].softmask?" (end softmask)":"",
1792 states[statepos].clipping?" (end clipping)":"");
1793 if(states[statepos].softmask) {
1794 clearSoftMask(state);
1798 while(states[statepos].clipping) {
1799 device->endclip(device);
1800 states[statepos].clipping--;
1802 if(states[statepos].state!=state) {
1803 msg("<fatal> bad state nesting");
1806 states[statepos].state=0;
1810 void GFXOutputDev::updateLineDash(GfxState *state)
1812 if(this->dashPattern) {
1813 free(this->dashPattern);this->dashPattern = 0;
1815 double *pattern = 0;
1816 state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1817 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1818 if(!this->dashLength) {
1819 this->dashPattern = 0;
1821 double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1822 memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1823 this->dashPattern = p;
1827 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1829 this->page2page = page2page;
1830 this->num_pages = num_pages;
1833 void GFXOutputDev::updateLineWidth(GfxState *state)
1835 double width = state->getTransformedLineWidth();
1838 void GFXOutputDev::updateLineCap(GfxState *state)
1840 int c = state->getLineCap();
1843 void GFXOutputDev::updateLineJoin(GfxState *state)
1845 int j = state->getLineJoin();
1848 void GFXOutputDev::updateFillColor(GfxState *state)
1851 double opaq = state->getFillOpacity();
1852 state->getFillRGB(&rgb);
1854 void GFXOutputDev::updateFillOpacity(GfxState *state)
1857 double opaq = state->getFillOpacity();
1858 state->getFillRGB(&rgb);
1859 dbg("update fillopaq %f", opaq);
1861 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1863 double opaq = state->getFillOpacity();
1864 dbg("update strokeopaq %f", opaq);
1866 void GFXOutputDev::updateFillOverprint(GfxState *state)
1868 double opaq = state->getFillOverprint();
1869 dbg("update filloverprint %f", opaq);
1871 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1873 double opaq = state->getStrokeOverprint();
1874 dbg("update strokeoverprint %f", opaq);
1876 void GFXOutputDev::updateTransfer(GfxState *state)
1878 dbg("update transfer");
1882 void GFXOutputDev::updateStrokeColor(GfxState *state)
1885 double opaq = state->getStrokeOpacity();
1886 state->getStrokeRGB(&rgb);
1889 void GFXOutputDev::updateFont(GfxState *state)
1891 GfxFont* gfxFont = state->getFont();
1895 char*id = getFontID(gfxFont);
1896 msg("<verbose> Updating font to %s", id);
1897 if(gfxFont->getType() == fontType3) {
1898 infofeature("Type3 fonts");
1899 if(!config_extrafontdata) {
1904 msg("<error> Internal Error: FontID is null");
1908 this->current_fontinfo = this->info->getFont(id);
1910 if(!this->current_fontinfo) {
1911 msg("<error> Internal Error: no fontinfo for font %s", id);
1914 if(!this->current_fontinfo->seen) {
1915 dumpFontInfo("<verbose>", gfxFont);
1918 current_gfxfont = this->current_fontinfo->getGfxFont();
1919 device->addfont(device, current_gfxfont);
1922 updateFontMatrix(state);
1925 #define SQR(x) ((x)*(x))
1927 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1929 if((newwidth<1 || newheight<1) ||
1930 (width<=newwidth || height<=newheight))
1932 unsigned char*newdata;
1934 newdata= (unsigned char*)malloc(newwidth*newheight);
1935 double fx = ((double)width)/newwidth;
1936 double fy = ((double)height)/newheight;
1938 int blocksize = (int)(8192/(fx*fy));
1939 int r = 8192*256/palettesize;
1940 for(x=0;x<newwidth;x++) {
1941 double ex = px + fx;
1942 int fromx = (int)px;
1944 int xweight1 = (int)((1-(px-fromx))*256);
1945 int xweight2 = (int)((ex-tox)*256);
1947 for(y=0;y<newheight;y++) {
1948 double ey = py + fy;
1949 int fromy = (int)py;
1951 int yweight1 = (int)((1-(py-fromy))*256);
1952 int yweight2 = (int)((ey-toy)*256);
1959 for(xx=fromx;xx<=tox;xx++)
1960 for(yy=fromy;yy<=toy;yy++) {
1961 int b = 1-data[width*yy+xx];
1963 if(xx==fromx) weight = (weight*xweight1)/256;
1964 if(xx==tox) weight = (weight*xweight2)/256;
1965 if(yy==fromy) weight = (weight*yweight1)/256;
1966 if(yy==toy) weight = (weight*yweight2)/256;
1969 //if(a) a=(palettesize-1)*r/blocksize;
1970 newdata[y*newwidth+x] = (a*blocksize)/r;
1978 #define IMAGE_TYPE_JPEG 0
1979 #define IMAGE_TYPE_LOSSLESS 1
1981 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1982 double x1,double y1,
1983 double x2,double y2,
1984 double x3,double y3,
1985 double x4,double y4, int type, int multiply)
1987 gfxcolor_t*newpic=0;
1989 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1990 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1992 gfxline_t p1,p2,p3,p4,p5;
1993 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1994 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1995 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1996 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1997 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1999 {p1.x = (int)(p1.x*20)/20.0;
2000 p1.y = (int)(p1.y*20)/20.0;
2001 p2.x = (int)(p2.x*20)/20.0;
2002 p2.y = (int)(p2.y*20)/20.0;
2003 p3.x = (int)(p3.x*20)/20.0;
2004 p3.y = (int)(p3.y*20)/20.0;
2005 p4.x = (int)(p4.x*20)/20.0;
2006 p4.y = (int)(p4.y*20)/20.0;
2007 p5.x = (int)(p5.x*20)/20.0;
2008 p5.y = (int)(p5.y*20)/20.0;
2012 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2013 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2015 m.tx = p1.x - 0.5*multiply;
2016 m.ty = p1.y - 0.5*multiply;
2019 img.data = (gfxcolor_t*)data;
2023 if(type == IMAGE_TYPE_JPEG)
2024 /* TODO: pass image_dpi to device instead */
2025 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2028 dev->fillbitmap(dev, &p1, &img, &m, 0);
2031 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2032 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2034 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2037 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2038 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2040 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2044 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2045 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2046 GBool inlineImg, int mask, int*maskColors,
2047 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2049 /* the code in this function is *old*. It's not pretty, but it works. */
2051 double x1,y1,x2,y2,x3,y3,x4,y4;
2052 ImageStream *imgStr;
2057 unsigned char* maskbitmap = 0;
2060 ncomps = colorMap->getNumPixelComps();
2061 bits = colorMap->getBits();
2066 unsigned char buf[8];
2067 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2069 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2070 imgMaskStr->reset();
2071 unsigned char pal[256];
2072 int n = 1 << colorMap->getBits();
2077 maskColorMap->getGray(pixBuf, &gray);
2078 pal[t] = colToByte(gray);
2080 for (y = 0; y < maskHeight; y++) {
2081 for (x = 0; x < maskWidth; x++) {
2082 imgMaskStr->getPixel(buf);
2083 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2088 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2089 imgMaskStr->reset();
2090 for (y = 0; y < maskHeight; y++) {
2091 for (x = 0; x < maskWidth; x++) {
2092 imgMaskStr->getPixel(buf);
2094 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2102 imgStr = new ImageStream(str, width, ncomps,bits);
2105 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2107 msg("<verbose> Ignoring %d by %d image", width, height);
2108 unsigned char buf[8];
2110 for (y = 0; y < height; ++y)
2111 for (x = 0; x < width; ++x) {
2112 imgStr->getPixel(buf);
2120 this->transformXY(state, 0, 1, &x1, &y1);
2121 this->transformXY(state, 0, 0, &x2, &y2);
2122 this->transformXY(state, 1, 0, &x3, &y3);
2123 this->transformXY(state, 1, 1, &x4, &y4);
2126 /* as type 3 bitmaps are antialized, we need to place them
2127 at integer coordinates, otherwise flash player's antializing
2128 will kick in and make everything blurry */
2129 x1 = (int)(x1);y1 = (int)(y1);
2130 x2 = (int)(x2);y2 = (int)(y2);
2131 x3 = (int)(x3);y3 = (int)(y3);
2132 x4 = (int)(x4);y4 = (int)(y4);
2135 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2137 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2138 gfxglobals->pbminfo = 1;
2141 msg("<verbose> drawing %d by %d masked picture", width, height);
2143 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2144 msg("<notice> File contains jpeg pictures");
2145 gfxglobals->jpeginfo = 1;
2149 unsigned char buf[8];
2151 unsigned char*pic = new unsigned char[width*height];
2152 gfxcolor_t pal[256];
2154 state->getFillRGB(&rgb);
2156 memset(pal,255,sizeof(pal));
2157 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2158 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2159 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2160 pal[0].a = 255; pal[1].a = 0;
2163 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2164 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2165 for (y = 0; y < height; ++y)
2166 for (x = 0; x < width; ++x)
2168 imgStr->getPixel(buf);
2171 pic[width*y+x] = buf[0];
2175 unsigned char*pic2 = 0;
2178 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2187 height = realheight;
2191 /* make a black/white palette */
2193 float r = 255./(float)(numpalette-1);
2195 for(t=0;t<numpalette;t++) {
2196 pal[t].r = colToByte(rgb.r);
2197 pal[t].g = colToByte(rgb.g);
2198 pal[t].b = colToByte(rgb.b);
2199 pal[t].a = (unsigned char)(t*r);
2204 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2205 for (y = 0; y < height; ++y) {
2206 for (x = 0; x < width; ++x) {
2207 pic2[width*y+x] = pal[pic[y*width+x]];
2210 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2214 if(maskbitmap) free(maskbitmap);
2220 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2221 gfxcolor_t*pic=new gfxcolor_t[width*height];
2222 for (y = 0; y < height; ++y) {
2223 for (x = 0; x < width; ++x) {
2224 imgStr->getPixel(pixBuf);
2225 colorMap->getRGB(pixBuf, &rgb);
2226 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2227 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2228 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2229 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2231 int x1 = x*maskWidth/width;
2232 int y1 = y*maskHeight/height;
2233 int x2 = (x+1)*maskWidth/width;
2234 int y2 = (y+1)*maskHeight/height;
2236 unsigned int alpha=0;
2237 unsigned int count=0;
2238 for(xx=x1;xx<x2;xx++)
2239 for(yy=y1;yy<y2;yy++) {
2240 alpha += maskbitmap[yy*maskWidth+xx];
2244 pic[width*y+x].a = alpha / count;
2246 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2251 if(str->getKind()==strDCT)
2252 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2254 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2257 if(maskbitmap) free(maskbitmap);
2260 gfxcolor_t*pic=new gfxcolor_t[width*height];
2261 gfxcolor_t pal[256];
2262 int n = 1 << colorMap->getBits();
2264 for(t=0;t<256;t++) {
2266 colorMap->getRGB(pixBuf, &rgb);
2268 {/*if(maskColors && *maskColors==t) {
2269 msg("<notice> Color %d is transparent", t);
2270 if (imgData->maskColors) {
2272 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2273 if (pix[i] < imgData->maskColors[2*i] ||
2274 pix[i] > imgData->maskColors[2*i+1]) {
2289 pal[t].r = (unsigned char)(colToByte(rgb.r));
2290 pal[t].g = (unsigned char)(colToByte(rgb.g));
2291 pal[t].b = (unsigned char)(colToByte(rgb.b));
2292 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2295 for (y = 0; y < height; ++y) {
2296 for (x = 0; x < width; ++x) {
2297 imgStr->getPixel(pixBuf);
2298 pic[width*y+x] = pal[pixBuf[0]];
2302 if(maskWidth < width && maskHeight < height) {
2303 for(y = 0; y < height; y++) {
2304 for (x = 0; x < width; x++) {
2305 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2309 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2310 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2311 double dx = width / maskWidth;
2312 double dy = height / maskHeight;
2314 for(y = 0; y < maskHeight; y++) {
2316 for (x = 0; x < maskWidth; x++) {
2317 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2318 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2326 height = maskHeight;
2329 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2333 if(maskbitmap) free(maskbitmap);
2338 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2339 int width, int height, GBool invert,
2342 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2343 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2344 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2347 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2348 int width, int height, GfxImageColorMap *colorMap,
2349 int *maskColors, GBool inlineImg)
2351 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2352 colorMap?"colorMap":"no colorMap",
2353 maskColors?"maskColors":"no maskColors",
2355 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2356 colorMap?"colorMap":"no colorMap",
2357 maskColors?"maskColors":"no maskColors",
2360 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2361 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2362 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2365 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2366 int width, int height,
2367 GfxImageColorMap *colorMap,
2368 Stream *maskStr, int maskWidth, int maskHeight,
2371 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2372 colorMap?"colorMap":"no colorMap",
2373 maskWidth, maskHeight);
2374 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2375 colorMap?"colorMap":"no colorMap",
2376 maskWidth, maskHeight);
2378 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2379 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2380 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2383 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2384 int width, int height,
2385 GfxImageColorMap *colorMap,
2387 int maskWidth, int maskHeight,
2388 GfxImageColorMap *maskColorMap)
2390 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2391 colorMap?"colorMap":"no colorMap",
2392 maskWidth, maskHeight);
2393 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2394 colorMap?"colorMap":"no colorMap",
2395 maskWidth, maskHeight);
2397 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2398 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2399 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2402 void GFXOutputDev::stroke(GfxState *state)
2406 GfxPath * path = state->getPath();
2407 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2408 strokeGfxline(state, line, 0);
2412 void GFXOutputDev::fill(GfxState *state)
2414 gfxcolor_t col = getFillColor(state);
2415 dbg("fill %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 if(config_optimize_polygons) {
2420 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2424 fillGfxLine(state, line);
2428 void GFXOutputDev::eoFill(GfxState *state)
2430 gfxcolor_t col = getFillColor(state);
2431 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2433 GfxPath * path = state->getPath();
2434 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2435 fillGfxLine(state, line);
2440 static const char* dirseparator()
2449 void addGlobalFont(const char*filename)
2451 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2452 memset(f, 0, sizeof(fontfile_t));
2453 f->filename = filename;
2454 int len = strlen(filename);
2455 char*r1 = strrchr(filename, '/');
2456 char*r2 = strrchr(filename, '\\');
2464 msg("<notice> Adding font \"%s\".", filename);
2465 if(global_fonts_next) {
2466 global_fonts_next->next = f;
2467 global_fonts_next = global_fonts_next->next;
2469 global_fonts_next = global_fonts = f;
2473 void addGlobalLanguageDir(const char*dir)
2475 msg("<notice> Adding %s to language pack directories", dir);
2478 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2479 strcpy(config_file, dir);
2480 strcat(config_file, dirseparator());
2481 strcat(config_file, "add-to-xpdfrc");
2483 fi = fopen(config_file, "rb");
2485 msg("<error> Could not open %s", config_file);
2488 globalParams->parseFile(new GString(config_file), fi);
2492 void addGlobalFontDir(const char*dirname)
2494 #ifdef HAVE_DIRENT_H
2495 msg("<notice> Adding %s to font directories", dirname);
2496 DIR*dir = opendir(dirname);
2498 msg("<warning> Couldn't open directory %s", dirname);
2503 ent = readdir (dir);
2507 char*name = ent->d_name;
2513 if(!strncasecmp(&name[l-4], ".pfa", 4))
2515 if(!strncasecmp(&name[l-4], ".pfb", 4))
2517 if(!strncasecmp(&name[l-4], ".ttf", 4))
2520 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2521 strcpy(fontname, dirname);
2522 strcat(fontname, dirseparator());
2523 strcat(fontname, name);
2524 addGlobalFont(fontname);
2529 msg("<warning> No dirent.h");
2532 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2533 memset(f, 0, sizeof(fontfile_t));
2534 f->filename = dirname;
2535 if(global_fontdirs_next) {
2536 global_fontdirs_next->next = f;
2537 global_fontdirs_next = global_fontdirs_next->next;
2539 global_fontdirs_next = global_fontdirs = f;
2543 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2544 GfxColorSpace *blendingColorSpace,
2545 GBool isolated, GBool knockout,
2548 const char*colormodename = "";
2550 if(blendingColorSpace) {
2551 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2553 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);
2554 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);
2556 //states[statepos].createsoftmask |= forSoftMask;
2557 states[statepos].createsoftmask = forSoftMask;
2558 states[statepos].transparencygroup = !forSoftMask;
2559 states[statepos].isolated = isolated;
2561 states[statepos].olddevice = this->device;
2562 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2563 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2565 gfxdevice_record_init(this->device);
2567 /*if(!forSoftMask) { ////???
2568 state->setFillOpacity(0.0);
2573 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2576 gfxdevice_t*r = this->device;
2578 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2580 this->device = states[statepos].olddevice;
2582 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2583 /* if these errors occur more often, we should build a seperate
2584 transparency group stack, like xpdf/SplashOutputDev.cc does */
2585 restoreState(state);
2586 this->device = states[statepos].olddevice;
2588 states[statepos].olddevice = 0;
2590 gfxresult_t*recording = r->finish(r);
2592 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2593 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2595 if(states[statepos].createsoftmask) {
2596 states[statepos-1].softmaskrecording = recording;
2598 states[statepos-1].grouprecording = recording;
2601 states[statepos].createsoftmask = 0;
2602 states[statepos].transparencygroup = 0;
2606 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2608 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2609 "colordodge","colorburn","hardlight","softlight","difference",
2610 "exclusion","hue","saturation","color","luminosity"};
2612 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2613 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2615 if(state->getBlendMode() == gfxBlendNormal)
2616 infofeature("transparency groups");
2619 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2620 warnfeature(buffer, 0);
2623 gfxresult_t*grouprecording = states[statepos].grouprecording;
2625 int blendmode = state->getBlendMode();
2626 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2627 int alpha = (int)(state->getFillOpacity()*255);
2628 if(blendmode == gfxBlendMultiply && alpha>200)
2631 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2632 gfxdevice_ops_init(&ops, this->device, alpha);
2633 gfxresult_record_replay(grouprecording, &ops);
2636 grouprecording->destroy(grouprecording);
2638 states[statepos].grouprecording = 0;
2641 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2643 if(states[statepos].softmask) {
2644 /* shouldn't happen, but *does* happen */
2645 clearSoftMask(state);
2648 /* alpha = 1: retrieve mask values from alpha layer
2649 alpha = 0: retrieve mask values from luminance */
2651 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2652 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2653 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2654 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2656 infofeature("soft masks");
2658 warnfeature("soft masks from alpha channel",0);
2660 if(states[statepos].olddevice) {
2661 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2664 states[statepos].olddevice = this->device;
2665 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2666 gfxdevice_record_init(this->device);
2668 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2670 states[statepos].softmask = 1;
2671 states[statepos].softmask_alpha = alpha;
2674 static inline Guchar div255(int x) {
2675 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2678 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2680 if(c < min) c = min;
2681 if(c > max) c = max;
2685 void GFXOutputDev::clearSoftMask(GfxState *state)
2687 if(!states[statepos].softmask)
2689 states[statepos].softmask = 0;
2690 dbg("clearSoftMask statepos=%d", statepos);
2691 msg("<verbose> clearSoftMask statepos=%d", statepos);
2693 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2694 msg("<error> Error in softmask/tgroup ordering");
2698 gfxresult_t*mask = states[statepos].softmaskrecording;
2699 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2700 this->device = states[statepos].olddevice;
2702 /* get outline of all objects below the soft mask */
2703 gfxdevice_t uniondev;
2704 gfxdevice_union_init(&uniondev, 0);
2705 gfxresult_record_replay(below, &uniondev);
2706 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2707 uniondev.finish(&uniondev);
2708 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2709 gfxline_free(belowoutline);belowoutline=0;
2711 this->device->startclip(this->device, belowoutline);
2712 gfxresult_record_replay(below, this->device);
2713 gfxresult_record_replay(mask, this->device);
2714 this->device->endclip(this->device);
2717 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2718 if(width<=0 || height<=0)
2721 gfxdevice_t belowrender;
2722 gfxdevice_render_init(&belowrender);
2723 if(states[statepos+1].isolated) {
2724 belowrender.setparameter(&belowrender, "fillwhite", "1");
2726 belowrender.setparameter(&belowrender, "antialize", "2");
2727 belowrender.startpage(&belowrender, width, height);
2728 gfxresult_record_replay(below, &belowrender);
2729 belowrender.endpage(&belowrender);
2730 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2731 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2732 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2734 gfxdevice_t maskrender;
2735 gfxdevice_render_init(&maskrender);
2736 maskrender.startpage(&maskrender, width, height);
2737 gfxresult_record_replay(mask, &maskrender);
2738 maskrender.endpage(&maskrender);
2739 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2740 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2742 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2743 msg("<fatal> Internal error in mask drawing");
2748 for(y=0;y<height;y++) {
2749 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2750 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2751 for(x=0;x<width;x++) {
2753 if(states[statepos].softmask_alpha) {
2756 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2759 l2->a = div255(alpha*l2->a);
2761 /* DON'T premultiply alpha- this is done by fillbitmap,
2762 depending on the output device */
2763 //l2->r = div255(alpha*l2->r);
2764 //l2->g = div255(alpha*l2->g);
2765 //l2->b = div255(alpha*l2->b);
2771 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2774 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2775 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2777 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2779 mask->destroy(mask);
2780 below->destroy(below);
2781 maskresult->destroy(maskresult);
2782 belowresult->destroy(belowresult);
2783 states[statepos].softmaskrecording = 0;
2788 // public: ~MemCheck()
2790 // delete globalParams;globalParams=0;
2791 // Object::memCheck(stderr);
2792 // gMemReport(stderr);