2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
28 #include "../../config.h"
33 #ifdef HAVE_SYS_STAT_H
36 #ifdef HAVE_FONTCONFIG
37 #include <fontconfig.h>
42 #include <goo/GooString.h>
43 #include <goo/gfile.h>
58 #include "OutputDev.h"
61 #include "NameToUnicodeTable.h"
62 #include "GlobalParams.h"
63 #include "GFXOutputDev.h"
65 // swftools header files
67 #include "../gfxdevice.h"
68 #include "../gfxtools.h"
69 #include "../gfxfont.h"
70 #include "../gfxpoly.h"
71 #include "../devices/record.h"
72 #include "../devices/ops.h"
73 #include "../devices/polyops.h"
74 #include "../devices/render.h"
81 #define SQRT2 1.41421356237309504880
83 typedef struct _fontfile
86 int len; // basename length
88 struct _fontfile*next;
93 static fontfile_t* global_fonts = 0;
94 static fontfile_t* global_fonts_next = 0;
96 static int fontnum = 0;
100 static char* lastfontdir = 0;
111 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
112 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
113 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
114 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
115 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
116 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
117 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
118 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
119 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
120 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
121 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
122 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
123 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
124 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
127 static int verbose = 0;
128 static int dbgindent = 1;
129 static void dbg(const char*format, ...)
136 va_start(arglist, format);
137 vsprintf(buf, format, arglist);
140 while(l && buf[l-1]=='\n') {
145 int indent = dbgindent;
154 GFXOutputGlobals*gfxglobals=0;
156 GFXOutputGlobals::GFXOutputGlobals()
158 this->featurewarnings = 0;
160 this->textmodeinfo = 0;
164 GFXOutputGlobals::~GFXOutputGlobals()
166 feature_t*f = this->featurewarnings;
168 feature_t*next = f->next;
170 free(f->string);f->string =0;
176 this->featurewarnings = 0;
179 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
181 feature_t*f = gfxglobals->featurewarnings;
183 if(!strcmp(feature, f->string))
187 f = (feature_t*)malloc(sizeof(feature_t));
188 f->string = strdup(feature);
189 f->next = gfxglobals->featurewarnings;
190 gfxglobals->featurewarnings = f;
192 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
193 if(this->config_break_on_warning) {
194 msg("<fatal> Aborting conversion due to unsupported feature");
198 msg("<notice> File contains %s",feature);
201 void GFXOutputDev::warnfeature(const char*feature,char fully)
203 showfeature(feature,fully,1);
205 void GFXOutputDev::infofeature(const char*feature)
207 showfeature(feature,0,0);
210 GFXOutputState::GFXOutputState() {
212 this->createsoftmask = 0;
213 this->transparencygroup = 0;
215 this->grouprecording = 0;
219 GBool GFXOutputDev::interpretType3Chars()
224 typedef struct _drawnchar
242 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
243 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
248 free(chars);chars = 0;
255 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
259 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
262 chars[num_chars].x = x;
263 chars[num_chars].y = y;
264 chars[num_chars].color = color;
265 chars[num_chars].charid = charid;
269 char* writeOutStdFont(fontentry* f)
274 char* tmpFileName = mktmpname(namebuf1);
276 sprintf(namebuf2, "%s.afm", tmpFileName);
277 fi = fopen(namebuf2, "wb");
280 fwrite(f->afm, 1, f->afmlen, fi);
283 sprintf(namebuf2, "%s.pfb", tmpFileName);
284 fi = fopen(namebuf2, "wb");
287 fwrite(f->pfb, 1, f->pfblen, fi);
289 return strdup(namebuf2);
291 void unlinkfont(char* filename)
296 msg("<verbose> Removing temporary font file %s", filename);
299 if(!strncmp(&filename[l-4],".afm",4)) {
300 memcpy(&filename[l-4],".pfb",4); unlink(filename);
301 memcpy(&filename[l-4],".pfa",4); unlink(filename);
302 memcpy(&filename[l-4],".afm",4);
305 if(!strncmp(&filename[l-4],".pfa",4)) {
306 memcpy(&filename[l-4],".afm",4); unlink(filename);
307 memcpy(&filename[l-4],".pfa",4);
310 if(!strncmp(&filename[l-4],".pfb",4)) {
311 memcpy(&filename[l-4],".afm",4); unlink(filename);
312 memcpy(&filename[l-4],".pfb",4);
317 static int config_use_fontconfig = 1;
318 static int fcinitcalled = 0;
320 GFXGlobalParams::GFXGlobalParams()
321 : GlobalParams((char*)"")
323 //setupBaseFonts(char *dir); //not tested yet
325 GFXGlobalParams::~GFXGlobalParams()
327 msg("<verbose> Performing cleanups");
329 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
330 if(pdf2t1map[t].fullfilename) {
331 unlinkfont(pdf2t1map[t].fullfilename);
334 #ifdef HAVE_FONTCONFIG
335 if(config_use_fontconfig && fcinitcalled)
339 #ifdef HAVE_FONTCONFIG
340 static char fc_ismatch(FcPattern*match, char*family, char*style)
342 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
343 FcBool scalable=FcFalse, outline=FcFalse;
344 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
345 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
346 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
347 FcPatternGetBool(match, "outline", 0, &outline);
348 FcPatternGetBool(match, "scalable", 0, &scalable);
350 if(scalable!=FcTrue || outline!=FcTrue)
353 if (!strcasecmp(fcfamily, family)) {
354 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
357 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
363 char* fontconfig_searchForFont(char*name)
365 #ifdef HAVE_FONTCONFIG
366 if(!config_use_fontconfig)
369 // call init ony once
373 // check whether we have a config file
374 char* configfile = (char*)FcConfigFilename(0);
375 int configexists = 0;
376 FILE*fi = fopen(configfile, "rb");
378 configexists = 1;fclose(fi);
379 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
381 msg("<debug> Initializing FontConfig (no configfile)");
385 /* A fontconfig instance which didn't find a configfile is unbelievably
386 cranky, so let's just write out a small xml file and make fontconfig
388 FcConfig*c = FcConfigCreate();
390 char* tmpFileName = mktmpname(namebuf);
391 FILE*fi = fopen(tmpFileName, "wb");
392 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
394 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
396 fprintf(fi, "<dir>~/.fonts</dir>\n");
398 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
400 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
401 fprintf(fi, "</fontconfig>\n");
403 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
404 FcConfigBuildFonts(c);
405 FcConfigSetCurrent(c);
409 msg("<debug> FontConfig Initialization failed. Disabling.");
410 config_use_fontconfig = 0;
413 FcConfig * config = FcConfigGetCurrent();
415 msg("<debug> FontConfig Config Initialization failed. Disabling.");
416 config_use_fontconfig = 0;
419 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
420 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
421 if(!set || !set->nfont) {
422 msg("<debug> FontConfig has zero fonts. Disabling.");
423 config_use_fontconfig = 0;
427 if(getLogLevel() >= LOGLEVEL_TRACE) {
429 for(t=0;t<set->nfont;t++) {
430 char*fcfamily=0,*fcstyle=0,*filename=0;
431 FcBool scalable=FcFalse, outline=FcFalse;
432 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
433 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
434 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
435 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
436 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
437 if(scalable && outline) {
438 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
444 char*family = strdup(name);
446 char*dash = strchr(family, '-');
447 FcPattern*pattern = 0;
451 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
452 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
454 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
455 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
459 FcConfigSubstitute(0, pattern, FcMatchPattern);
460 FcDefaultSubstitute(pattern);
462 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
465 for(t=0;t<set->nfont;t++) {
466 FcPattern*match = set->fonts[t];
467 //FcPattern*match = FcFontMatch(0, pattern, &result);
468 if(fc_ismatch(match, family, style)) {
470 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
471 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
474 //FcPatternDestroy(match);
475 msg("<debug> fontconfig: returning filename %s", filename);
477 FcPatternDestroy(pattern);
478 FcFontSetDestroy(set);
479 return filename?strdup(filename):0;
484 FcPatternDestroy(pattern);
485 FcFontSetDestroy(set);
492 static DisplayFontParamKind detectFontType(const char*filename)
494 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
495 return displayFontTT;
496 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
497 return displayFontT1;
498 return displayFontTT;
501 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
503 msg("<verbose> looking for font %s in global params", fontName->getCString());
505 char*name = fontName->getCString();
507 /* see if it is a pdf standard font */
509 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
510 if(!strcmp(name, pdf2t1map[t].pdffont)) {
511 if(!pdf2t1map[t].fullfilename) {
512 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
513 if(!pdf2t1map[t].fullfilename) {
514 msg("<error> Couldn't save default font- is the Temp Directory writable?");
516 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
519 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
520 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
525 int bestlen = 0x7fffffff;
526 const char*bestfilename = 0;
528 fontfile_t*f = global_fonts;
530 if(strstr(f->filename, name)) {
531 if(f->len < bestlen) {
533 bestfilename = f->filename;
539 /* if we didn't find anything up to now, try looking for the
540 font via fontconfig */
543 filename = fontconfig_searchForFont(name);
545 filename = strdup(bestfilename);
549 DisplayFontParamKind kind = detectFontType(filename);
550 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
551 if(kind == displayFontTT) {
552 dfp->tt.fileName = new GString(filename);
554 dfp->t1.fileName = new GString(filename);
559 return GlobalParams::getDisplayFont(fontName);
562 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
565 gfxglobals = new GFXOutputGlobals();
569 this->xref = doc->getXRef();
571 this->type3active = 0;
574 this->user_movex = 0;
575 this->user_movey = 0;
578 this->user_clipx1 = 0;
579 this->user_clipy1 = 0;
580 this->user_clipx2 = 0;
581 this->user_clipy2 = 0;
582 this->current_text_stroke = 0;
583 this->current_text_clip = 0;
584 this->outer_clip_box = 0;
585 this->config_bigchar=0;
586 this->config_convertgradients=1;
587 this->config_break_on_warning=0;
588 this->config_remapunicode=0;
589 this->config_transparent=0;
590 this->config_extrafontdata = 0;
591 this->config_optimize_polygons = 0;
592 this->config_multiply = 1;
593 this->dashPattern = 0;
597 memset(states, 0, sizeof(states));
600 void GFXOutputDev::setParameter(const char*key, const char*value)
602 if(!strcmp(key,"breakonwarning")) {
603 this->config_break_on_warning = atoi(value);
604 } else if(!strcmp(key,"remapunicode")) {
605 this->config_remapunicode = atoi(value);
606 } else if(!strcmp(key,"transparent")) {
607 this->config_transparent = atoi(value);
608 } else if(!strcmp(key,"extrafontdata")) {
609 this->config_extrafontdata = atoi(value);
610 } else if(!strcmp(key,"convertgradients")) {
611 this->config_convertgradients = atoi(value);
612 } else if(!strcmp(key,"multiply")) {
613 this->config_multiply = atoi(value);
614 if(this->config_multiply<1)
615 this->config_multiply=1;
616 } else if(!strcmp(key,"optimize_polygons")) {
617 this->config_optimize_polygons = atoi(value);
621 void GFXOutputDev::setDevice(gfxdevice_t*dev)
626 void GFXOutputDev::setMove(int x,int y)
628 this->user_movex = x;
629 this->user_movey = y;
632 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
634 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
635 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
637 this->user_clipx1 = x1;
638 this->user_clipy1 = y1;
639 this->user_clipx2 = x2;
640 this->user_clipy2 = y2;
643 static char*getFontName(GfxFont*font)
646 GString*gstr = font->getName();
647 char* fname = gstr==0?0:gstr->getCString();
651 sprintf(buf, "UFONT%d", r->num);
652 fontid = strdup(buf);
654 fontid = strdup(fname);
658 char* plus = strchr(fontid, '+');
659 if(plus && plus < &fontid[strlen(fontid)-1]) {
660 fontname = strdup(plus+1);
662 fontname = strdup(fontid);
668 static void dumpFontInfo(const char*loglevel, GfxFont*font);
669 static int lastdumps[1024];
670 static int lastdumppos = 0;
675 static void showFontError(GfxFont*font, int nr)
679 for(t=0;t<lastdumppos;t++)
680 if(lastdumps[t] == r->num)
684 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
685 lastdumps[lastdumppos++] = r->num;
687 msg("<warning> The following font caused problems:");
689 msg("<warning> The following font caused problems (substituting):");
691 msg("<warning> The following Type 3 Font will be rendered as graphics:");
692 dumpFontInfo("<warning>", font);
695 static void dumpFontInfo(const char*loglevel, GfxFont*font)
697 char* id = getFontID(font);
698 char* name = getFontName(font);
699 Ref* r=font->getID();
700 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
702 GString*gstr = font->getTag();
704 msg("%s| Tag: %s", loglevel, id);
706 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
708 GfxFontType type=font->getType();
710 case fontUnknownType:
711 msg("%s| Type: unknown",loglevel);
714 msg("%s| Type: 1",loglevel);
717 msg("%s| Type: 1C",loglevel);
720 msg("%s| Type: 3",loglevel);
723 msg("%s| Type: TrueType",loglevel);
726 msg("%s| Type: CIDType0",loglevel);
729 msg("%s| Type: CIDType0C",loglevel);
732 msg("%s| Type: CIDType2",loglevel);
737 GBool embedded = font->getEmbeddedFontID(&embRef);
739 if(font->getEmbeddedFontName()) {
740 embeddedName = font->getEmbeddedFontName()->getCString();
743 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
745 gstr = font->getExtFontFile();
747 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
749 // Get font descriptor flags.
750 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
751 if(font->isSerif()) msg("%s| is serif", loglevel);
752 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
753 if(font->isItalic()) msg("%s| is italic", loglevel);
754 if(font->isBold()) msg("%s| is bold", loglevel);
760 //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");}
761 //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");}
763 void dump_outline(gfxline_t*line)
766 if(line->type == gfx_moveTo) {
767 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
768 } else if(line->type == gfx_lineTo) {
769 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
770 } else if(line->type == gfx_splineTo) {
771 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
777 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
779 int num = path->getNumSubpaths();
782 double lastx=0,lasty=0,posx=0,posy=0;
785 msg("<warning> empty path");
789 gfxdrawer_target_gfxline(&draw);
791 for(t = 0; t < num; t++) {
792 GfxSubpath *subpath = path->getSubpath(t);
793 int subnum = subpath->getNumPoints();
794 double bx=0,by=0,cx=0,cy=0;
796 for(s=0;s<subnum;s++) {
799 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
802 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
803 draw.lineTo(&draw, lastx, lasty);
805 draw.moveTo(&draw, x,y);
810 } else if(subpath->getCurve(s) && cpos==0) {
814 } else if(subpath->getCurve(s) && cpos==1) {
822 draw.lineTo(&draw, x,y);
824 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
831 /* fix non-closed lines */
832 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
833 draw.lineTo(&draw, lastx, lasty);
835 gfxline_t*result = (gfxline_t*)draw.result(&draw);
837 gfxline_optimize(result);
842 GBool GFXOutputDev::useTilingPatternFill()
844 infofeature("tiled patterns");
845 // if(config_convertgradients)
849 GBool GFXOutputDev::useShadedFills()
851 infofeature("shaded fills");
852 if(config_convertgradients)
857 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
859 state->transform(x,y,nx,ny);
860 *nx += user_movex + clipmovex;
861 *ny += user_movey + clipmovey;
865 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
866 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
867 int paintType, Dict *resDict,
868 double *mat, double *bbox,
869 int x0, int y0, int x1, int y1,
870 double xStep, double yStep)
872 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
873 int paintType, Dict *resDict,
874 double *mat, double *bbox,
875 int x0, int y0, int x1, int y1,
876 double xStep, double yStep)
879 msg("<debug> tilingPatternFill");
880 infofeature("tiling pattern fills");
883 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
885 msg("<error> functionShadedFill not supported yet");
886 infofeature("function shaded fills");
889 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
893 colspace->getRGB(col, &rgb);
894 c.r = colToByte(rgb.r);
895 c.g = colToByte(rgb.g);
896 c.b = colToByte(rgb.b);
901 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
903 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
904 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
907 this->transformXY(state, x0,y0, &x0,&y0);
908 this->transformXY(state, x1,y1, &x1,&y1);
909 this->transformXY(state, x2,y2, &x2,&y2);
914 shading->getColor(0.0, &color0);
915 shading->getColor(0.5, &color1);
916 shading->getColor(1.0, &color2);
918 GfxColorSpace* colspace = shading->getColorSpace();
920 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
921 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
922 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
923 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
924 infofeature("radial shaded fills");
926 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
930 g[0].color = col2col(colspace, &color0);
931 g[1].color = col2col(colspace, &color1);
932 g[2].color = col2col(colspace, &color2);
937 gfxbbox_t b = states[statepos].clipbbox;
938 gfxline_t p1,p2,p3,p4,p5;
939 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
940 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
941 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
942 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
943 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
946 //m.m00 = (x3-x0); m.m10 = (x1-x0);
947 //m.m01 = (y3-y0); m.m11 = (y1-y0);
948 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
949 m.m00 = (x1-x0); m.m10 = (x2-x0);
950 m.m01 = (y1-y0); m.m11 = (y2-y0);
954 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
958 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
961 shading->getCoords(&x0,&y0,&x1,&y1);
962 this->transformXY(state, x0,y0,&x0,&y0);
963 this->transformXY(state, x1,y1,&x1,&y1);
968 shading->getColor(0.0, &color0);
969 shading->getColor(0.5, &color1);
970 shading->getColor(1.0, &color2);
972 GfxColorSpace* colspace = shading->getColorSpace();
974 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
975 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
976 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
977 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
979 infofeature("axial shaded fills");
981 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
985 g[0].color = col2col(colspace, &color0);
986 g[1].color = col2col(colspace, &color1);
987 g[2].color = col2col(colspace, &color2);
992 double xMin,yMin,xMax,yMax;
993 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
994 this->transformXY(state, xMin, yMin, &xMin, &yMin);
995 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
998 xMin = 1024; yMin = 1024;
1000 gfxbbox_t b = states[statepos].clipbbox;
1001 gfxline_t p1,p2,p3,p4,p5;
1002 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1003 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1004 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1005 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1006 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1008 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1009 the middle of the two control points */
1011 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1012 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1013 m.tx = (x0 + x1)/2 - 0.5;
1014 m.ty = (y0 + y1)/2 - 0.5;
1016 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1022 GBool GFXOutputDev::useDrawForm()
1024 infofeature("forms");
1027 void GFXOutputDev::drawForm(Ref id)
1029 msg("<error> drawForm not implemented");
1031 GBool GFXOutputDev::needNonText()
1035 void GFXOutputDev::endPage()
1037 msg("<verbose> endPage (GfxOutputDev)");
1038 if(outer_clip_box) {
1039 device->endclip(device);
1042 this->dashPattern = 0;
1043 /* notice: we're not fully done yet with this page- there might still be
1044 a few calls to drawLink() yet to come */
1047 static inline double sqr(double x) {return x*x;}
1049 #define STROKE_FILL 1
1050 #define STROKE_CLIP 2
1051 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1053 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1054 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1055 double miterLimit = state->getMiterLimit();
1056 double width = state->getTransformedLineWidth();
1059 double opaq = state->getStrokeOpacity();
1061 state->getFillRGB(&rgb);
1063 state->getStrokeRGB(&rgb);
1065 col.r = colToByte(rgb.r);
1066 col.g = colToByte(rgb.g);
1067 col.b = colToByte(rgb.b);
1068 col.a = (unsigned char)(opaq*255);
1070 gfx_capType capType = gfx_capRound;
1071 if(lineCap == 0) capType = gfx_capButt;
1072 else if(lineCap == 1) capType = gfx_capRound;
1073 else if(lineCap == 2) capType = gfx_capSquare;
1075 gfx_joinType joinType = gfx_joinRound;
1076 if(lineJoin == 0) joinType = gfx_joinMiter;
1077 else if(lineJoin == 1) joinType = gfx_joinRound;
1078 else if(lineJoin == 2) joinType = gfx_joinBevel;
1080 gfxline_t*line2 = 0;
1082 if(this->dashLength && this->dashPattern) {
1083 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1086 /* try to find out how much the transformation matrix would
1087 stretch the dashes, and factor that into the dash lengths.
1088 This is not the entirely correct approach- it would be
1089 better to first convert the path to an unscaled version,
1090 then apply dashing, and then transform the path using
1091 the current transformation matrix. However there are few
1092 PDFs which actually stretch a dashed path in a non-orthonormal
1094 double tx1, ty1, tx2, ty2;
1095 this->transformXY(state, 0, 0, &tx1, &ty1);
1096 this->transformXY(state, 1, 1, &tx2, &ty2);
1097 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1099 msg("<trace> %d dashes", this->dashLength);
1100 msg("<trace> | phase: %f", this->dashStart);
1101 for(t=0;t<this->dashLength;t++) {
1102 dash[t] = (float)this->dashPattern[t] * f;
1105 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1107 dash[this->dashLength] = -1;
1108 if(getLogLevel() >= LOGLEVEL_TRACE) {
1112 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1115 msg("<trace> After dashing:");
1118 if(getLogLevel() >= LOGLEVEL_TRACE) {
1119 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1121 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1122 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1124 col.r,col.g,col.b,col.a
1129 if(flags&STROKE_FILL) {
1130 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1131 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1132 if(getLogLevel() >= LOGLEVEL_TRACE) {
1133 dump_outline(gfxline);
1136 msg("<warning> Empty polygon (resulting from stroked line)");
1138 if(flags&STROKE_CLIP) {
1139 device->startclip(device, gfxline);
1140 states[statepos].clipping++;
1142 device->fill(device, gfxline, &col);
1144 gfxline_free(gfxline);
1147 if(flags&STROKE_CLIP)
1148 msg("<error> Stroke&clip not supported at the same time");
1149 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1153 gfxline_free(line2);
1156 gfxcolor_t getFillColor(GfxState * state)
1159 double opaq = state->getFillOpacity();
1160 state->getFillRGB(&rgb);
1162 col.r = colToByte(rgb.r);
1163 col.g = colToByte(rgb.g);
1164 col.b = colToByte(rgb.b);
1165 col.a = (unsigned char)(opaq*255);
1169 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1171 gfxcolor_t col = getFillColor(state);
1173 if(getLogLevel() >= LOGLEVEL_TRACE) {
1174 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1177 device->fill(device, line, &col);
1180 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1182 if(getLogLevel() >= LOGLEVEL_TRACE) {
1185 gfxbbox_t bbox = gfxline_getbbox(line);
1186 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1188 device->startclip(device, line);
1189 states[statepos].clipping++;
1192 void GFXOutputDev::clip(GfxState *state)
1194 GfxPath * path = state->getPath();
1195 msg("<trace> clip");
1196 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1197 if(config_optimize_polygons) {
1198 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1202 clipToGfxLine(state, line);
1206 void GFXOutputDev::eoClip(GfxState *state)
1208 GfxPath * path = state->getPath();
1209 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1210 clipToGfxLine(state, line);
1213 void GFXOutputDev::clipToStrokePath(GfxState *state)
1215 GfxPath * path = state->getPath();
1216 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1218 if(getLogLevel() >= LOGLEVEL_TRACE) {
1219 double width = state->getTransformedLineWidth();
1220 msg("<trace> cliptostrokepath width=%f", width);
1224 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1228 void GFXOutputDev::finish()
1230 if(outer_clip_box) {
1232 device->endclip(device);
1238 GFXOutputDev::~GFXOutputDev()
1241 if(this->dashPattern) {
1242 free(this->dashPattern);this->dashPattern = 0;
1245 GBool GFXOutputDev::upsideDown()
1249 GBool GFXOutputDev::useDrawChar()
1254 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1255 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1257 static char tmp_printstr[4096];
1258 char* makeStringPrintable(char*str)
1260 int len = strlen(str);
1267 for(t=0;t<len;t++) {
1272 tmp_printstr[t] = c;
1275 tmp_printstr[len++] = '.';
1276 tmp_printstr[len++] = '.';
1277 tmp_printstr[len++] = '.';
1279 tmp_printstr[len] = 0;
1280 return tmp_printstr;
1282 void GFXOutputDev::updateFontMatrix(GfxState*state)
1284 double* ctm = state->getCTM();
1285 double fontSize = state->getFontSize();
1286 double*textMat = state->getTextMat();
1288 /* taking the absolute value of horizScaling seems to be required for
1289 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1290 double hscale = fabs(state->getHorizScaling());
1292 // from xpdf-3.02/SplashOutputDev:updateFont
1293 double mm11 = textMat[0] * fontSize * hscale;
1294 double mm12 = textMat[1] * fontSize * hscale;
1295 double mm21 = textMat[2] * fontSize;
1296 double mm22 = textMat[3] * fontSize;
1298 // multiply with ctm, like state->getFontTransMat() does
1299 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1300 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1301 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1302 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1303 this->current_font_matrix.tx = 0;
1304 this->current_font_matrix.ty = 0;
1307 void GFXOutputDev::beginString(GfxState *state, GString *s)
1309 int render = state->getRender();
1310 if(current_text_stroke) {
1311 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1314 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1317 static gfxline_t* mkEmptyGfxShape(double x, double y)
1319 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1320 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1324 static char isValidUnicode(int c)
1326 if(c>=32 && c<0x2fffe)
1331 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1332 double dx, double dy,
1333 double originX, double originY,
1334 CharCode charid, int nBytes, Unicode *_u, int uLen)
1336 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1337 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1341 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1343 int render = state->getRender();
1344 gfxcolor_t col = getFillColor(state);
1346 // check for invisible text -- this is used by Acrobat Capture
1347 if (render == RENDER_INVISIBLE) {
1349 if(!config_extrafontdata)
1353 GfxFont*font = state->getFont();
1355 if(font->getType() == fontType3) {
1356 /* type 3 chars are passed as graphics */
1357 msg("<debug> type3 char at %f/%f", x, y);
1361 Unicode u = uLen?(_u[0]):0;
1362 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);
1364 gfxmatrix_t m = this->current_font_matrix;
1365 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1366 //m.tx += originX; m.ty += originY;
1368 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1369 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1371 msg("<debug> Drawing glyph %d as shape", charid);
1372 if(!gfxglobals->textmodeinfo) {
1373 msg("<notice> Some texts will be rendered as shape");
1374 gfxglobals->textmodeinfo = 1;
1376 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1377 gfxline_t*tglyph = gfxline_clone(glyph);
1378 gfxline_transform(tglyph, &m);
1379 if((render&3) != RENDER_INVISIBLE) {
1380 gfxline_t*add = gfxline_clone(tglyph);
1381 current_text_stroke = gfxline_append(current_text_stroke, add);
1383 if(render&RENDER_CLIP) {
1384 gfxline_t*add = gfxline_clone(tglyph);
1385 current_text_clip = gfxline_append(current_text_clip, add);
1386 if(!current_text_clip) {
1387 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1390 gfxline_free(tglyph);
1394 void GFXOutputDev::endString(GfxState *state)
1396 int render = state->getRender();
1397 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1399 if(current_text_stroke) {
1400 /* fillstroke and stroke text rendering objects we can process right
1401 now (as there may be texts of other rendering modes in this
1402 text object)- clipping objects have to wait until endTextObject,
1404 device->setparameter(device, "mark","TXT");
1405 if((render&3) == RENDER_FILL) {
1406 fillGfxLine(state, current_text_stroke);
1407 gfxline_free(current_text_stroke);
1408 current_text_stroke = 0;
1409 } else if((render&3) == RENDER_FILLSTROKE) {
1410 fillGfxLine(state, current_text_stroke);
1411 strokeGfxline(state, current_text_stroke,0);
1412 gfxline_free(current_text_stroke);
1413 current_text_stroke = 0;
1414 } else if((render&3) == RENDER_STROKE) {
1415 strokeGfxline(state, current_text_stroke,0);
1416 gfxline_free(current_text_stroke);
1417 current_text_stroke = 0;
1419 device->setparameter(device, "mark","");
1423 void GFXOutputDev::endTextObject(GfxState *state)
1425 int render = state->getRender();
1426 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1428 if(current_text_clip) {
1429 device->setparameter(device, "mark","TXT");
1430 clipToGfxLine(state, current_text_clip);
1431 device->setparameter(device, "mark","");
1432 gfxline_free(current_text_clip);
1433 current_text_clip = 0;
1437 /* the logic seems to be as following:
1438 first, beginType3Char is called, with the charcode and the coordinates.
1439 if this function returns true, it already knew about the char and has now drawn it.
1440 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1441 called with some parameters.
1442 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1443 at the position first passed to beginType3Char). the char ends with endType3Char.
1445 The drawing operations between beginType3Char and endType3Char are somewhat different to
1446 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1447 color determines the color of a font)
1450 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1452 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1455 if(config_extrafontdata && current_fontinfo) {
1457 gfxmatrix_t m = this->current_font_matrix;
1458 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1459 m.m00*=INTERNAL_FONT_SIZE;
1460 m.m01*=INTERNAL_FONT_SIZE;
1461 m.m10*=INTERNAL_FONT_SIZE;
1462 m.m11*=INTERNAL_FONT_SIZE;
1464 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1465 msg("<error> Invalid charid %d for font", charid);
1468 gfxcolor_t col={0,0,0,0};
1469 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1470 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1474 /* the character itself is going to be passed using the draw functions */
1475 return gFalse; /* gTrue= is_in_cache? */
1478 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1480 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1483 void GFXOutputDev::endType3Char(GfxState *state)
1486 msg("<debug> endType3Char");
1489 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1491 this->currentpage = pageNum;
1493 int rot = doc->getPageRotate(1);
1494 gfxcolor_t white = {255,255,255,255};
1495 gfxcolor_t black = {255,0,0,0};
1497 gfxline_t clippath[5];
1499 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1500 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1501 Use CropBox, not MediaBox, as page size
1508 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1509 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1511 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1512 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1514 this->clipmovex = -(int)x1;
1515 this->clipmovey = -(int)y1;
1517 /* apply user clip box */
1518 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1519 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1520 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1521 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1522 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1523 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1526 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1528 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);
1530 msg("<verbose> page is rotated %d degrees", rot);
1532 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1533 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1534 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1535 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1536 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1537 device->startclip(device, clippath); outer_clip_box = 1;
1538 if(!config_transparent) {
1539 device->fill(device, clippath, &white);
1541 states[statepos].clipbbox.xmin = x1;
1542 states[statepos].clipbbox.ymin = x1;
1543 states[statepos].clipbbox.xmax = x2;
1544 states[statepos].clipbbox.ymax = y2;
1546 this->dashPattern = 0;
1547 this->dashLength = 0;
1548 this->dashStart = 0;
1552 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1554 double x1, y1, x2, y2;
1555 gfxline_t points[5];
1558 msg("<debug> drawlink");
1560 link->getRect(&x1, &y1, &x2, &y2);
1561 cvtUserToDev(x1, y1, &x, &y);
1562 points[0].type = gfx_moveTo;
1563 points[0].x = points[4].x = x + user_movex + clipmovex;
1564 points[0].y = points[4].y = y + user_movey + clipmovey;
1565 points[0].next = &points[1];
1566 cvtUserToDev(x2, y1, &x, &y);
1567 points[1].type = gfx_lineTo;
1568 points[1].x = x + user_movex + clipmovex;
1569 points[1].y = y + user_movey + clipmovey;
1570 points[1].next = &points[2];
1571 cvtUserToDev(x2, y2, &x, &y);
1572 points[2].type = gfx_lineTo;
1573 points[2].x = x + user_movex + clipmovex;
1574 points[2].y = y + user_movey + clipmovey;
1575 points[2].next = &points[3];
1576 cvtUserToDev(x1, y2, &x, &y);
1577 points[3].type = gfx_lineTo;
1578 points[3].x = x + user_movex + clipmovex;
1579 points[3].y = y + user_movey + clipmovey;
1580 points[3].next = &points[4];
1581 cvtUserToDev(x1, y1, &x, &y);
1582 points[4].type = gfx_lineTo;
1583 points[4].x = x + user_movex + clipmovex;
1584 points[4].y = y + user_movey + clipmovey;
1587 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1588 points[0].x, points[0].y,
1589 points[1].x, points[1].y,
1590 points[2].x, points[2].y,
1591 points[3].x, points[3].y);
1593 if(getLogLevel() >= LOGLEVEL_TRACE) {
1594 dump_outline(points);
1597 LinkAction*action=link->getAction();
1600 const char*type = "-?-";
1603 msg("<trace> drawlink action=%d", action->getKind());
1604 switch(action->getKind())
1608 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1609 LinkDest *dest=NULL;
1610 if (ha->getDest()==NULL)
1611 dest=catalog->findDest(ha->getNamedDest());
1612 else dest=ha->getDest();
1614 if (dest->isPageRef()){
1615 Ref pageref=dest->getPageRef();
1616 page=catalog->findPage(pageref.num,pageref.gen);
1618 else page=dest->getPageNum();
1619 sprintf(buf, "%d", page);
1626 LinkGoToR*l = (LinkGoToR*)action;
1627 GString*g = l->getFileName();
1629 s = strdup(g->getCString());
1631 /* if the GoToR link has no filename, then
1632 try to find a refernce in the *local*
1634 GString*g = l->getNamedDest();
1636 s = strdup(g->getCString());
1642 LinkNamed*l = (LinkNamed*)action;
1643 GString*name = l->getName();
1645 s = strdup(name->lowerCase()->getCString());
1646 named = name->getCString();
1649 if(strstr(s, "next") || strstr(s, "forward"))
1651 page = currentpage + 1;
1653 else if(strstr(s, "prev") || strstr(s, "back"))
1655 page = currentpage - 1;
1657 else if(strstr(s, "last") || strstr(s, "end"))
1659 if(this->page2page && this->num_pages) {
1660 page = this->page2page[this->num_pages-1];
1663 else if(strstr(s, "first") || strstr(s, "top"))
1671 case actionLaunch: {
1673 LinkLaunch*l = (LinkLaunch*)action;
1674 GString * str = new GString(l->getFileName());
1675 GString * params = l->getParams();
1677 str->append(params);
1678 s = strdup(str->getCString());
1685 LinkURI*l = (LinkURI*)action;
1686 GString*g = l->getURI();
1688 url = g->getCString();
1693 case actionUnknown: {
1695 LinkUnknown*l = (LinkUnknown*)action;
1700 msg("<error> Unknown link type!");
1705 if(!s) s = strdup("-?-");
1707 msg("<trace> drawlink s=%s", s);
1709 if(!gfxglobals->linkinfo && (page || s))
1711 msg("<notice> File contains links");
1712 gfxglobals->linkinfo = 1;
1718 for(t=1;t<=this->num_pages;t++) {
1719 if(this->page2page[t]==page) {
1729 sprintf(buf, "page%d", lpage);
1730 device->drawlink(device, points, buf);
1734 device->drawlink(device, points, s);
1737 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1741 void GFXOutputDev::saveState(GfxState *state) {
1742 dbg("saveState %08x", state); dbgindent+=2;
1744 msg("<trace> saveState %08x", state);
1747 msg("<fatal> Too many nested states in pdf.");
1751 states[statepos].state = state;
1752 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1753 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1754 states[statepos].clipping = 0;
1755 states[statepos].olddevice = 0;
1756 states[statepos].clipbbox = states[statepos-1].clipbbox;
1759 void GFXOutputDev::restoreState(GfxState *state) {
1760 dbgindent-=2; dbg("restoreState %08x", state);
1763 msg("<fatal> Invalid restoreState");
1766 msg("<trace> restoreState %08x%s%s", state,
1767 states[statepos].softmask?" (end softmask)":"",
1768 states[statepos].clipping?" (end clipping)":"");
1769 if(states[statepos].softmask) {
1770 clearSoftMask(state);
1774 while(states[statepos].clipping) {
1775 device->endclip(device);
1776 states[statepos].clipping--;
1778 if(states[statepos].state!=state) {
1779 msg("<fatal> bad state nesting");
1782 states[statepos].state=0;
1786 void GFXOutputDev::updateLineDash(GfxState *state)
1788 if(this->dashPattern) {
1789 free(this->dashPattern);this->dashPattern = 0;
1791 double *pattern = 0;
1792 state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1793 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1794 if(!this->dashLength) {
1795 this->dashPattern = 0;
1797 double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1798 memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1799 this->dashPattern = p;
1803 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1805 this->page2page = page2page;
1806 this->num_pages = num_pages;
1809 void GFXOutputDev::updateLineWidth(GfxState *state)
1811 double width = state->getTransformedLineWidth();
1814 void GFXOutputDev::updateLineCap(GfxState *state)
1816 int c = state->getLineCap();
1819 void GFXOutputDev::updateLineJoin(GfxState *state)
1821 int j = state->getLineJoin();
1824 void GFXOutputDev::updateFillColor(GfxState *state)
1827 double opaq = state->getFillOpacity();
1828 state->getFillRGB(&rgb);
1830 void GFXOutputDev::updateFillOpacity(GfxState *state)
1833 double opaq = state->getFillOpacity();
1834 state->getFillRGB(&rgb);
1835 dbg("update fillopaq %f", opaq);
1837 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1839 double opaq = state->getFillOpacity();
1840 dbg("update strokeopaq %f", opaq);
1842 void GFXOutputDev::updateFillOverprint(GfxState *state)
1844 double opaq = state->getFillOverprint();
1845 dbg("update filloverprint %f", opaq);
1847 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1849 double opaq = state->getStrokeOverprint();
1850 dbg("update strokeoverprint %f", opaq);
1852 void GFXOutputDev::updateTransfer(GfxState *state)
1854 dbg("update transfer");
1858 void GFXOutputDev::updateStrokeColor(GfxState *state)
1861 double opaq = state->getStrokeOpacity();
1862 state->getStrokeRGB(&rgb);
1865 void GFXOutputDev::updateFont(GfxState *state)
1867 GfxFont* gfxFont = state->getFont();
1871 char*id = getFontID(gfxFont);
1872 msg("<verbose> Updating font to %s", id);
1873 if(gfxFont->getType() == fontType3) {
1874 infofeature("Type3 fonts");
1875 if(!config_extrafontdata) {
1880 msg("<error> Internal Error: FontID is null");
1884 this->current_fontinfo = this->info->getFont(id);
1885 if(!this->current_fontinfo) {
1886 msg("<error> Internal Error: no fontinfo for font %s", id);
1889 if(!this->current_fontinfo->seen) {
1890 dumpFontInfo("<verbose>", gfxFont);
1893 current_gfxfont = this->current_fontinfo->gfxfont;
1894 device->addfont(device, current_gfxfont);
1897 updateFontMatrix(state);
1900 #define SQR(x) ((x)*(x))
1902 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1904 if((newwidth<1 || newheight<1) ||
1905 (width<=newwidth || height<=newheight))
1907 unsigned char*newdata;
1909 newdata= (unsigned char*)malloc(newwidth*newheight);
1910 double fx = ((double)width)/newwidth;
1911 double fy = ((double)height)/newheight;
1913 int blocksize = (int)(8192/(fx*fy));
1914 int r = 8192*256/palettesize;
1915 for(x=0;x<newwidth;x++) {
1916 double ex = px + fx;
1917 int fromx = (int)px;
1919 int xweight1 = (int)((1-(px-fromx))*256);
1920 int xweight2 = (int)((ex-tox)*256);
1922 for(y=0;y<newheight;y++) {
1923 double ey = py + fy;
1924 int fromy = (int)py;
1926 int yweight1 = (int)((1-(py-fromy))*256);
1927 int yweight2 = (int)((ey-toy)*256);
1934 for(xx=fromx;xx<=tox;xx++)
1935 for(yy=fromy;yy<=toy;yy++) {
1936 int b = 1-data[width*yy+xx];
1938 if(xx==fromx) weight = (weight*xweight1)/256;
1939 if(xx==tox) weight = (weight*xweight2)/256;
1940 if(yy==fromy) weight = (weight*yweight1)/256;
1941 if(yy==toy) weight = (weight*yweight2)/256;
1944 //if(a) a=(palettesize-1)*r/blocksize;
1945 newdata[y*newwidth+x] = (a*blocksize)/r;
1953 #define IMAGE_TYPE_JPEG 0
1954 #define IMAGE_TYPE_LOSSLESS 1
1956 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1957 double x1,double y1,
1958 double x2,double y2,
1959 double x3,double y3,
1960 double x4,double y4, int type, int multiply)
1962 gfxcolor_t*newpic=0;
1964 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1965 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1967 gfxline_t p1,p2,p3,p4,p5;
1968 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1969 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1970 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1971 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1972 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1974 {p1.x = (int)(p1.x*20)/20.0;
1975 p1.y = (int)(p1.y*20)/20.0;
1976 p2.x = (int)(p2.x*20)/20.0;
1977 p2.y = (int)(p2.y*20)/20.0;
1978 p3.x = (int)(p3.x*20)/20.0;
1979 p3.y = (int)(p3.y*20)/20.0;
1980 p4.x = (int)(p4.x*20)/20.0;
1981 p4.y = (int)(p4.y*20)/20.0;
1982 p5.x = (int)(p5.x*20)/20.0;
1983 p5.y = (int)(p5.y*20)/20.0;
1987 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1988 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1990 m.tx = p1.x - 0.5*multiply;
1991 m.ty = p1.y - 0.5*multiply;
1994 img.data = (gfxcolor_t*)data;
1998 if(type == IMAGE_TYPE_JPEG)
1999 /* TODO: pass image_dpi to device instead */
2000 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2003 dev->fillbitmap(dev, &p1, &img, &m, 0);
2006 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2007 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2009 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2012 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2013 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2015 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2019 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2020 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2021 GBool inlineImg, int mask, int*maskColors,
2022 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2024 /* the code in this function is *old*. It's not pretty, but it works. */
2026 double x1,y1,x2,y2,x3,y3,x4,y4;
2027 ImageStream *imgStr;
2032 unsigned char* maskbitmap = 0;
2035 ncomps = colorMap->getNumPixelComps();
2036 bits = colorMap->getBits();
2041 unsigned char buf[8];
2042 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2044 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2045 imgMaskStr->reset();
2046 unsigned char pal[256];
2047 int n = 1 << colorMap->getBits();
2052 maskColorMap->getGray(pixBuf, &gray);
2053 pal[t] = colToByte(gray);
2055 for (y = 0; y < maskHeight; y++) {
2056 for (x = 0; x < maskWidth; x++) {
2057 imgMaskStr->getPixel(buf);
2058 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2063 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2064 imgMaskStr->reset();
2065 for (y = 0; y < maskHeight; y++) {
2066 for (x = 0; x < maskWidth; x++) {
2067 imgMaskStr->getPixel(buf);
2069 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2077 imgStr = new ImageStream(str, width, ncomps,bits);
2080 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2082 msg("<verbose> Ignoring %d by %d image", width, height);
2083 unsigned char buf[8];
2085 for (y = 0; y < height; ++y)
2086 for (x = 0; x < width; ++x) {
2087 imgStr->getPixel(buf);
2095 this->transformXY(state, 0, 1, &x1, &y1);
2096 this->transformXY(state, 0, 0, &x2, &y2);
2097 this->transformXY(state, 1, 0, &x3, &y3);
2098 this->transformXY(state, 1, 1, &x4, &y4);
2101 /* as type 3 bitmaps are antialized, we need to place them
2102 at integer coordinates, otherwise flash player's antializing
2103 will kick in and make everything blurry */
2104 x1 = (int)(x1);y1 = (int)(y1);
2105 x2 = (int)(x2);y2 = (int)(y2);
2106 x3 = (int)(x3);y3 = (int)(y3);
2107 x4 = (int)(x4);y4 = (int)(y4);
2110 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2112 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2113 gfxglobals->pbminfo = 1;
2116 msg("<verbose> drawing %d by %d masked picture", width, height);
2118 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2119 msg("<notice> File contains jpeg pictures");
2120 gfxglobals->jpeginfo = 1;
2124 unsigned char buf[8];
2126 unsigned char*pic = new unsigned char[width*height];
2127 gfxcolor_t pal[256];
2129 state->getFillRGB(&rgb);
2131 memset(pal,255,sizeof(pal));
2132 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2133 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2134 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2135 pal[0].a = 255; pal[1].a = 0;
2138 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2139 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2140 for (y = 0; y < height; ++y)
2141 for (x = 0; x < width; ++x)
2143 imgStr->getPixel(buf);
2146 pic[width*y+x] = buf[0];
2150 unsigned char*pic2 = 0;
2153 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2162 height = realheight;
2166 /* make a black/white palette */
2168 float r = 255./(float)(numpalette-1);
2170 for(t=0;t<numpalette;t++) {
2171 pal[t].r = colToByte(rgb.r);
2172 pal[t].g = colToByte(rgb.g);
2173 pal[t].b = colToByte(rgb.b);
2174 pal[t].a = (unsigned char)(t*r);
2179 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2180 for (y = 0; y < height; ++y) {
2181 for (x = 0; x < width; ++x) {
2182 pic2[width*y+x] = pal[pic[y*width+x]];
2185 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2189 if(maskbitmap) free(maskbitmap);
2195 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2196 gfxcolor_t*pic=new gfxcolor_t[width*height];
2197 for (y = 0; y < height; ++y) {
2198 for (x = 0; x < width; ++x) {
2199 imgStr->getPixel(pixBuf);
2200 colorMap->getRGB(pixBuf, &rgb);
2201 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2202 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2203 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2204 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2206 int x1 = x*maskWidth/width;
2207 int y1 = y*maskHeight/height;
2208 int x2 = (x+1)*maskWidth/width;
2209 int y2 = (y+1)*maskHeight/height;
2211 unsigned int alpha=0;
2212 unsigned int count=0;
2213 for(xx=x1;xx<x2;xx++)
2214 for(yy=y1;yy<y2;yy++) {
2215 alpha += maskbitmap[yy*maskWidth+xx];
2219 pic[width*y+x].a = alpha / count;
2221 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2226 if(str->getKind()==strDCT)
2227 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2229 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2232 if(maskbitmap) free(maskbitmap);
2235 gfxcolor_t*pic=new gfxcolor_t[width*height];
2236 gfxcolor_t pal[256];
2237 int n = 1 << colorMap->getBits();
2239 for(t=0;t<256;t++) {
2241 colorMap->getRGB(pixBuf, &rgb);
2243 {/*if(maskColors && *maskColors==t) {
2244 msg("<notice> Color %d is transparent", t);
2245 if (imgData->maskColors) {
2247 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2248 if (pix[i] < imgData->maskColors[2*i] ||
2249 pix[i] > imgData->maskColors[2*i+1]) {
2264 pal[t].r = (unsigned char)(colToByte(rgb.r));
2265 pal[t].g = (unsigned char)(colToByte(rgb.g));
2266 pal[t].b = (unsigned char)(colToByte(rgb.b));
2267 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2270 for (y = 0; y < height; ++y) {
2271 for (x = 0; x < width; ++x) {
2272 imgStr->getPixel(pixBuf);
2273 pic[width*y+x] = pal[pixBuf[0]];
2277 if(maskWidth < width && maskHeight < height) {
2278 for(y = 0; y < height; y++) {
2279 for (x = 0; x < width; x++) {
2280 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2284 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2285 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2286 double dx = width / maskWidth;
2287 double dy = height / maskHeight;
2289 for(y = 0; y < maskHeight; y++) {
2291 for (x = 0; x < maskWidth; x++) {
2292 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2293 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2301 height = maskHeight;
2304 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2308 if(maskbitmap) free(maskbitmap);
2313 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2314 int width, int height, GBool invert,
2317 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2318 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2319 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2322 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2323 int width, int height, GfxImageColorMap *colorMap,
2324 int *maskColors, GBool inlineImg)
2326 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2327 colorMap?"colorMap":"no colorMap",
2328 maskColors?"maskColors":"no maskColors",
2330 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2331 colorMap?"colorMap":"no colorMap",
2332 maskColors?"maskColors":"no maskColors",
2335 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2336 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2337 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2340 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2341 int width, int height,
2342 GfxImageColorMap *colorMap,
2343 Stream *maskStr, int maskWidth, int maskHeight,
2346 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2347 colorMap?"colorMap":"no colorMap",
2348 maskWidth, maskHeight);
2349 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2350 colorMap?"colorMap":"no colorMap",
2351 maskWidth, maskHeight);
2353 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2354 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2355 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2358 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2359 int width, int height,
2360 GfxImageColorMap *colorMap,
2362 int maskWidth, int maskHeight,
2363 GfxImageColorMap *maskColorMap)
2365 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2366 colorMap?"colorMap":"no colorMap",
2367 maskWidth, maskHeight);
2368 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2369 colorMap?"colorMap":"no colorMap",
2370 maskWidth, maskHeight);
2372 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2373 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2374 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2377 void GFXOutputDev::stroke(GfxState *state)
2381 GfxPath * path = state->getPath();
2382 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2383 strokeGfxline(state, line, 0);
2387 void GFXOutputDev::fill(GfxState *state)
2389 gfxcolor_t col = getFillColor(state);
2390 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2392 GfxPath * path = state->getPath();
2393 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2394 if(config_optimize_polygons) {
2395 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2399 fillGfxLine(state, line);
2403 void GFXOutputDev::eoFill(GfxState *state)
2405 gfxcolor_t col = getFillColor(state);
2406 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2408 GfxPath * path = state->getPath();
2409 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2410 fillGfxLine(state, line);
2415 static const char* dirseparator()
2424 void addGlobalFont(const char*filename)
2426 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2427 memset(f, 0, sizeof(fontfile_t));
2428 f->filename = filename;
2429 int len = strlen(filename);
2430 char*r1 = strrchr(filename, '/');
2431 char*r2 = strrchr(filename, '\\');
2439 msg("<notice> Adding font \"%s\".", filename);
2440 if(global_fonts_next) {
2441 global_fonts_next->next = f;
2442 global_fonts_next = global_fonts_next->next;
2444 global_fonts_next = global_fonts = f;
2448 void addGlobalLanguageDir(const char*dir)
2450 msg("<notice> Adding %s to language pack directories", dir);
2453 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2454 strcpy(config_file, dir);
2455 strcat(config_file, dirseparator());
2456 strcat(config_file, "add-to-xpdfrc");
2458 fi = fopen(config_file, "rb");
2460 msg("<error> Could not open %s", config_file);
2463 globalParams->parseFile(new GString(config_file), fi);
2467 void addGlobalFontDir(const char*dirname)
2469 #ifdef HAVE_DIRENT_H
2470 msg("<notice> Adding %s to font directories", dirname);
2471 lastfontdir = strdup(dirname);
2472 DIR*dir = opendir(dirname);
2474 msg("<warning> Couldn't open directory %s", dirname);
2479 ent = readdir (dir);
2483 char*name = ent->d_name;
2489 if(!strncasecmp(&name[l-4], ".pfa", 4))
2491 if(!strncasecmp(&name[l-4], ".pfb", 4))
2493 if(!strncasecmp(&name[l-4], ".ttf", 4))
2496 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2497 strcpy(fontname, dirname);
2498 strcat(fontname, dirseparator());
2499 strcat(fontname, name);
2500 addGlobalFont(fontname);
2505 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2509 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2510 GfxColorSpace *blendingColorSpace,
2511 GBool isolated, GBool knockout,
2514 const char*colormodename = "";
2516 if(blendingColorSpace) {
2517 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2519 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);
2520 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);
2522 //states[statepos].createsoftmask |= forSoftMask;
2523 states[statepos].createsoftmask = forSoftMask;
2524 states[statepos].transparencygroup = !forSoftMask;
2525 states[statepos].isolated = isolated;
2527 states[statepos].olddevice = this->device;
2528 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2529 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2531 gfxdevice_record_init(this->device);
2533 /*if(!forSoftMask) { ////???
2534 state->setFillOpacity(0.0);
2539 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2542 gfxdevice_t*r = this->device;
2544 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2546 this->device = states[statepos].olddevice;
2548 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2549 /* if these errors occur more often, we should build a seperate
2550 transparency group stack, like xpdf/SplashOutputDev.cc does */
2551 restoreState(state);
2552 this->device = states[statepos].olddevice;
2554 states[statepos].olddevice = 0;
2556 gfxresult_t*recording = r->finish(r);
2558 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2559 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2561 if(states[statepos].createsoftmask) {
2562 states[statepos-1].softmaskrecording = recording;
2564 states[statepos-1].grouprecording = recording;
2567 states[statepos].createsoftmask = 0;
2568 states[statepos].transparencygroup = 0;
2572 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2574 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2575 "colordodge","colorburn","hardlight","softlight","difference",
2576 "exclusion","hue","saturation","color","luminosity"};
2578 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2579 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2581 if(state->getBlendMode() == gfxBlendNormal)
2582 infofeature("transparency groups");
2585 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2586 warnfeature(buffer, 0);
2589 gfxresult_t*grouprecording = states[statepos].grouprecording;
2591 int blendmode = state->getBlendMode();
2592 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2593 int alpha = (int)(state->getFillOpacity()*255);
2594 if(blendmode == gfxBlendMultiply && alpha>200)
2597 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2598 gfxdevice_ops_init(&ops, this->device, alpha);
2599 gfxresult_record_replay(grouprecording, &ops);
2602 grouprecording->destroy(grouprecording);
2604 states[statepos].grouprecording = 0;
2607 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2609 if(states[statepos].softmask) {
2610 /* shouldn't happen, but *does* happen */
2611 clearSoftMask(state);
2614 /* alpha = 1: retrieve mask values from alpha layer
2615 alpha = 0: retrieve mask values from luminance */
2617 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2618 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2619 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2620 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2622 infofeature("soft masks");
2624 warnfeature("soft masks from alpha channel",0);
2626 if(states[statepos].olddevice) {
2627 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2630 states[statepos].olddevice = this->device;
2631 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2632 gfxdevice_record_init(this->device);
2634 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2636 states[statepos].softmask = 1;
2637 states[statepos].softmask_alpha = alpha;
2640 static inline Guchar div255(int x) {
2641 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2644 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2646 if(c < min) c = min;
2647 if(c > max) c = max;
2651 void GFXOutputDev::clearSoftMask(GfxState *state)
2653 if(!states[statepos].softmask)
2655 states[statepos].softmask = 0;
2656 dbg("clearSoftMask statepos=%d", statepos);
2657 msg("<verbose> clearSoftMask statepos=%d", statepos);
2659 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2660 msg("<error> Error in softmask/tgroup ordering");
2664 gfxresult_t*mask = states[statepos].softmaskrecording;
2665 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2666 this->device = states[statepos].olddevice;
2668 /* get outline of all objects below the soft mask */
2669 gfxdevice_t uniondev;
2670 gfxdevice_union_init(&uniondev, 0);
2671 gfxresult_record_replay(below, &uniondev);
2672 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2673 uniondev.finish(&uniondev);
2674 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2675 gfxline_free(belowoutline);belowoutline=0;
2677 this->device->startclip(this->device, belowoutline);
2678 gfxresult_record_replay(below, this->device);
2679 gfxresult_record_replay(mask, this->device);
2680 this->device->endclip(this->device);
2683 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2684 if(width<=0 || height<=0)
2687 gfxdevice_t belowrender;
2688 gfxdevice_render_init(&belowrender);
2689 if(states[statepos+1].isolated) {
2690 belowrender.setparameter(&belowrender, "fillwhite", "1");
2692 belowrender.setparameter(&belowrender, "antialize", "2");
2693 belowrender.startpage(&belowrender, width, height);
2694 gfxresult_record_replay(below, &belowrender);
2695 belowrender.endpage(&belowrender);
2696 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2697 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2698 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2700 gfxdevice_t maskrender;
2701 gfxdevice_render_init(&maskrender);
2702 maskrender.startpage(&maskrender, width, height);
2703 gfxresult_record_replay(mask, &maskrender);
2704 maskrender.endpage(&maskrender);
2705 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2706 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2708 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2709 msg("<fatal> Internal error in mask drawing");
2714 for(y=0;y<height;y++) {
2715 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2716 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2717 for(x=0;x<width;x++) {
2719 if(states[statepos].softmask_alpha) {
2722 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2725 l2->a = div255(alpha*l2->a);
2727 /* DON'T premultiply alpha- this is done by fillbitmap,
2728 depending on the output device */
2729 //l2->r = div255(alpha*l2->r);
2730 //l2->g = div255(alpha*l2->g);
2731 //l2->b = div255(alpha*l2->b);
2737 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2740 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2741 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2743 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2745 mask->destroy(mask);
2746 below->destroy(below);
2747 maskresult->destroy(maskresult);
2748 belowresult->destroy(belowresult);
2749 states[statepos].softmaskrecording = 0;
2754 // public: ~MemCheck()
2756 // delete globalParams;globalParams=0;
2757 // Object::memCheck(stderr);
2758 // gMemReport(stderr);