2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
28 #include "../../config.h"
33 #ifdef HAVE_SYS_STAT_H
36 #ifdef HAVE_FONTCONFIG
37 #include <fontconfig.h>
42 #include <goo/GooString.h>
43 #include <goo/gfile.h>
58 #include "OutputDev.h"
61 #include "NameToUnicodeTable.h"
62 #include "GlobalParams.h"
63 #include "GFXOutputDev.h"
65 // swftools header files
67 #include "../gfxdevice.h"
68 #include "../gfxtools.h"
69 #include "../gfxfont.h"
70 #include "../gfxpoly.h"
71 #include "../devices/record.h"
72 #include "../devices/ops.h"
73 #include "../devices/polyops.h"
74 #include "../devices/render.h"
81 #define SQRT2 1.41421356237309504880
83 typedef struct _fontfile
86 int len; // basename length
88 struct _fontfile*next;
93 static fontfile_t* global_fonts = 0;
94 static fontfile_t* global_fonts_next = 0;
96 static int fontnum = 0;
100 static char* lastfontdir = 0;
111 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
112 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
113 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
114 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
115 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
116 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
117 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
118 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
119 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
120 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
121 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
122 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
123 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
124 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
127 static int verbose = 0;
128 static int dbgindent = 1;
129 static void dbg(const char*format, ...)
136 va_start(arglist, format);
137 vsprintf(buf, format, arglist);
140 while(l && buf[l-1]=='\n') {
145 int indent = dbgindent;
155 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
157 feature_t*f = this->featurewarnings;
159 if(!strcmp(feature, f->string))
163 f = (feature_t*)malloc(sizeof(feature_t));
164 f->string = strdup(feature);
165 f->next = this->featurewarnings;
166 this->featurewarnings = f;
168 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
169 if(this->config_break_on_warning) {
170 msg("<fatal> Aborting conversion due to unsupported feature");
174 msg("<notice> File contains %s",feature);
177 void GFXOutputDev::warnfeature(const char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(const char*feature)
183 showfeature(feature,0,0);
186 GFXOutputState::GFXOutputState() {
188 this->createsoftmask = 0;
189 this->transparencygroup = 0;
191 this->grouprecording = 0;
195 GBool GFXOutputDev::interpretType3Chars()
200 typedef struct _drawnchar
218 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
219 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224 free(chars);chars = 0;
231 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
235 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
238 chars[num_chars].x = x;
239 chars[num_chars].y = y;
240 chars[num_chars].color = color;
241 chars[num_chars].charid = charid;
245 char* writeOutStdFont(fontentry* f)
250 char* tmpFileName = mktmpname(namebuf1);
252 sprintf(namebuf2, "%s.afm", tmpFileName);
253 fi = fopen(namebuf2, "wb");
256 fwrite(f->afm, 1, f->afmlen, fi);
259 sprintf(namebuf2, "%s.pfb", tmpFileName);
260 fi = fopen(namebuf2, "wb");
263 fwrite(f->pfb, 1, f->pfblen, fi);
265 return strdup(namebuf2);
267 void unlinkfont(char* filename)
272 msg("<verbose> Removing temporary font file %s", filename);
275 if(!strncmp(&filename[l-4],".afm",4)) {
276 memcpy(&filename[l-4],".pfb",4); unlink(filename);
277 memcpy(&filename[l-4],".pfa",4); unlink(filename);
278 memcpy(&filename[l-4],".afm",4);
281 if(!strncmp(&filename[l-4],".pfa",4)) {
282 memcpy(&filename[l-4],".afm",4); unlink(filename);
283 memcpy(&filename[l-4],".pfa",4);
286 if(!strncmp(&filename[l-4],".pfb",4)) {
287 memcpy(&filename[l-4],".afm",4); unlink(filename);
288 memcpy(&filename[l-4],".pfb",4);
293 static int config_use_fontconfig = 1;
294 static int fcinitcalled = 0;
296 GFXGlobalParams::GFXGlobalParams()
299 //setupBaseFonts(char *dir); //not tested yet
301 GFXGlobalParams::~GFXGlobalParams()
303 msg("<verbose> Performing cleanups");
305 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
306 if(pdf2t1map[t].fullfilename) {
307 unlinkfont(pdf2t1map[t].fullfilename);
310 #ifdef HAVE_FONTCONFIG
311 if(config_use_fontconfig && fcinitcalled)
315 #ifdef HAVE_FONTCONFIG
316 static char fc_ismatch(FcPattern*match, char*family, char*style)
318 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
319 FcBool scalable=FcFalse, outline=FcFalse;
320 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
321 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
322 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
323 FcPatternGetBool(match, "outline", 0, &outline);
324 FcPatternGetBool(match, "scalable", 0, &scalable);
326 if(scalable!=FcTrue || outline!=FcTrue)
329 if (!strcasecmp(fcfamily, family)) {
330 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
333 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
339 char* fontconfig_searchForFont(char*name)
341 #ifdef HAVE_FONTCONFIG
342 if(!config_use_fontconfig)
345 // call init ony once
349 // check whether we have a config file
350 char* configfile = (char*)FcConfigFilename(0);
351 int configexists = 0;
352 FILE*fi = fopen(configfile, "rb");
354 configexists = 1;fclose(fi);
355 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
357 msg("<debug> Initializing FontConfig (no configfile)");
361 /* A fontconfig instance which didn't find a configfile is unbelievably
362 cranky, so let's just write out a small xml file and make fontconfig
364 FcConfig*c = FcConfigCreate();
366 char* tmpFileName = mktmpname(namebuf);
367 FILE*fi = fopen(tmpFileName, "wb");
368 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
370 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
372 fprintf(fi, "<dir>~/.fonts</dir>\n");
374 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
376 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
377 fprintf(fi, "</fontconfig>\n");
379 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
380 FcConfigBuildFonts(c);
381 FcConfigSetCurrent(c);
385 msg("<debug> FontConfig Initialization failed. Disabling.");
386 config_use_fontconfig = 0;
389 FcConfig * config = FcConfigGetCurrent();
391 msg("<debug> FontConfig Config Initialization failed. Disabling.");
392 config_use_fontconfig = 0;
395 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
396 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
397 if(!set || !set->nfont) {
398 msg("<debug> FontConfig has zero fonts. Disabling.");
399 config_use_fontconfig = 0;
403 if(getLogLevel() >= LOGLEVEL_TRACE) {
405 for(t=0;t<set->nfont;t++) {
406 char*fcfamily=0,*fcstyle=0,*filename=0;
407 FcBool scalable=FcFalse, outline=FcFalse;
408 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
409 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
410 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
411 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
412 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
413 if(scalable && outline) {
414 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
420 char*family = strdup(name);
422 char*dash = strchr(family, '-');
423 FcPattern*pattern = 0;
427 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
428 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
430 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
431 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
435 FcConfigSubstitute(0, pattern, FcMatchPattern);
436 FcDefaultSubstitute(pattern);
438 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
441 for(t=0;t<set->nfont;t++) {
442 FcPattern*match = set->fonts[t];
443 //FcPattern*match = FcFontMatch(0, pattern, &result);
444 if(fc_ismatch(match, family, style)) {
446 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
447 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
450 //FcPatternDestroy(match);
451 msg("<debug> fontconfig: returning filename %s", filename);
453 FcPatternDestroy(pattern);
454 FcFontSetDestroy(set);
455 return filename?strdup(filename):0;
460 FcPatternDestroy(pattern);
461 FcFontSetDestroy(set);
468 static DisplayFontParamKind detectFontType(const char*filename)
470 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
471 return displayFontTT;
472 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
473 return displayFontT1;
474 return displayFontTT;
477 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
479 msg("<verbose> looking for font %s in global params", fontName->getCString());
481 char*name = fontName->getCString();
483 /* see if it is a pdf standard font */
485 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
486 if(!strcmp(name, pdf2t1map[t].pdffont)) {
487 if(!pdf2t1map[t].fullfilename) {
488 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
489 if(!pdf2t1map[t].fullfilename) {
490 msg("<error> Couldn't save default font- is the Temp Directory writable?");
492 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
495 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
496 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
501 int bestlen = 0x7fffffff;
502 const char*bestfilename = 0;
504 fontfile_t*f = global_fonts;
506 if(strstr(f->filename, name)) {
507 if(f->len < bestlen) {
509 bestfilename = f->filename;
515 /* if we didn't find anything up to now, try looking for the
516 font via fontconfig */
519 filename = fontconfig_searchForFont(name);
521 filename = strdup(bestfilename);
525 DisplayFontParamKind kind = detectFontType(filename);
526 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
527 if(kind == displayFontTT) {
528 dfp->tt.fileName = new GString(filename);
530 dfp->t1.fileName = new GString(filename);
535 return GlobalParams::getDisplayFont(fontName);
538 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
542 this->xref = doc->getXRef();
545 this->textmodeinfo = 0;
548 this->type3active = 0;
551 this->substitutepos = 0;
552 this->type3Warning = 0;
553 this->user_movex = 0;
554 this->user_movey = 0;
557 this->user_clipx1 = 0;
558 this->user_clipy1 = 0;
559 this->user_clipx2 = 0;
560 this->user_clipy2 = 0;
561 this->current_text_stroke = 0;
562 this->current_text_clip = 0;
563 this->outer_clip_box = 0;
565 this->pagebuflen = 0;
567 this->config_bigchar=0;
568 this->config_convertgradients=1;
569 this->config_break_on_warning=0;
570 this->config_remapunicode=0;
571 this->config_transparent=0;
572 this->config_extrafontdata = 0;
573 this->config_fontquality = 10;
574 this->config_optimize_polygons = 0;
575 this->config_multiply = 1;
577 this->gfxfontlist = gfxfontlist_create();
578 this->dashPattern = 0;
580 memset(states, 0, sizeof(states));
581 this->featurewarnings = 0;
584 void GFXOutputDev::setParameter(const char*key, const char*value)
586 if(!strcmp(key,"breakonwarning")) {
587 this->config_break_on_warning = atoi(value);
588 } else if(!strcmp(key,"remapunicode")) {
589 this->config_remapunicode = atoi(value);
590 } else if(!strcmp(key,"transparent")) {
591 this->config_transparent = atoi(value);
592 } else if(!strcmp(key,"extrafontdata")) {
593 this->config_extrafontdata = atoi(value);
594 } else if(!strcmp(key,"convertgradients")) {
595 this->config_convertgradients = atoi(value);
596 } else if(!strcmp(key,"multiply")) {
597 this->config_multiply = atoi(value);
598 if(this->config_multiply<1)
599 this->config_multiply=1;
600 } else if(!strcmp(key,"optimize_polygons")) {
601 this->config_optimize_polygons = atoi(value);
602 } else if(!strcmp(key,"bigchar")) {
603 this->config_bigchar = atoi(value);
604 } else if(!strcmp(key,"fontquality")) {
605 this->config_fontquality = atof(value);
606 if(this->config_fontquality<=1)
607 this->config_fontquality=1;
612 void GFXOutputDev::setDevice(gfxdevice_t*dev)
617 void GFXOutputDev::setMove(int x,int y)
619 this->user_movex = x;
620 this->user_movey = y;
623 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
625 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
626 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
628 this->user_clipx1 = x1;
629 this->user_clipy1 = y1;
630 this->user_clipx2 = x2;
631 this->user_clipy2 = y2;
634 static char*getFontName(GfxFont*font)
637 GString*gstr = font->getName();
638 char* fname = gstr==0?0:gstr->getCString();
642 sprintf(buf, "UFONT%d", r->num);
643 fontid = strdup(buf);
645 fontid = strdup(fname);
649 char* plus = strchr(fontid, '+');
650 if(plus && plus < &fontid[strlen(fontid)-1]) {
651 fontname = strdup(plus+1);
653 fontname = strdup(fontid);
659 static void dumpFontInfo(const char*loglevel, GfxFont*font);
660 static int lastdumps[1024];
661 static int lastdumppos = 0;
666 static void showFontError(GfxFont*font, int nr)
670 for(t=0;t<lastdumppos;t++)
671 if(lastdumps[t] == r->num)
675 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
676 lastdumps[lastdumppos++] = r->num;
678 msg("<warning> The following font caused problems:");
680 msg("<warning> The following font caused problems (substituting):");
682 msg("<warning> The following Type 3 Font will be rendered as graphics:");
683 dumpFontInfo("<warning>", font);
686 static void dumpFontInfo(const char*loglevel, GfxFont*font)
688 char* id = getFontID(font);
689 char* name = getFontName(font);
690 Ref* r=font->getID();
691 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
693 GString*gstr = font->getTag();
695 msg("%s| Tag: %s", loglevel, id);
697 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
699 GfxFontType type=font->getType();
701 case fontUnknownType:
702 msg("%s| Type: unknown",loglevel);
705 msg("%s| Type: 1",loglevel);
708 msg("%s| Type: 1C",loglevel);
711 msg("%s| Type: 3",loglevel);
714 msg("%s| Type: TrueType",loglevel);
717 msg("%s| Type: CIDType0",loglevel);
720 msg("%s| Type: CIDType0C",loglevel);
723 msg("%s| Type: CIDType2",loglevel);
728 GBool embedded = font->getEmbeddedFontID(&embRef);
730 if(font->getEmbeddedFontName()) {
731 embeddedName = font->getEmbeddedFontName()->getCString();
734 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
736 gstr = font->getExtFontFile();
738 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
740 // Get font descriptor flags.
741 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
742 if(font->isSerif()) msg("%s| is serif", loglevel);
743 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
744 if(font->isItalic()) msg("%s| is italic", loglevel);
745 if(font->isBold()) msg("%s| is bold", loglevel);
751 //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");}
752 //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");}
754 void dump_outline(gfxline_t*line)
757 if(line->type == gfx_moveTo) {
758 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
759 } else if(line->type == gfx_lineTo) {
760 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
761 } else if(line->type == gfx_splineTo) {
762 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
768 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
770 int num = path->getNumSubpaths();
773 double lastx=0,lasty=0,posx=0,posy=0;
776 msg("<warning> empty path");
780 gfxdrawer_target_gfxline(&draw);
782 for(t = 0; t < num; t++) {
783 GfxSubpath *subpath = path->getSubpath(t);
784 int subnum = subpath->getNumPoints();
785 double bx=0,by=0,cx=0,cy=0;
787 for(s=0;s<subnum;s++) {
790 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
793 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
794 draw.lineTo(&draw, lastx, lasty);
796 draw.moveTo(&draw, x,y);
801 } else if(subpath->getCurve(s) && cpos==0) {
805 } else if(subpath->getCurve(s) && cpos==1) {
813 draw.lineTo(&draw, x,y);
815 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
822 /* fix non-closed lines */
823 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
824 draw.lineTo(&draw, lastx, lasty);
826 gfxline_t*result = (gfxline_t*)draw.result(&draw);
828 gfxline_optimize(result);
833 GBool GFXOutputDev::useTilingPatternFill()
835 infofeature("tiled patterns");
836 // if(config_convertgradients)
840 GBool GFXOutputDev::useShadedFills()
842 infofeature("shaded fills");
843 if(config_convertgradients)
848 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
850 state->transform(x,y,nx,ny);
851 *nx += user_movex + clipmovex;
852 *ny += user_movey + clipmovey;
856 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
857 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
858 int paintType, Dict *resDict,
859 double *mat, double *bbox,
860 int x0, int y0, int x1, int y1,
861 double xStep, double yStep)
863 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
864 int paintType, Dict *resDict,
865 double *mat, double *bbox,
866 int x0, int y0, int x1, int y1,
867 double xStep, double yStep)
870 msg("<debug> tilingPatternFill");
871 infofeature("tiling pattern fills");
874 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
876 msg("<error> functionShadedFill not supported yet");
877 infofeature("function shaded fills");
880 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
884 colspace->getRGB(col, &rgb);
885 c.r = colToByte(rgb.r);
886 c.g = colToByte(rgb.g);
887 c.b = colToByte(rgb.b);
892 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
894 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
895 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
898 this->transformXY(state, x0,y0, &x0,&y0);
899 this->transformXY(state, x1,y1, &x1,&y1);
900 this->transformXY(state, x2,y2, &x2,&y2);
905 shading->getColor(0.0, &color0);
906 shading->getColor(0.5, &color1);
907 shading->getColor(1.0, &color2);
909 GfxColorSpace* colspace = shading->getColorSpace();
911 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
912 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
913 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
914 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
915 infofeature("radial shaded fills");
917 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
921 g[0].color = col2col(colspace, &color0);
922 g[1].color = col2col(colspace, &color1);
923 g[2].color = col2col(colspace, &color2);
928 gfxbbox_t b = states[statepos].clipbbox;
929 gfxline_t p1,p2,p3,p4,p5;
930 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
931 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
932 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
933 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
934 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
937 //m.m00 = (x3-x0); m.m10 = (x1-x0);
938 //m.m01 = (y3-y0); m.m11 = (y1-y0);
939 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
940 m.m00 = (x1-x0); m.m10 = (x2-x0);
941 m.m01 = (y1-y0); m.m11 = (y2-y0);
945 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
949 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
952 shading->getCoords(&x0,&y0,&x1,&y1);
953 this->transformXY(state, x0,y0,&x0,&y0);
954 this->transformXY(state, x1,y1,&x1,&y1);
959 shading->getColor(0.0, &color0);
960 shading->getColor(0.5, &color1);
961 shading->getColor(1.0, &color2);
963 GfxColorSpace* colspace = shading->getColorSpace();
965 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
966 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
967 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
968 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
970 infofeature("axial shaded fills");
972 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
976 g[0].color = col2col(colspace, &color0);
977 g[1].color = col2col(colspace, &color1);
978 g[2].color = col2col(colspace, &color2);
983 double xMin,yMin,xMax,yMax;
984 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
985 this->transformXY(state, xMin, yMin, &xMin, &yMin);
986 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
989 xMin = 1024; yMin = 1024;
991 gfxbbox_t b = states[statepos].clipbbox;
992 gfxline_t p1,p2,p3,p4,p5;
993 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
994 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
995 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
996 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
997 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
999 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1000 the middle of the two control points */
1002 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1003 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1004 m.tx = (x0 + x1)/2 - 0.5;
1005 m.ty = (y0 + y1)/2 - 0.5;
1007 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1013 GBool GFXOutputDev::useDrawForm()
1015 infofeature("forms");
1018 void GFXOutputDev::drawForm(Ref id)
1020 msg("<error> drawForm not implemented");
1022 GBool GFXOutputDev::needNonText()
1026 void GFXOutputDev::endPage()
1028 msg("<verbose> endPage (GfxOutputDev)");
1029 if(outer_clip_box) {
1030 device->endclip(device);
1033 this->dashPattern = 0;
1034 /* notice: we're not fully done yet with this page- there might still be
1035 a few calls to drawLink() yet to come */
1038 static inline double sqr(double x) {return x*x;}
1040 #define STROKE_FILL 1
1041 #define STROKE_CLIP 2
1042 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1044 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1045 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1046 double miterLimit = state->getMiterLimit();
1047 double width = state->getTransformedLineWidth();
1050 double opaq = state->getStrokeOpacity();
1052 state->getFillRGB(&rgb);
1054 state->getStrokeRGB(&rgb);
1056 col.r = colToByte(rgb.r);
1057 col.g = colToByte(rgb.g);
1058 col.b = colToByte(rgb.b);
1059 col.a = (unsigned char)(opaq*255);
1061 gfx_capType capType = gfx_capRound;
1062 if(lineCap == 0) capType = gfx_capButt;
1063 else if(lineCap == 1) capType = gfx_capRound;
1064 else if(lineCap == 2) capType = gfx_capSquare;
1066 gfx_joinType joinType = gfx_joinRound;
1067 if(lineJoin == 0) joinType = gfx_joinMiter;
1068 else if(lineJoin == 1) joinType = gfx_joinRound;
1069 else if(lineJoin == 2) joinType = gfx_joinBevel;
1071 gfxline_t*line2 = 0;
1073 if(this->dashLength && this->dashPattern) {
1074 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1077 /* try to find out how much the transformation matrix would
1078 stretch the dashes, and factor that into the dash lengths.
1079 This is not the entirely correct approach- it would be
1080 better to first convert the path to an unscaled version,
1081 then apply dashing, and then transform the path using
1082 the current transformation matrix. However there are few
1083 PDFs which actually stretch a dashed path in a non-orthonormal
1085 double tx1, ty1, tx2, ty2;
1086 this->transformXY(state, 0, 0, &tx1, &ty1);
1087 this->transformXY(state, 1, 1, &tx2, &ty2);
1088 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1090 msg("<trace> %d dashes", this->dashLength);
1091 msg("<trace> | phase: %f", this->dashStart);
1092 for(t=0;t<this->dashLength;t++) {
1093 dash[t] = (float)this->dashPattern[t] * f;
1096 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1098 dash[this->dashLength] = -1;
1099 if(getLogLevel() >= LOGLEVEL_TRACE) {
1103 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1106 msg("<trace> After dashing:");
1109 if(getLogLevel() >= LOGLEVEL_TRACE) {
1110 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1112 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1113 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1115 col.r,col.g,col.b,col.a
1120 if(flags&STROKE_FILL) {
1121 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1122 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1123 if(getLogLevel() >= LOGLEVEL_TRACE) {
1124 dump_outline(gfxline);
1127 msg("<warning> Empty polygon (resulting from stroked line)");
1129 if(flags&STROKE_CLIP) {
1130 device->startclip(device, gfxline);
1131 states[statepos].clipping++;
1133 device->fill(device, gfxline, &col);
1135 gfxline_free(gfxline);
1138 if(flags&STROKE_CLIP)
1139 msg("<error> Stroke&clip not supported at the same time");
1140 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1144 gfxline_free(line2);
1147 gfxcolor_t getFillColor(GfxState * state)
1150 double opaq = state->getFillOpacity();
1151 state->getFillRGB(&rgb);
1153 col.r = colToByte(rgb.r);
1154 col.g = colToByte(rgb.g);
1155 col.b = colToByte(rgb.b);
1156 col.a = (unsigned char)(opaq*255);
1160 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1162 gfxcolor_t col = getFillColor(state);
1164 if(getLogLevel() >= LOGLEVEL_TRACE) {
1165 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1168 device->fill(device, line, &col);
1171 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1173 if(getLogLevel() >= LOGLEVEL_TRACE) {
1176 gfxbbox_t bbox = gfxline_getbbox(line);
1177 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1179 device->startclip(device, line);
1180 states[statepos].clipping++;
1183 void GFXOutputDev::clip(GfxState *state)
1185 GfxPath * path = state->getPath();
1186 msg("<trace> clip");
1187 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1188 if(config_optimize_polygons) {
1189 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1193 clipToGfxLine(state, line);
1197 void GFXOutputDev::eoClip(GfxState *state)
1199 GfxPath * path = state->getPath();
1200 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1201 clipToGfxLine(state, line);
1204 void GFXOutputDev::clipToStrokePath(GfxState *state)
1206 GfxPath * path = state->getPath();
1207 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1209 if(getLogLevel() >= LOGLEVEL_TRACE) {
1210 double width = state->getTransformedLineWidth();
1211 msg("<trace> cliptostrokepath width=%f", width);
1215 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1219 void GFXOutputDev::finish()
1221 if(outer_clip_box) {
1223 device->endclip(device);
1229 GFXOutputDev::~GFXOutputDev()
1234 free(this->pages); this->pages = 0;
1236 if(this->dashPattern) {
1237 free(this->dashPattern);this->dashPattern = 0;
1240 feature_t*f = this->featurewarnings;
1242 feature_t*next = f->next;
1244 free(f->string);f->string =0;
1250 this->featurewarnings = 0;
1252 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1254 GBool GFXOutputDev::upsideDown()
1258 GBool GFXOutputDev::useDrawChar()
1263 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1264 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1266 static char tmp_printstr[4096];
1267 char* makeStringPrintable(char*str)
1269 int len = strlen(str);
1276 for(t=0;t<len;t++) {
1281 tmp_printstr[t] = c;
1284 tmp_printstr[len++] = '.';
1285 tmp_printstr[len++] = '.';
1286 tmp_printstr[len++] = '.';
1288 tmp_printstr[len] = 0;
1289 return tmp_printstr;
1291 #define INTERNAL_FONT_SIZE 1024.0
1292 void GFXOutputDev::updateFontMatrix(GfxState*state)
1294 double* ctm = state->getCTM();
1295 double fontSize = state->getFontSize();
1296 double*textMat = state->getTextMat();
1298 /* taking the absolute value of horizScaling seems to be required for
1299 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1300 double hscale = fabs(state->getHorizScaling());
1302 // from xpdf-3.02/SplashOutputDev:updateFont
1303 double mm11 = textMat[0] * fontSize * hscale;
1304 double mm12 = textMat[1] * fontSize * hscale;
1305 double mm21 = textMat[2] * fontSize;
1306 double mm22 = textMat[3] * fontSize;
1308 // multiply with ctm, like state->getFontTransMat() does
1309 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1310 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1311 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1312 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1313 this->current_font_matrix.tx = 0;
1314 this->current_font_matrix.ty = 0;
1317 void GFXOutputDev::beginString(GfxState *state, GString *s)
1319 int render = state->getRender();
1320 if(current_text_stroke) {
1321 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1324 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1327 static gfxline_t* mkEmptyGfxShape(double x, double y)
1329 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1330 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1334 static char isValidUnicode(int c)
1336 if(c>=32 && c<0x2fffe)
1341 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1342 double dx, double dy,
1343 double originX, double originY,
1344 CharCode charid, int nBytes, Unicode *_u, int uLen)
1346 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1347 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1351 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1353 int render = state->getRender();
1354 gfxcolor_t col = getFillColor(state);
1356 // check for invisible text -- this is used by Acrobat Capture
1357 if (render == RENDER_INVISIBLE) {
1359 if(!config_extrafontdata)
1363 GfxFont*font = state->getFont();
1365 if(font->getType() == fontType3) {
1366 /* type 3 chars are passed as graphics */
1367 msg("<debug> type3 char at %f/%f", x, y);
1371 Unicode u = uLen?(_u[0]):0;
1372 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);
1374 gfxmatrix_t m = this->current_font_matrix;
1375 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1376 m.tx += originX; m.ty += originY;
1378 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1379 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1381 msg("<debug> Drawing glyph %d as shape", charid);
1383 msg("<notice> Some texts will be rendered as shape");
1386 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1387 gfxline_t*tglyph = gfxline_clone(glyph);
1388 gfxline_transform(tglyph, &m);
1389 if((render&3) != RENDER_INVISIBLE) {
1390 gfxline_t*add = gfxline_clone(tglyph);
1391 current_text_stroke = gfxline_append(current_text_stroke, add);
1393 if(render&RENDER_CLIP) {
1394 gfxline_t*add = gfxline_clone(tglyph);
1395 current_text_clip = gfxline_append(current_text_clip, add);
1396 if(!current_text_clip) {
1397 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1400 gfxline_free(tglyph);
1404 void GFXOutputDev::endString(GfxState *state)
1406 int render = state->getRender();
1407 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1409 if(current_text_stroke) {
1410 /* fillstroke and stroke text rendering objects we can process right
1411 now (as there may be texts of other rendering modes in this
1412 text object)- clipping objects have to wait until endTextObject,
1414 device->setparameter(device, "mark","TXT");
1415 if((render&3) == RENDER_FILL) {
1416 fillGfxLine(state, current_text_stroke);
1417 gfxline_free(current_text_stroke);
1418 current_text_stroke = 0;
1419 } else if((render&3) == RENDER_FILLSTROKE) {
1420 fillGfxLine(state, current_text_stroke);
1421 strokeGfxline(state, current_text_stroke,0);
1422 gfxline_free(current_text_stroke);
1423 current_text_stroke = 0;
1424 } else if((render&3) == RENDER_STROKE) {
1425 strokeGfxline(state, current_text_stroke,0);
1426 gfxline_free(current_text_stroke);
1427 current_text_stroke = 0;
1429 device->setparameter(device, "mark","");
1433 void GFXOutputDev::endTextObject(GfxState *state)
1435 int render = state->getRender();
1436 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1438 if(current_text_clip) {
1439 device->setparameter(device, "mark","TXT");
1440 clipToGfxLine(state, current_text_clip);
1441 device->setparameter(device, "mark","");
1442 gfxline_free(current_text_clip);
1443 current_text_clip = 0;
1447 /* the logic seems to be as following:
1448 first, beginType3Char is called, with the charcode and the coordinates.
1449 if this function returns true, it already knew about the char and has now drawn it.
1450 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1451 called with some parameters.
1452 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1453 at the position first passed to beginType3Char). the char ends with endType3Char.
1455 The drawing operations between beginType3Char and endType3Char are somewhat different to
1456 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1457 color determines the color of a font)
1460 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1462 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1465 if(config_extrafontdata && current_fontinfo) {
1467 gfxmatrix_t m = this->current_font_matrix;
1468 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1469 m.m00*=INTERNAL_FONT_SIZE;
1470 m.m01*=INTERNAL_FONT_SIZE;
1471 m.m10*=INTERNAL_FONT_SIZE;
1472 m.m11*=INTERNAL_FONT_SIZE;
1474 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1475 msg("<error> Invalid charid %d for font", charid);
1478 gfxcolor_t col={0,0,0,0};
1479 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1480 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1484 /* the character itself is going to be passed using the draw functions */
1485 return gFalse; /* gTrue= is_in_cache? */
1488 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1490 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1493 void GFXOutputDev::endType3Char(GfxState *state)
1496 msg("<debug> endType3Char");
1499 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1501 this->currentpage = pageNum;
1503 int rot = doc->getPageRotate(1);
1504 gfxcolor_t white = {255,255,255,255};
1505 gfxcolor_t black = {255,0,0,0};
1507 gfxline_t clippath[5];
1509 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1510 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1511 Use CropBox, not MediaBox, as page size
1518 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1519 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1521 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1522 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1524 this->clipmovex = -(int)x1;
1525 this->clipmovey = -(int)y1;
1527 /* apply user clip box */
1528 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1529 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1530 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1531 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1532 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1533 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1536 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1538 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);
1540 msg("<verbose> page is rotated %d degrees", rot);
1542 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1543 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1544 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1545 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1546 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1547 device->startclip(device, clippath); outer_clip_box = 1;
1548 if(!config_transparent) {
1549 device->fill(device, clippath, &white);
1551 states[statepos].clipbbox.xmin = x1;
1552 states[statepos].clipbbox.ymin = x1;
1553 states[statepos].clipbbox.xmax = x2;
1554 states[statepos].clipbbox.ymax = y2;
1556 this->dashPattern = 0;
1557 this->dashLength = 0;
1558 this->dashStart = 0;
1562 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1564 double x1, y1, x2, y2;
1565 gfxline_t points[5];
1568 msg("<debug> drawlink");
1570 link->getRect(&x1, &y1, &x2, &y2);
1571 cvtUserToDev(x1, y1, &x, &y);
1572 points[0].type = gfx_moveTo;
1573 points[0].x = points[4].x = x + user_movex + clipmovex;
1574 points[0].y = points[4].y = y + user_movey + clipmovey;
1575 points[0].next = &points[1];
1576 cvtUserToDev(x2, y1, &x, &y);
1577 points[1].type = gfx_lineTo;
1578 points[1].x = x + user_movex + clipmovex;
1579 points[1].y = y + user_movey + clipmovey;
1580 points[1].next = &points[2];
1581 cvtUserToDev(x2, y2, &x, &y);
1582 points[2].type = gfx_lineTo;
1583 points[2].x = x + user_movex + clipmovex;
1584 points[2].y = y + user_movey + clipmovey;
1585 points[2].next = &points[3];
1586 cvtUserToDev(x1, y2, &x, &y);
1587 points[3].type = gfx_lineTo;
1588 points[3].x = x + user_movex + clipmovex;
1589 points[3].y = y + user_movey + clipmovey;
1590 points[3].next = &points[4];
1591 cvtUserToDev(x1, y1, &x, &y);
1592 points[4].type = gfx_lineTo;
1593 points[4].x = x + user_movex + clipmovex;
1594 points[4].y = y + user_movey + clipmovey;
1597 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1598 points[0].x, points[0].y,
1599 points[1].x, points[1].y,
1600 points[2].x, points[2].y,
1601 points[3].x, points[3].y);
1603 if(getLogLevel() >= LOGLEVEL_TRACE) {
1604 dump_outline(points);
1607 LinkAction*action=link->getAction();
1610 const char*type = "-?-";
1613 msg("<trace> drawlink action=%d", action->getKind());
1614 switch(action->getKind())
1618 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1619 LinkDest *dest=NULL;
1620 if (ha->getDest()==NULL)
1621 dest=catalog->findDest(ha->getNamedDest());
1622 else dest=ha->getDest();
1624 if (dest->isPageRef()){
1625 Ref pageref=dest->getPageRef();
1626 page=catalog->findPage(pageref.num,pageref.gen);
1628 else page=dest->getPageNum();
1629 sprintf(buf, "%d", page);
1636 LinkGoToR*l = (LinkGoToR*)action;
1637 GString*g = l->getFileName();
1639 s = strdup(g->getCString());
1641 /* if the GoToR link has no filename, then
1642 try to find a refernce in the *local*
1644 GString*g = l->getNamedDest();
1646 s = strdup(g->getCString());
1652 LinkNamed*l = (LinkNamed*)action;
1653 GString*name = l->getName();
1655 s = strdup(name->lowerCase()->getCString());
1656 named = name->getCString();
1659 if(strstr(s, "next") || strstr(s, "forward"))
1661 page = currentpage + 1;
1663 else if(strstr(s, "prev") || strstr(s, "back"))
1665 page = currentpage - 1;
1667 else if(strstr(s, "last") || strstr(s, "end"))
1669 if(pages && pagepos>0)
1670 page = pages[pagepos-1];
1672 else if(strstr(s, "first") || strstr(s, "top"))
1680 case actionLaunch: {
1682 LinkLaunch*l = (LinkLaunch*)action;
1683 GString * str = new GString(l->getFileName());
1684 GString * params = l->getParams();
1686 str->append(params);
1687 s = strdup(str->getCString());
1694 LinkURI*l = (LinkURI*)action;
1695 GString*g = l->getURI();
1697 url = g->getCString();
1702 case actionUnknown: {
1704 LinkUnknown*l = (LinkUnknown*)action;
1709 msg("<error> Unknown link type!");
1714 if(!s) s = strdup("-?-");
1716 msg("<trace> drawlink s=%s", s);
1718 if(!linkinfo && (page || s))
1720 msg("<notice> File contains links");
1728 for(t=1;t<=pagepos;t++) {
1729 if(pages[t]==page) {
1738 sprintf(buf, "page%d", lpage);
1739 device->drawlink(device, points, buf);
1743 device->drawlink(device, points, s);
1746 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1750 void GFXOutputDev::saveState(GfxState *state) {
1751 dbg("saveState %08x", state); dbgindent+=2;
1753 msg("<trace> saveState %08x", state);
1756 msg("<fatal> Too many nested states in pdf.");
1760 states[statepos].state = state;
1761 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1762 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1763 states[statepos].clipping = 0;
1764 states[statepos].olddevice = 0;
1765 states[statepos].clipbbox = states[statepos-1].clipbbox;
1768 void GFXOutputDev::restoreState(GfxState *state) {
1769 dbgindent-=2; dbg("restoreState %08x", state);
1772 msg("<fatal> Invalid restoreState");
1775 msg("<trace> restoreState %08x%s%s", state,
1776 states[statepos].softmask?" (end softmask)":"",
1777 states[statepos].clipping?" (end clipping)":"");
1778 if(states[statepos].softmask) {
1779 clearSoftMask(state);
1783 while(states[statepos].clipping) {
1784 device->endclip(device);
1785 states[statepos].clipping--;
1787 if(states[statepos].state!=state) {
1788 msg("<fatal> bad state nesting");
1791 states[statepos].state=0;
1795 void GFXOutputDev::updateLineDash(GfxState *state)
1797 if(this->dashPattern) {
1798 free(this->dashPattern);this->dashPattern = 0;
1800 double *pattern = 0;
1801 state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1802 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1803 if(!this->dashLength) {
1804 this->dashPattern = 0;
1806 double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1807 memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1808 this->dashPattern = p;
1812 void GFXOutputDev::updateLineWidth(GfxState *state)
1814 double width = state->getTransformedLineWidth();
1817 void GFXOutputDev::updateLineCap(GfxState *state)
1819 int c = state->getLineCap();
1822 void GFXOutputDev::updateLineJoin(GfxState *state)
1824 int j = state->getLineJoin();
1827 void GFXOutputDev::updateFillColor(GfxState *state)
1830 double opaq = state->getFillOpacity();
1831 state->getFillRGB(&rgb);
1833 void GFXOutputDev::updateFillOpacity(GfxState *state)
1836 double opaq = state->getFillOpacity();
1837 state->getFillRGB(&rgb);
1838 dbg("update fillopaq %f", opaq);
1840 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1842 double opaq = state->getFillOpacity();
1843 dbg("update strokeopaq %f", opaq);
1845 void GFXOutputDev::updateFillOverprint(GfxState *state)
1847 double opaq = state->getFillOverprint();
1848 dbg("update filloverprint %f", opaq);
1850 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1852 double opaq = state->getStrokeOverprint();
1853 dbg("update strokeoverprint %f", opaq);
1855 void GFXOutputDev::updateTransfer(GfxState *state)
1857 dbg("update transfer");
1861 void GFXOutputDev::updateStrokeColor(GfxState *state)
1864 double opaq = state->getStrokeOpacity();
1865 state->getStrokeRGB(&rgb);
1869 gfxfont_t* GFXOutputDev::createGfxFont(GfxFont*xpdffont, FontInfo*src)
1871 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1872 memset(font, 0, sizeof(gfxfont_t));
1874 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1875 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1879 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1881 //printf("%d glyphs\n", font->num_glyphs);
1882 font->num_glyphs = 0;
1883 font->ascent = fabs(src->descender);
1884 font->descent = fabs(src->ascender);
1886 for(t=0;t<src->num_glyphs;t++) {
1887 if(src->glyphs[t]) {
1888 SplashPath*path = src->glyphs[t]->path;
1889 int len = path?path->getLength():0;
1890 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1891 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1892 src->glyphs[t]->glyphid = font->num_glyphs;
1893 glyph->unicode = src->glyphs[t]->unicode;
1894 if(glyph->unicode >= font->max_unicode)
1895 font->max_unicode = glyph->unicode+1;
1897 gfxdrawer_target_gfxline(&drawer);
1901 for(s=0;s<len;s++) {
1904 path->getPoint(s, &x, &y, &f);
1907 if(f&splashPathFirst) {
1908 drawer.moveTo(&drawer, x*scale, y*scale);
1910 if(f&splashPathCurve) {
1912 path->getPoint(++s, &x2, &y2, &f);
1913 if(f&splashPathCurve) {
1915 path->getPoint(++s, &x3, &y3, &f);
1916 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1918 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1921 drawer.lineTo(&drawer, x*scale, y*scale);
1923 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1924 // (f&splashPathFirst)?"first":"",
1925 // (f&splashPathLast)?"last":"");
1928 glyph->line = (gfxline_t*)drawer.result(&drawer);
1929 if(src->glyphs[t]->advance>0) {
1930 glyph->advance = src->glyphs[t]->advance;
1932 msg("<warning> Approximating advance value for glyph %d", t);
1933 glyph->advance = xmax*scale;
1935 if(this->config_bigchar) {
1936 double max = src->glyphs[t]->advance_max;
1937 if(max>0 && max > glyph->advance) {
1938 glyph->advance = max;
1945 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1946 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1947 for(t=0;t<font->num_glyphs;t++) {
1948 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1949 font->unicode2glyph[font->glyphs[t].unicode] = t;
1953 msg("<trace> %d glyphs.", t, font->num_glyphs);
1957 void GFXOutputDev::updateFont(GfxState *state)
1959 GfxFont* gfxFont = state->getFont();
1963 char*id = getFontID(gfxFont);
1964 msg("<verbose> Updating font to %s", id);
1965 if(gfxFont->getType() == fontType3) {
1966 infofeature("Type3 fonts");
1967 if(!config_extrafontdata) {
1972 msg("<error> Internal Error: FontID is null");
1976 this->current_fontinfo = this->info->getFont(id);
1977 if(!this->current_fontinfo) {
1978 msg("<error> Internal Error: no fontinfo for font %s", id);
1981 if(!this->current_fontinfo->seen) {
1982 dumpFontInfo("<verbose>", gfxFont);
1985 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1987 font = this->createGfxFont(gfxFont, current_fontinfo);
1988 font->id = strdup(id);
1989 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1991 device->addfont(device, font);
1993 current_gfxfont = font;
1996 updateFontMatrix(state);
1999 #define SQR(x) ((x)*(x))
2001 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2003 if((newwidth<1 || newheight<1) ||
2004 (width<=newwidth || height<=newheight))
2006 unsigned char*newdata;
2008 newdata= (unsigned char*)malloc(newwidth*newheight);
2009 double fx = ((double)width)/newwidth;
2010 double fy = ((double)height)/newheight;
2012 int blocksize = (int)(8192/(fx*fy));
2013 int r = 8192*256/palettesize;
2014 for(x=0;x<newwidth;x++) {
2015 double ex = px + fx;
2016 int fromx = (int)px;
2018 int xweight1 = (int)((1-(px-fromx))*256);
2019 int xweight2 = (int)((ex-tox)*256);
2021 for(y=0;y<newheight;y++) {
2022 double ey = py + fy;
2023 int fromy = (int)py;
2025 int yweight1 = (int)((1-(py-fromy))*256);
2026 int yweight2 = (int)((ey-toy)*256);
2033 for(xx=fromx;xx<=tox;xx++)
2034 for(yy=fromy;yy<=toy;yy++) {
2035 int b = 1-data[width*yy+xx];
2037 if(xx==fromx) weight = (weight*xweight1)/256;
2038 if(xx==tox) weight = (weight*xweight2)/256;
2039 if(yy==fromy) weight = (weight*yweight1)/256;
2040 if(yy==toy) weight = (weight*yweight2)/256;
2043 //if(a) a=(palettesize-1)*r/blocksize;
2044 newdata[y*newwidth+x] = (a*blocksize)/r;
2052 #define IMAGE_TYPE_JPEG 0
2053 #define IMAGE_TYPE_LOSSLESS 1
2055 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2056 double x1,double y1,
2057 double x2,double y2,
2058 double x3,double y3,
2059 double x4,double y4, int type, int multiply)
2061 gfxcolor_t*newpic=0;
2063 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2064 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2066 gfxline_t p1,p2,p3,p4,p5;
2067 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2068 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2069 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2070 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2071 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2073 {p1.x = (int)(p1.x*20)/20.0;
2074 p1.y = (int)(p1.y*20)/20.0;
2075 p2.x = (int)(p2.x*20)/20.0;
2076 p2.y = (int)(p2.y*20)/20.0;
2077 p3.x = (int)(p3.x*20)/20.0;
2078 p3.y = (int)(p3.y*20)/20.0;
2079 p4.x = (int)(p4.x*20)/20.0;
2080 p4.y = (int)(p4.y*20)/20.0;
2081 p5.x = (int)(p5.x*20)/20.0;
2082 p5.y = (int)(p5.y*20)/20.0;
2086 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2087 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2089 m.tx = p1.x - 0.5*multiply;
2090 m.ty = p1.y - 0.5*multiply;
2093 img.data = (gfxcolor_t*)data;
2097 if(type == IMAGE_TYPE_JPEG)
2098 /* TODO: pass image_dpi to device instead */
2099 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2102 dev->fillbitmap(dev, &p1, &img, &m, 0);
2105 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2106 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2108 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2111 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2112 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2114 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2118 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2119 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2120 GBool inlineImg, int mask, int*maskColors,
2121 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2123 /* the code in this function is *old*. It's not pretty, but it works. */
2125 double x1,y1,x2,y2,x3,y3,x4,y4;
2126 ImageStream *imgStr;
2131 unsigned char* maskbitmap = 0;
2134 ncomps = colorMap->getNumPixelComps();
2135 bits = colorMap->getBits();
2140 unsigned char buf[8];
2141 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2143 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2144 imgMaskStr->reset();
2145 unsigned char pal[256];
2146 int n = 1 << colorMap->getBits();
2151 maskColorMap->getGray(pixBuf, &gray);
2152 pal[t] = colToByte(gray);
2154 for (y = 0; y < maskHeight; y++) {
2155 for (x = 0; x < maskWidth; x++) {
2156 imgMaskStr->getPixel(buf);
2157 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2162 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2163 imgMaskStr->reset();
2164 for (y = 0; y < maskHeight; y++) {
2165 for (x = 0; x < maskWidth; x++) {
2166 imgMaskStr->getPixel(buf);
2168 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2176 imgStr = new ImageStream(str, width, ncomps,bits);
2179 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2181 msg("<verbose> Ignoring %d by %d image", width, height);
2182 unsigned char buf[8];
2184 for (y = 0; y < height; ++y)
2185 for (x = 0; x < width; ++x) {
2186 imgStr->getPixel(buf);
2194 this->transformXY(state, 0, 1, &x1, &y1);
2195 this->transformXY(state, 0, 0, &x2, &y2);
2196 this->transformXY(state, 1, 0, &x3, &y3);
2197 this->transformXY(state, 1, 1, &x4, &y4);
2200 /* as type 3 bitmaps are antialized, we need to place them
2201 at integer coordinates, otherwise flash player's antializing
2202 will kick in and make everything blurry */
2203 x1 = (int)(x1);y1 = (int)(y1);
2204 x2 = (int)(x2);y2 = (int)(y2);
2205 x3 = (int)(x3);y3 = (int)(y3);
2206 x4 = (int)(x4);y4 = (int)(y4);
2209 if(!pbminfo && !(str->getKind()==strDCT)) {
2211 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2215 msg("<verbose> drawing %d by %d masked picture", width, height);
2217 if(!jpeginfo && (str->getKind()==strDCT)) {
2218 msg("<notice> File contains jpeg pictures");
2223 unsigned char buf[8];
2225 unsigned char*pic = new unsigned char[width*height];
2226 gfxcolor_t pal[256];
2228 state->getFillRGB(&rgb);
2230 memset(pal,255,sizeof(pal));
2231 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2232 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2233 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2234 pal[0].a = 255; pal[1].a = 0;
2237 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2238 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2239 for (y = 0; y < height; ++y)
2240 for (x = 0; x < width; ++x)
2242 imgStr->getPixel(buf);
2245 pic[width*y+x] = buf[0];
2249 unsigned char*pic2 = 0;
2252 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2261 height = realheight;
2265 /* make a black/white palette */
2267 float r = 255./(float)(numpalette-1);
2269 for(t=0;t<numpalette;t++) {
2270 pal[t].r = colToByte(rgb.r);
2271 pal[t].g = colToByte(rgb.g);
2272 pal[t].b = colToByte(rgb.b);
2273 pal[t].a = (unsigned char)(t*r);
2278 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2279 for (y = 0; y < height; ++y) {
2280 for (x = 0; x < width; ++x) {
2281 pic2[width*y+x] = pal[pic[y*width+x]];
2284 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2288 if(maskbitmap) free(maskbitmap);
2294 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2295 gfxcolor_t*pic=new gfxcolor_t[width*height];
2296 for (y = 0; y < height; ++y) {
2297 for (x = 0; x < width; ++x) {
2298 imgStr->getPixel(pixBuf);
2299 colorMap->getRGB(pixBuf, &rgb);
2300 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2301 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2302 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2303 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2305 int x1 = x*maskWidth/width;
2306 int y1 = y*maskHeight/height;
2307 int x2 = (x+1)*maskWidth/width;
2308 int y2 = (y+1)*maskHeight/height;
2310 unsigned int alpha=0;
2311 unsigned int count=0;
2312 for(xx=x1;xx<x2;xx++)
2313 for(yy=y1;yy<y2;yy++) {
2314 alpha += maskbitmap[yy*maskWidth+xx];
2318 pic[width*y+x].a = alpha / count;
2320 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2325 if(str->getKind()==strDCT)
2326 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2328 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2331 if(maskbitmap) free(maskbitmap);
2334 gfxcolor_t*pic=new gfxcolor_t[width*height];
2335 gfxcolor_t pal[256];
2336 int n = 1 << colorMap->getBits();
2338 for(t=0;t<256;t++) {
2340 colorMap->getRGB(pixBuf, &rgb);
2342 {/*if(maskColors && *maskColors==t) {
2343 msg("<notice> Color %d is transparent", t);
2344 if (imgData->maskColors) {
2346 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2347 if (pix[i] < imgData->maskColors[2*i] ||
2348 pix[i] > imgData->maskColors[2*i+1]) {
2363 pal[t].r = (unsigned char)(colToByte(rgb.r));
2364 pal[t].g = (unsigned char)(colToByte(rgb.g));
2365 pal[t].b = (unsigned char)(colToByte(rgb.b));
2366 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2369 for (y = 0; y < height; ++y) {
2370 for (x = 0; x < width; ++x) {
2371 imgStr->getPixel(pixBuf);
2372 pic[width*y+x] = pal[pixBuf[0]];
2376 if(maskWidth < width && maskHeight < height) {
2377 for(y = 0; y < height; y++) {
2378 for (x = 0; x < width; x++) {
2379 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2383 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2384 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2385 double dx = width / maskWidth;
2386 double dy = height / maskHeight;
2388 for(y = 0; y < maskHeight; y++) {
2390 for (x = 0; x < maskWidth; x++) {
2391 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2392 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2400 height = maskHeight;
2403 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2407 if(maskbitmap) free(maskbitmap);
2412 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2413 int width, int height, GBool invert,
2416 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2417 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2418 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2421 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2422 int width, int height, GfxImageColorMap *colorMap,
2423 int *maskColors, GBool inlineImg)
2425 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2426 colorMap?"colorMap":"no colorMap",
2427 maskColors?"maskColors":"no maskColors",
2429 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2430 colorMap?"colorMap":"no colorMap",
2431 maskColors?"maskColors":"no maskColors",
2434 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2435 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2436 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2439 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2440 int width, int height,
2441 GfxImageColorMap *colorMap,
2442 Stream *maskStr, int maskWidth, int maskHeight,
2445 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2446 colorMap?"colorMap":"no colorMap",
2447 maskWidth, maskHeight);
2448 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2449 colorMap?"colorMap":"no colorMap",
2450 maskWidth, maskHeight);
2452 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2453 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2454 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2457 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2458 int width, int height,
2459 GfxImageColorMap *colorMap,
2461 int maskWidth, int maskHeight,
2462 GfxImageColorMap *maskColorMap)
2464 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2465 colorMap?"colorMap":"no colorMap",
2466 maskWidth, maskHeight);
2467 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2468 colorMap?"colorMap":"no colorMap",
2469 maskWidth, maskHeight);
2471 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2472 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2473 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2476 void GFXOutputDev::stroke(GfxState *state)
2480 GfxPath * path = state->getPath();
2481 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2482 strokeGfxline(state, line, 0);
2486 void GFXOutputDev::fill(GfxState *state)
2488 gfxcolor_t col = getFillColor(state);
2489 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2491 GfxPath * path = state->getPath();
2492 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2493 if(config_optimize_polygons) {
2494 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2498 fillGfxLine(state, line);
2502 void GFXOutputDev::eoFill(GfxState *state)
2504 gfxcolor_t col = getFillColor(state);
2505 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2507 GfxPath * path = state->getPath();
2508 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2509 fillGfxLine(state, line);
2514 static const char* dirseparator()
2523 void addGlobalFont(const char*filename)
2525 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2526 memset(f, 0, sizeof(fontfile_t));
2527 f->filename = filename;
2528 int len = strlen(filename);
2529 char*r1 = strrchr(filename, '/');
2530 char*r2 = strrchr(filename, '\\');
2538 msg("<notice> Adding font \"%s\".", filename);
2539 if(global_fonts_next) {
2540 global_fonts_next->next = f;
2541 global_fonts_next = global_fonts_next->next;
2543 global_fonts_next = global_fonts = f;
2547 void addGlobalLanguageDir(const char*dir)
2549 msg("<notice> Adding %s to language pack directories", dir);
2552 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2553 strcpy(config_file, dir);
2554 strcat(config_file, dirseparator());
2555 strcat(config_file, "add-to-xpdfrc");
2557 fi = fopen(config_file, "rb");
2559 msg("<error> Could not open %s", config_file);
2562 globalParams->parseFile(new GString(config_file), fi);
2566 void addGlobalFontDir(const char*dirname)
2568 #ifdef HAVE_DIRENT_H
2569 msg("<notice> Adding %s to font directories", dirname);
2570 lastfontdir = strdup(dirname);
2571 DIR*dir = opendir(dirname);
2573 msg("<warning> Couldn't open directory %s", dirname);
2578 ent = readdir (dir);
2582 char*name = ent->d_name;
2588 if(!strncasecmp(&name[l-4], ".pfa", 4))
2590 if(!strncasecmp(&name[l-4], ".pfb", 4))
2592 if(!strncasecmp(&name[l-4], ".ttf", 4))
2595 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2596 strcpy(fontname, dirname);
2597 strcat(fontname, dirseparator());
2598 strcat(fontname, name);
2599 addGlobalFont(fontname);
2604 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2608 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2614 this->pagebuflen = 1024;
2615 if(pdfpage > this->pagebuflen)
2616 this->pagebuflen = pdfpage+1;
2617 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2618 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2621 while(pdfpage >= this->pagebuflen)
2623 int oldlen = this->pagebuflen;
2624 this->pagebuflen+=1024;
2625 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2626 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2629 this->pages[pdfpage] = outputpage;
2630 if(pdfpage>this->pagepos)
2631 this->pagepos = pdfpage;
2634 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2635 GfxColorSpace *blendingColorSpace,
2636 GBool isolated, GBool knockout,
2639 const char*colormodename = "";
2641 if(blendingColorSpace) {
2642 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2644 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);
2645 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);
2647 //states[statepos].createsoftmask |= forSoftMask;
2648 states[statepos].createsoftmask = forSoftMask;
2649 states[statepos].transparencygroup = !forSoftMask;
2650 states[statepos].isolated = isolated;
2652 states[statepos].olddevice = this->device;
2653 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2654 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2656 gfxdevice_record_init(this->device);
2658 /*if(!forSoftMask) { ////???
2659 state->setFillOpacity(0.0);
2664 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2667 gfxdevice_t*r = this->device;
2669 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2671 this->device = states[statepos].olddevice;
2673 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2674 /* if these errors occur more often, we should build a seperate
2675 transparency group stack, like xpdf/SplashOutputDev.cc does */
2676 restoreState(state);
2677 this->device = states[statepos].olddevice;
2679 states[statepos].olddevice = 0;
2681 gfxresult_t*recording = r->finish(r);
2683 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2684 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2686 if(states[statepos].createsoftmask) {
2687 states[statepos-1].softmaskrecording = recording;
2689 states[statepos-1].grouprecording = recording;
2692 states[statepos].createsoftmask = 0;
2693 states[statepos].transparencygroup = 0;
2697 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2699 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2700 "colordodge","colorburn","hardlight","softlight","difference",
2701 "exclusion","hue","saturation","color","luminosity"};
2703 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2704 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2706 if(state->getBlendMode() == gfxBlendNormal)
2707 infofeature("transparency groups");
2710 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2711 warnfeature(buffer, 0);
2714 gfxresult_t*grouprecording = states[statepos].grouprecording;
2716 int blendmode = state->getBlendMode();
2717 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2718 int alpha = (int)(state->getFillOpacity()*255);
2719 if(blendmode == gfxBlendMultiply && alpha>200)
2722 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2723 gfxdevice_ops_init(&ops, this->device, alpha);
2724 gfxresult_record_replay(grouprecording, &ops);
2727 grouprecording->destroy(grouprecording);
2729 states[statepos].grouprecording = 0;
2732 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2734 if(states[statepos].softmask) {
2735 /* shouldn't happen, but *does* happen */
2736 clearSoftMask(state);
2739 /* alpha = 1: retrieve mask values from alpha layer
2740 alpha = 0: retrieve mask values from luminance */
2742 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2743 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2744 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2745 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2747 infofeature("soft masks");
2749 warnfeature("soft masks from alpha channel",0);
2751 if(states[statepos].olddevice) {
2752 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2755 states[statepos].olddevice = this->device;
2756 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2757 gfxdevice_record_init(this->device);
2759 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2761 states[statepos].softmask = 1;
2762 states[statepos].softmask_alpha = alpha;
2765 static inline Guchar div255(int x) {
2766 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2769 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2771 if(c < min) c = min;
2772 if(c > max) c = max;
2776 void GFXOutputDev::clearSoftMask(GfxState *state)
2778 if(!states[statepos].softmask)
2780 states[statepos].softmask = 0;
2781 dbg("clearSoftMask statepos=%d", statepos);
2782 msg("<verbose> clearSoftMask statepos=%d", statepos);
2784 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2785 msg("<error> Error in softmask/tgroup ordering");
2789 gfxresult_t*mask = states[statepos].softmaskrecording;
2790 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2791 this->device = states[statepos].olddevice;
2793 /* get outline of all objects below the soft mask */
2794 gfxdevice_t uniondev;
2795 gfxdevice_union_init(&uniondev, 0);
2796 gfxresult_record_replay(below, &uniondev);
2797 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2798 uniondev.finish(&uniondev);
2799 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2800 gfxline_free(belowoutline);belowoutline=0;
2802 this->device->startclip(this->device, belowoutline);
2803 gfxresult_record_replay(below, this->device);
2804 gfxresult_record_replay(mask, this->device);
2805 this->device->endclip(this->device);
2808 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2809 if(width<=0 || height<=0)
2812 gfxdevice_t belowrender;
2813 gfxdevice_render_init(&belowrender);
2814 if(states[statepos+1].isolated) {
2815 belowrender.setparameter(&belowrender, "fillwhite", "1");
2817 belowrender.setparameter(&belowrender, "antialize", "2");
2818 belowrender.startpage(&belowrender, width, height);
2819 gfxresult_record_replay(below, &belowrender);
2820 belowrender.endpage(&belowrender);
2821 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2822 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2823 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2825 gfxdevice_t maskrender;
2826 gfxdevice_render_init(&maskrender);
2827 maskrender.startpage(&maskrender, width, height);
2828 gfxresult_record_replay(mask, &maskrender);
2829 maskrender.endpage(&maskrender);
2830 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2831 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2833 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2834 msg("<fatal> Internal error in mask drawing");
2839 for(y=0;y<height;y++) {
2840 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2841 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2842 for(x=0;x<width;x++) {
2844 if(states[statepos].softmask_alpha) {
2847 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2850 l2->a = div255(alpha*l2->a);
2852 /* DON'T premultiply alpha- this is done by fillbitmap,
2853 depending on the output device */
2854 //l2->r = div255(alpha*l2->r);
2855 //l2->g = div255(alpha*l2->g);
2856 //l2->b = div255(alpha*l2->b);
2862 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2865 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2866 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2868 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2870 mask->destroy(mask);
2871 below->destroy(below);
2872 maskresult->destroy(maskresult);
2873 belowresult->destroy(belowresult);
2874 states[statepos].softmaskrecording = 0;
2879 // public: ~MemCheck()
2881 // delete globalParams;globalParams=0;
2882 // Object::memCheck(stderr);
2883 // gMemReport(stderr);