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;
109 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
110 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
111 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
112 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
113 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
114 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
115 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
116 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
117 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
118 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
119 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
120 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
121 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
122 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
125 static int verbose = 0;
126 static int dbgindent = 1;
127 static void dbg(const char*format, ...)
134 va_start(arglist, format);
135 vsprintf(buf, format, arglist);
138 while(l && buf[l-1]=='\n') {
143 int indent = dbgindent;
152 GFXOutputGlobals*gfxglobals=0;
154 GFXOutputGlobals::GFXOutputGlobals()
156 this->featurewarnings = 0;
158 this->textmodeinfo = 0;
162 GFXOutputGlobals::~GFXOutputGlobals()
164 feature_t*f = this->featurewarnings;
166 feature_t*next = f->next;
168 free(f->string);f->string =0;
174 this->featurewarnings = 0;
177 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
179 feature_t*f = gfxglobals->featurewarnings;
181 if(!strcmp(feature, f->string))
185 f = (feature_t*)malloc(sizeof(feature_t));
186 f->string = strdup(feature);
187 f->next = gfxglobals->featurewarnings;
188 gfxglobals->featurewarnings = f;
190 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
191 if(this->config_break_on_warning) {
192 msg("<fatal> Aborting conversion due to unsupported feature");
196 msg("<notice> File contains %s",feature);
199 void GFXOutputDev::warnfeature(const char*feature,char fully)
201 showfeature(feature,fully,1);
203 void GFXOutputDev::infofeature(const char*feature)
205 showfeature(feature,0,0);
208 GFXOutputState::GFXOutputState() {
210 this->createsoftmask = 0;
211 this->transparencygroup = 0;
213 this->grouprecording = 0;
217 GBool GFXOutputDev::interpretType3Chars()
222 typedef struct _drawnchar
240 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
241 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
246 free(chars);chars = 0;
253 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
257 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
260 chars[num_chars].x = x;
261 chars[num_chars].y = y;
262 chars[num_chars].color = color;
263 chars[num_chars].charid = charid;
267 char* writeOutStdFont(fontentry* f)
272 char* tmpFileName = mktmpname(namebuf1);
274 sprintf(namebuf2, "%s.afm", tmpFileName);
275 fi = fopen(namebuf2, "wb");
278 fwrite(f->afm, 1, f->afmlen, fi);
281 sprintf(namebuf2, "%s.pfb", tmpFileName);
282 fi = fopen(namebuf2, "wb");
285 fwrite(f->pfb, 1, f->pfblen, fi);
287 return strdup(namebuf2);
289 void unlinkfont(char* filename)
294 msg("<verbose> Removing temporary font file %s", filename);
297 if(!strncmp(&filename[l-4],".afm",4)) {
298 memcpy(&filename[l-4],".pfb",4); unlink(filename);
299 memcpy(&filename[l-4],".pfa",4); unlink(filename);
300 memcpy(&filename[l-4],".afm",4);
303 if(!strncmp(&filename[l-4],".pfa",4)) {
304 memcpy(&filename[l-4],".afm",4); unlink(filename);
305 memcpy(&filename[l-4],".pfa",4);
308 if(!strncmp(&filename[l-4],".pfb",4)) {
309 memcpy(&filename[l-4],".afm",4); unlink(filename);
310 memcpy(&filename[l-4],".pfb",4);
315 static int config_use_fontconfig = 1;
316 static int fcinitcalled = 0;
318 GFXGlobalParams::GFXGlobalParams()
319 : GlobalParams((char*)"")
321 //setupBaseFonts(char *dir); //not tested yet
323 GFXGlobalParams::~GFXGlobalParams()
325 msg("<verbose> Performing cleanups");
327 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
328 if(pdf2t1map[t].fullfilename) {
329 unlinkfont(pdf2t1map[t].fullfilename);
332 #ifdef HAVE_FONTCONFIG
333 if(config_use_fontconfig && fcinitcalled)
337 #ifdef HAVE_FONTCONFIG
338 static char fc_ismatch(FcPattern*match, char*family, char*style)
340 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
341 FcBool scalable=FcFalse, outline=FcFalse;
342 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
343 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
344 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
345 FcPatternGetBool(match, "outline", 0, &outline);
346 FcPatternGetBool(match, "scalable", 0, &scalable);
348 if(scalable!=FcTrue || outline!=FcTrue)
351 if (!strcasecmp(fcfamily, family)) {
352 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
355 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
361 char* fontconfig_searchForFont(char*name)
363 #ifdef HAVE_FONTCONFIG
364 if(!config_use_fontconfig)
367 // call init ony once
371 // check whether we have a config file
372 char* configfile = (char*)FcConfigFilename(0);
373 int configexists = 0;
374 FILE*fi = fopen(configfile, "rb");
376 configexists = 1;fclose(fi);
377 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
379 msg("<debug> Initializing FontConfig (no configfile)");
383 /* A fontconfig instance which didn't find a configfile is unbelievably
384 cranky, so let's just write out a small xml file and make fontconfig
386 FcConfig*c = FcConfigCreate();
388 char* tmpFileName = mktmpname(namebuf);
389 FILE*fi = fopen(tmpFileName, "wb");
390 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
392 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
394 fprintf(fi, "<dir>~/.fonts</dir>\n");
396 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
398 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
399 fprintf(fi, "</fontconfig>\n");
401 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
402 FcConfigBuildFonts(c);
403 FcConfigSetCurrent(c);
407 msg("<debug> FontConfig Initialization failed. Disabling.");
408 config_use_fontconfig = 0;
411 FcConfig * config = FcConfigGetCurrent();
413 msg("<debug> FontConfig Config Initialization failed. Disabling.");
414 config_use_fontconfig = 0;
418 /* add external fonts to fontconfig's config, too. */
419 fontfile_t*fd = global_fonts;
421 FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
425 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
426 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
427 if(!set || !set->nfont) {
428 msg("<debug> FontConfig has zero fonts. Disabling.");
429 config_use_fontconfig = 0;
433 if(getLogLevel() >= LOGLEVEL_TRACE) {
435 for(t=0;t<set->nfont;t++) {
436 char*fcfamily=0,*fcstyle=0,*filename=0;
437 FcBool scalable=FcFalse, outline=FcFalse;
438 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
439 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
440 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
441 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
442 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
443 if(scalable && outline) {
444 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
450 char*family = strdup(name);
452 char*dash = strchr(family, '-');
453 FcPattern*pattern = 0;
457 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
458 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
460 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
461 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
465 FcConfigSubstitute(0, pattern, FcMatchPattern);
466 FcDefaultSubstitute(pattern);
468 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
471 for(t=0;t<set->nfont;t++) {
472 FcPattern*match = set->fonts[t];
473 //FcPattern*match = FcFontMatch(0, pattern, &result);
474 if(fc_ismatch(match, family, style)) {
476 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
477 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
480 //FcPatternDestroy(match);
481 msg("<debug> fontconfig: returning filename %s", filename);
483 FcPatternDestroy(pattern);
484 FcFontSetDestroy(set);
485 return filename?strdup(filename):0;
490 FcPatternDestroy(pattern);
491 FcFontSetDestroy(set);
498 static DisplayFontParamKind detectFontType(const char*filename)
500 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
501 return displayFontTT;
502 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
503 return displayFontT1;
504 return displayFontTT;
507 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
509 msg("<verbose> looking for font %s in global params", fontName->getCString());
511 char*name = fontName->getCString();
513 /* see if it is a pdf standard font */
515 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
516 if(!strcmp(name, pdf2t1map[t].pdffont)) {
517 if(!pdf2t1map[t].fullfilename) {
518 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
519 if(!pdf2t1map[t].fullfilename) {
520 msg("<error> Couldn't save default font- is the Temp Directory writable?");
522 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
525 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
526 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
531 int bestlen = 0x7fffffff;
532 const char*bestfilename = 0;
534 #ifndef HAVE_FONTCONFIG
535 /* if we don't have fontconfig, try a simple filename-comparison approach */
536 fontfile_t*f = global_fonts;
538 if(strstr(f->filename, name)) {
539 if(f->len < bestlen) {
541 bestfilename = f->filename;
548 /* if we didn't find anything up to now, try looking for the
549 font via fontconfig */
552 filename = fontconfig_searchForFont(name);
554 filename = strdup(bestfilename);
558 msg("<verbose> Font %s maps to %s\n", name, filename);
559 DisplayFontParamKind kind = detectFontType(filename);
560 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
561 if(kind == displayFontTT) {
562 dfp->tt.fileName = new GString(filename);
564 dfp->t1.fileName = new GString(filename);
569 return GlobalParams::getDisplayFont(fontName);
572 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
575 gfxglobals = new GFXOutputGlobals();
579 this->xref = doc->getXRef();
581 this->type3active = 0;
584 this->user_movex = 0;
585 this->user_movey = 0;
588 this->user_clipx1 = 0;
589 this->user_clipy1 = 0;
590 this->user_clipx2 = 0;
591 this->user_clipy2 = 0;
592 this->current_fontinfo = 0;
593 this->current_text_stroke = 0;
594 this->current_text_clip = 0;
595 this->outer_clip_box = 0;
596 this->config_bigchar=0;
597 this->config_convertgradients=1;
598 this->config_break_on_warning=0;
599 this->config_remapunicode=0;
600 this->config_transparent=0;
601 this->config_extrafontdata = 0;
602 this->config_optimize_polygons = 0;
603 this->config_multiply = 1;
604 this->dashPattern = 0;
608 memset(states, 0, sizeof(states));
611 void GFXOutputDev::setParameter(const char*key, const char*value)
613 if(!strcmp(key,"breakonwarning")) {
614 this->config_break_on_warning = atoi(value);
615 } else if(!strcmp(key,"remapunicode")) {
616 this->config_remapunicode = atoi(value);
617 } else if(!strcmp(key,"transparent")) {
618 this->config_transparent = atoi(value);
619 } else if(!strcmp(key,"extrafontdata")) {
620 this->config_extrafontdata = atoi(value);
621 } else if(!strcmp(key,"convertgradients")) {
622 this->config_convertgradients = atoi(value);
623 } else if(!strcmp(key,"multiply")) {
624 this->config_multiply = atoi(value);
625 if(this->config_multiply<1)
626 this->config_multiply=1;
627 } else if(!strcmp(key,"optimize_polygons")) {
628 this->config_optimize_polygons = atoi(value);
632 void GFXOutputDev::setDevice(gfxdevice_t*dev)
637 void GFXOutputDev::setMove(int x,int y)
639 this->user_movex = x;
640 this->user_movey = y;
643 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
645 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
646 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
648 this->user_clipx1 = x1;
649 this->user_clipy1 = y1;
650 this->user_clipx2 = x2;
651 this->user_clipy2 = y2;
654 static char*getFontName(GfxFont*font)
657 GString*gstr = font->getName();
658 char* fname = gstr==0?0:gstr->getCString();
662 sprintf(buf, "UFONT%d", r->num);
663 fontid = strdup(buf);
665 fontid = strdup(fname);
669 char* plus = strchr(fontid, '+');
670 if(plus && plus < &fontid[strlen(fontid)-1]) {
671 fontname = strdup(plus+1);
673 fontname = strdup(fontid);
679 static void dumpFontInfo(const char*loglevel, GfxFont*font);
680 static int lastdumps[1024];
681 static int lastdumppos = 0;
686 static void showFontError(GfxFont*font, int nr)
690 for(t=0;t<lastdumppos;t++)
691 if(lastdumps[t] == r->num)
695 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
696 lastdumps[lastdumppos++] = r->num;
698 msg("<warning> The following font caused problems:");
700 msg("<warning> The following font caused problems (substituting):");
702 msg("<warning> The following Type 3 Font will be rendered as graphics:");
703 dumpFontInfo("<warning>", font);
706 static void dumpFontInfo(const char*loglevel, GfxFont*font)
708 char* id = getFontID(font);
709 char* name = getFontName(font);
710 Ref* r=font->getID();
711 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
713 GString*gstr = font->getTag();
715 msg("%s| Tag: %s", loglevel, id);
717 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
719 GfxFontType type=font->getType();
721 case fontUnknownType:
722 msg("%s| Type: unknown",loglevel);
725 msg("%s| Type: 1",loglevel);
728 msg("%s| Type: 1C",loglevel);
731 msg("%s| Type: 3",loglevel);
734 msg("%s| Type: TrueType",loglevel);
737 msg("%s| Type: CIDType0",loglevel);
740 msg("%s| Type: CIDType0C",loglevel);
743 msg("%s| Type: CIDType2",loglevel);
748 GBool embedded = font->getEmbeddedFontID(&embRef);
750 if(font->getEmbeddedFontName()) {
751 embeddedName = font->getEmbeddedFontName()->getCString();
754 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
756 gstr = font->getExtFontFile();
758 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
760 // Get font descriptor flags.
761 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
762 if(font->isSerif()) msg("%s| is serif", loglevel);
763 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
764 if(font->isItalic()) msg("%s| is italic", loglevel);
765 if(font->isBold()) msg("%s| is bold", loglevel);
771 //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");}
772 //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");}
774 void dump_outline(gfxline_t*line)
776 /*gfxbbox_t*r = gfxline_isrectangle(line);
778 printf("is not a rectangle\n");
780 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
784 if(line->type == gfx_moveTo) {
785 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
786 } else if(line->type == gfx_lineTo) {
787 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
788 } else if(line->type == gfx_splineTo) {
789 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
795 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
797 int num = path->getNumSubpaths();
800 double lastx=0,lasty=0,posx=0,posy=0;
803 msg("<warning> empty path");
807 gfxdrawer_target_gfxline(&draw);
809 for(t = 0; t < num; t++) {
810 GfxSubpath *subpath = path->getSubpath(t);
811 int subnum = subpath->getNumPoints();
812 double bx=0,by=0,cx=0,cy=0;
814 for(s=0;s<subnum;s++) {
817 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
820 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
821 draw.lineTo(&draw, lastx, lasty);
823 draw.moveTo(&draw, x,y);
828 } else if(subpath->getCurve(s) && cpos==0) {
832 } else if(subpath->getCurve(s) && cpos==1) {
840 draw.lineTo(&draw, x,y);
842 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
849 /* fix non-closed lines */
850 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
851 draw.lineTo(&draw, lastx, lasty);
853 gfxline_t*result = (gfxline_t*)draw.result(&draw);
855 gfxline_optimize(result);
860 GBool GFXOutputDev::useTilingPatternFill()
862 infofeature("tiled patterns");
863 // if(config_convertgradients)
867 GBool GFXOutputDev::useShadedFills()
869 infofeature("shaded fills");
870 if(config_convertgradients)
875 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
877 state->transform(x,y,nx,ny);
878 *nx += user_movex + clipmovex;
879 *ny += user_movey + clipmovey;
883 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
884 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
885 int paintType, Dict *resDict,
886 double *mat, double *bbox,
887 int x0, int y0, int x1, int y1,
888 double xStep, double yStep)
890 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
891 int paintType, Dict *resDict,
892 double *mat, double *bbox,
893 int x0, int y0, int x1, int y1,
894 double xStep, double yStep)
897 msg("<debug> tilingPatternFill");
898 infofeature("tiling pattern fills");
901 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
903 msg("<error> functionShadedFill not supported yet");
904 infofeature("function shaded fills");
907 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
911 colspace->getRGB(col, &rgb);
912 c.r = colToByte(rgb.r);
913 c.g = colToByte(rgb.g);
914 c.b = colToByte(rgb.b);
919 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
921 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
922 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
925 this->transformXY(state, x0,y0, &x0,&y0);
926 this->transformXY(state, x1,y1, &x1,&y1);
927 this->transformXY(state, x2,y2, &x2,&y2);
932 shading->getColor(0.0, &color0);
933 shading->getColor(0.5, &color1);
934 shading->getColor(1.0, &color2);
936 GfxColorSpace* colspace = shading->getColorSpace();
938 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
939 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
940 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
941 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
942 infofeature("radial shaded fills");
944 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
948 g[0].color = col2col(colspace, &color0);
949 g[1].color = col2col(colspace, &color1);
950 g[2].color = col2col(colspace, &color2);
955 gfxbbox_t b = states[statepos].clipbbox;
956 gfxline_t p1,p2,p3,p4,p5;
957 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
958 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
959 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
960 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
961 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
964 //m.m00 = (x3-x0); m.m10 = (x1-x0);
965 //m.m01 = (y3-y0); m.m11 = (y1-y0);
966 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
967 m.m00 = (x1-x0); m.m10 = (x2-x0);
968 m.m01 = (y1-y0); m.m11 = (y2-y0);
972 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
976 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
979 shading->getCoords(&x0,&y0,&x1,&y1);
980 this->transformXY(state, x0,y0,&x0,&y0);
981 this->transformXY(state, x1,y1,&x1,&y1);
986 shading->getColor(0.0, &color0);
987 shading->getColor(0.5, &color1);
988 shading->getColor(1.0, &color2);
990 GfxColorSpace* colspace = shading->getColorSpace();
992 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
993 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
994 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
995 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
997 infofeature("axial shaded fills");
999 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1003 g[0].color = col2col(colspace, &color0);
1004 g[1].color = col2col(colspace, &color1);
1005 g[2].color = col2col(colspace, &color2);
1010 double xMin,yMin,xMax,yMax;
1011 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1012 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1013 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1016 xMin = 1024; yMin = 1024;
1018 gfxbbox_t b = states[statepos].clipbbox;
1019 gfxline_t p1,p2,p3,p4,p5;
1020 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1021 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1022 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1023 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1024 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1026 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1027 the middle of the two control points */
1029 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1030 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1031 m.tx = (x0 + x1)/2 - 0.5;
1032 m.ty = (y0 + y1)/2 - 0.5;
1034 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1040 GBool GFXOutputDev::useDrawForm()
1042 infofeature("forms");
1045 void GFXOutputDev::drawForm(Ref id)
1047 msg("<error> drawForm not implemented");
1049 GBool GFXOutputDev::needNonText()
1053 void GFXOutputDev::endPage()
1055 msg("<verbose> endPage (GfxOutputDev)");
1056 if(outer_clip_box) {
1057 device->endclip(device);
1060 this->dashPattern = 0;
1061 /* notice: we're not fully done yet with this page- there might still be
1062 a few calls to drawLink() yet to come */
1065 static inline double sqr(double x) {return x*x;}
1067 #define STROKE_FILL 1
1068 #define STROKE_CLIP 2
1069 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1071 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1072 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1073 double miterLimit = state->getMiterLimit();
1074 double width = state->getTransformedLineWidth();
1077 double opaq = state->getStrokeOpacity();
1079 state->getFillRGB(&rgb);
1081 state->getStrokeRGB(&rgb);
1083 col.r = colToByte(rgb.r);
1084 col.g = colToByte(rgb.g);
1085 col.b = colToByte(rgb.b);
1086 col.a = (unsigned char)(opaq*255);
1088 gfx_capType capType = gfx_capRound;
1089 if(lineCap == 0) capType = gfx_capButt;
1090 else if(lineCap == 1) capType = gfx_capRound;
1091 else if(lineCap == 2) capType = gfx_capSquare;
1093 gfx_joinType joinType = gfx_joinRound;
1094 if(lineJoin == 0) joinType = gfx_joinMiter;
1095 else if(lineJoin == 1) joinType = gfx_joinRound;
1096 else if(lineJoin == 2) joinType = gfx_joinBevel;
1098 gfxline_t*line2 = 0;
1100 if(this->dashLength && this->dashPattern) {
1101 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1104 /* try to find out how much the transformation matrix would
1105 stretch the dashes, and factor that into the dash lengths.
1106 This is not the entirely correct approach- it would be
1107 better to first convert the path to an unscaled version,
1108 then apply dashing, and then transform the path using
1109 the current transformation matrix. However there are few
1110 PDFs which actually stretch a dashed path in a non-orthonormal
1112 double tx1, ty1, tx2, ty2;
1113 this->transformXY(state, 0, 0, &tx1, &ty1);
1114 this->transformXY(state, 1, 1, &tx2, &ty2);
1115 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1117 msg("<trace> %d dashes", this->dashLength);
1118 msg("<trace> | phase: %f", this->dashStart);
1119 for(t=0;t<this->dashLength;t++) {
1120 dash[t] = (float)this->dashPattern[t] * f;
1123 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1125 dash[this->dashLength] = -1;
1126 if(getLogLevel() >= LOGLEVEL_TRACE) {
1130 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1133 msg("<trace> After dashing:");
1136 if(getLogLevel() >= LOGLEVEL_TRACE) {
1137 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1139 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1140 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1142 col.r,col.g,col.b,col.a
1147 if(flags&STROKE_FILL) {
1148 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1149 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1150 if(getLogLevel() >= LOGLEVEL_TRACE) {
1151 dump_outline(gfxline);
1154 msg("<warning> Empty polygon (resulting from stroked line)");
1156 if(flags&STROKE_CLIP) {
1157 device->startclip(device, gfxline);
1158 states[statepos].clipping++;
1160 device->fill(device, gfxline, &col);
1162 gfxline_free(gfxline);
1165 if(flags&STROKE_CLIP)
1166 msg("<error> Stroke&clip not supported at the same time");
1167 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1171 gfxline_free(line2);
1174 gfxcolor_t getFillColor(GfxState * state)
1177 double opaq = state->getFillOpacity();
1178 state->getFillRGB(&rgb);
1180 col.r = colToByte(rgb.r);
1181 col.g = colToByte(rgb.g);
1182 col.b = colToByte(rgb.b);
1183 col.a = (unsigned char)(opaq*255);
1187 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1189 gfxcolor_t col = getFillColor(state);
1191 if(getLogLevel() >= LOGLEVEL_TRACE) {
1192 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1195 device->fill(device, line, &col);
1198 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1200 if(getLogLevel() >= LOGLEVEL_TRACE) {
1203 gfxbbox_t bbox = gfxline_getbbox(line);
1204 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1206 device->startclip(device, line);
1207 states[statepos].clipping++;
1210 void GFXOutputDev::clip(GfxState *state)
1212 GfxPath * path = state->getPath();
1213 msg("<trace> clip");
1214 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1215 if(config_optimize_polygons) {
1216 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1220 clipToGfxLine(state, line);
1224 void GFXOutputDev::eoClip(GfxState *state)
1226 GfxPath * path = state->getPath();
1227 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1228 clipToGfxLine(state, line);
1231 void GFXOutputDev::clipToStrokePath(GfxState *state)
1233 GfxPath * path = state->getPath();
1234 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1236 if(getLogLevel() >= LOGLEVEL_TRACE) {
1237 double width = state->getTransformedLineWidth();
1238 msg("<trace> cliptostrokepath width=%f", width);
1242 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1246 void GFXOutputDev::finish()
1248 if(outer_clip_box) {
1250 device->endclip(device);
1256 GFXOutputDev::~GFXOutputDev()
1259 if(this->dashPattern) {
1260 free(this->dashPattern);this->dashPattern = 0;
1263 GBool GFXOutputDev::upsideDown()
1267 GBool GFXOutputDev::useDrawChar()
1272 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1273 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1275 static char tmp_printstr[4096];
1276 char* makeStringPrintable(char*str)
1278 int len = strlen(str);
1285 for(t=0;t<len;t++) {
1290 tmp_printstr[t] = c;
1293 tmp_printstr[len++] = '.';
1294 tmp_printstr[len++] = '.';
1295 tmp_printstr[len++] = '.';
1297 tmp_printstr[len] = 0;
1298 return tmp_printstr;
1300 void GFXOutputDev::updateFontMatrix(GfxState*state)
1302 double* ctm = state->getCTM();
1303 double fontSize = state->getFontSize();
1304 double*textMat = state->getTextMat();
1306 /* taking the absolute value of horizScaling seems to be required for
1307 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1308 double hscale = fabs(state->getHorizScaling());
1310 // from xpdf-3.02/SplashOutputDev:updateFont
1311 double mm11 = textMat[0] * fontSize * hscale;
1312 double mm12 = textMat[1] * fontSize * hscale;
1313 double mm21 = textMat[2] * fontSize;
1314 double mm22 = textMat[3] * fontSize;
1316 // multiply with ctm, like state->getFontTransMat() does
1317 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1318 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1319 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1320 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1321 this->current_font_matrix.tx = 0;
1322 this->current_font_matrix.ty = 0;
1325 void GFXOutputDev::beginString(GfxState *state, GString *s)
1327 int render = state->getRender();
1328 if(current_text_stroke) {
1329 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1332 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1335 static gfxline_t* mkEmptyGfxShape(double x, double y)
1337 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1338 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1342 static char isValidUnicode(int c)
1344 if(c>=32 && c<0x2fffe)
1349 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1350 double dx, double dy,
1351 double originX, double originY,
1352 CharCode charid, int nBytes, Unicode *_u, int uLen)
1354 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1355 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1359 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1361 int render = state->getRender();
1362 gfxcolor_t col = getFillColor(state);
1364 // check for invisible text -- this is used by Acrobat Capture
1365 if (render == RENDER_INVISIBLE) {
1367 if(!config_extrafontdata)
1371 GfxFont*font = state->getFont();
1373 if(font->getType() == fontType3) {
1374 /* type 3 chars are passed as graphics */
1375 msg("<debug> type3 char at %f/%f", x, y);
1379 Unicode u = uLen?(_u[0]):0;
1380 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);
1382 gfxmatrix_t m = this->current_font_matrix;
1383 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1384 //m.tx += originX; m.ty += originY;
1386 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1387 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1389 msg("<debug> Drawing glyph %d as shape", charid);
1390 if(!gfxglobals->textmodeinfo) {
1391 msg("<notice> Some texts will be rendered as shape");
1392 gfxglobals->textmodeinfo = 1;
1394 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1395 gfxline_t*tglyph = gfxline_clone(glyph);
1396 gfxline_transform(tglyph, &m);
1397 if((render&3) != RENDER_INVISIBLE) {
1398 gfxline_t*add = gfxline_clone(tglyph);
1399 current_text_stroke = gfxline_append(current_text_stroke, add);
1401 if(render&RENDER_CLIP) {
1402 gfxline_t*add = gfxline_clone(tglyph);
1403 current_text_clip = gfxline_append(current_text_clip, add);
1404 if(!current_text_clip) {
1405 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1408 gfxline_free(tglyph);
1412 void GFXOutputDev::endString(GfxState *state)
1414 int render = state->getRender();
1415 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1417 if(current_text_stroke) {
1418 /* fillstroke and stroke text rendering objects we can process right
1419 now (as there may be texts of other rendering modes in this
1420 text object)- clipping objects have to wait until endTextObject,
1422 device->setparameter(device, "mark","TXT");
1423 if((render&3) == RENDER_FILL) {
1424 fillGfxLine(state, current_text_stroke);
1425 gfxline_free(current_text_stroke);
1426 current_text_stroke = 0;
1427 } else if((render&3) == RENDER_FILLSTROKE) {
1428 fillGfxLine(state, current_text_stroke);
1429 strokeGfxline(state, current_text_stroke,0);
1430 gfxline_free(current_text_stroke);
1431 current_text_stroke = 0;
1432 } else if((render&3) == RENDER_STROKE) {
1433 strokeGfxline(state, current_text_stroke,0);
1434 gfxline_free(current_text_stroke);
1435 current_text_stroke = 0;
1437 device->setparameter(device, "mark","");
1441 void GFXOutputDev::endTextObject(GfxState *state)
1443 int render = state->getRender();
1444 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1446 if(current_text_clip) {
1447 device->setparameter(device, "mark","TXT");
1448 clipToGfxLine(state, current_text_clip);
1449 device->setparameter(device, "mark","");
1450 gfxline_free(current_text_clip);
1451 current_text_clip = 0;
1455 /* the logic seems to be as following:
1456 first, beginType3Char is called, with the charcode and the coordinates.
1457 if this function returns true, it already knew about the char and has now drawn it.
1458 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1459 called with some parameters.
1460 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1461 at the position first passed to beginType3Char). the char ends with endType3Char.
1463 The drawing operations between beginType3Char and endType3Char are somewhat different to
1464 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1465 color determines the color of a font)
1468 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1470 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1473 if(config_extrafontdata && current_fontinfo) {
1475 gfxmatrix_t m = this->current_font_matrix;
1476 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1477 m.m00*=INTERNAL_FONT_SIZE;
1478 m.m01*=INTERNAL_FONT_SIZE;
1479 m.m10*=INTERNAL_FONT_SIZE;
1480 m.m11*=INTERNAL_FONT_SIZE;
1482 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1483 msg("<error> Invalid charid %d for font", charid);
1486 gfxcolor_t col={0,0,0,0};
1487 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1488 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1492 /* the character itself is going to be passed using the draw functions */
1493 return gFalse; /* gTrue= is_in_cache? */
1496 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1498 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1501 void GFXOutputDev::endType3Char(GfxState *state)
1504 msg("<debug> endType3Char");
1507 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1509 this->currentpage = pageNum;
1511 int rot = doc->getPageRotate(1);
1512 gfxcolor_t white = {255,255,255,255};
1513 gfxcolor_t black = {255,0,0,0};
1515 gfxline_t clippath[5];
1517 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1518 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1519 Use CropBox, not MediaBox, as page size
1526 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1527 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1529 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1530 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1532 this->clipmovex = -(int)x1;
1533 this->clipmovey = -(int)y1;
1535 /* apply user clip box */
1536 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1537 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1538 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1539 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1540 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1541 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1544 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1546 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);
1548 msg("<verbose> page is rotated %d degrees", rot);
1550 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1551 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1552 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1553 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1554 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1555 device->startclip(device, clippath); outer_clip_box = 1;
1556 if(!config_transparent) {
1557 device->fill(device, clippath, &white);
1559 states[statepos].clipbbox.xmin = x1;
1560 states[statepos].clipbbox.ymin = x1;
1561 states[statepos].clipbbox.xmax = x2;
1562 states[statepos].clipbbox.ymax = y2;
1564 this->dashPattern = 0;
1565 this->dashLength = 0;
1566 this->dashStart = 0;
1570 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1572 double x1, y1, x2, y2;
1573 gfxline_t points[5];
1576 msg("<debug> drawlink");
1578 link->getRect(&x1, &y1, &x2, &y2);
1579 cvtUserToDev(x1, y1, &x, &y);
1580 points[0].type = gfx_moveTo;
1581 points[0].x = points[4].x = x + user_movex + clipmovex;
1582 points[0].y = points[4].y = y + user_movey + clipmovey;
1583 points[0].next = &points[1];
1584 cvtUserToDev(x2, y1, &x, &y);
1585 points[1].type = gfx_lineTo;
1586 points[1].x = x + user_movex + clipmovex;
1587 points[1].y = y + user_movey + clipmovey;
1588 points[1].next = &points[2];
1589 cvtUserToDev(x2, y2, &x, &y);
1590 points[2].type = gfx_lineTo;
1591 points[2].x = x + user_movex + clipmovex;
1592 points[2].y = y + user_movey + clipmovey;
1593 points[2].next = &points[3];
1594 cvtUserToDev(x1, y2, &x, &y);
1595 points[3].type = gfx_lineTo;
1596 points[3].x = x + user_movex + clipmovex;
1597 points[3].y = y + user_movey + clipmovey;
1598 points[3].next = &points[4];
1599 cvtUserToDev(x1, y1, &x, &y);
1600 points[4].type = gfx_lineTo;
1601 points[4].x = x + user_movex + clipmovex;
1602 points[4].y = y + user_movey + clipmovey;
1605 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1606 points[0].x, points[0].y,
1607 points[1].x, points[1].y,
1608 points[2].x, points[2].y,
1609 points[3].x, points[3].y);
1611 if(getLogLevel() >= LOGLEVEL_TRACE) {
1612 dump_outline(points);
1615 LinkAction*action=link->getAction();
1618 const char*type = "-?-";
1621 msg("<trace> drawlink action=%d", action->getKind());
1622 switch(action->getKind())
1626 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1627 LinkDest *dest=NULL;
1628 if (ha->getDest()==NULL)
1629 dest=catalog->findDest(ha->getNamedDest());
1631 dest=ha->getDest()->copy();
1633 if (dest->isPageRef()){
1634 Ref pageref=dest->getPageRef();
1635 page=catalog->findPage(pageref.num,pageref.gen);
1637 else page=dest->getPageNum();
1638 sprintf(buf, "%d", page);
1646 LinkGoToR*l = (LinkGoToR*)action;
1647 GString*g = l->getFileName();
1649 s = strdup(g->getCString());
1651 /* if the GoToR link has no filename, then
1652 try to find a refernce in the *local*
1654 GString*g = l->getNamedDest();
1656 s = strdup(g->getCString());
1662 LinkNamed*l = (LinkNamed*)action;
1663 GString*name = l->getName();
1665 s = strdup(name->lowerCase()->getCString());
1666 named = name->getCString();
1669 if(strstr(s, "next") || strstr(s, "forward"))
1671 page = currentpage + 1;
1673 else if(strstr(s, "prev") || strstr(s, "back"))
1675 page = currentpage - 1;
1677 else if(strstr(s, "last") || strstr(s, "end"))
1679 if(this->page2page && this->num_pages) {
1680 page = this->page2page[this->num_pages-1];
1683 else if(strstr(s, "first") || strstr(s, "top"))
1691 case actionLaunch: {
1693 LinkLaunch*l = (LinkLaunch*)action;
1694 GString * str = new GString(l->getFileName());
1695 GString * params = l->getParams();
1697 str->append(params);
1698 s = strdup(str->getCString());
1705 LinkURI*l = (LinkURI*)action;
1706 GString*g = l->getURI();
1708 url = g->getCString();
1713 case actionUnknown: {
1715 LinkUnknown*l = (LinkUnknown*)action;
1720 msg("<error> Unknown link type!");
1725 if(!s) s = strdup("-?-");
1727 msg("<trace> drawlink s=%s", s);
1729 if(!gfxglobals->linkinfo && (page || s))
1731 msg("<notice> File contains links");
1732 gfxglobals->linkinfo = 1;
1738 for(t=1;t<=this->num_pages;t++) {
1739 if(this->page2page[t]==page) {
1749 sprintf(buf, "page%d", lpage);
1750 device->drawlink(device, points, buf);
1754 device->drawlink(device, points, s);
1757 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1761 void GFXOutputDev::saveState(GfxState *state) {
1762 dbg("saveState %08x", state); dbgindent+=2;
1764 msg("<trace> saveState %08x", state);
1767 msg("<fatal> Too many nested states in pdf.");
1771 states[statepos].state = state;
1772 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1773 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1774 states[statepos].clipping = 0;
1775 states[statepos].olddevice = 0;
1776 states[statepos].clipbbox = states[statepos-1].clipbbox;
1779 void GFXOutputDev::restoreState(GfxState *state) {
1780 dbgindent-=2; dbg("restoreState %08x", state);
1783 msg("<fatal> Invalid restoreState");
1786 msg("<trace> restoreState %08x%s%s", state,
1787 states[statepos].softmask?" (end softmask)":"",
1788 states[statepos].clipping?" (end clipping)":"");
1789 if(states[statepos].softmask) {
1790 clearSoftMask(state);
1794 while(states[statepos].clipping) {
1795 device->endclip(device);
1796 states[statepos].clipping--;
1798 if(states[statepos].state!=state) {
1799 msg("<fatal> bad state nesting");
1802 states[statepos].state=0;
1806 void GFXOutputDev::updateLineDash(GfxState *state)
1808 if(this->dashPattern) {
1809 free(this->dashPattern);this->dashPattern = 0;
1811 double *pattern = 0;
1812 state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1813 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1814 if(!this->dashLength) {
1815 this->dashPattern = 0;
1817 double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1818 memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1819 this->dashPattern = p;
1823 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1825 this->page2page = page2page;
1826 this->num_pages = num_pages;
1829 void GFXOutputDev::updateLineWidth(GfxState *state)
1831 double width = state->getTransformedLineWidth();
1834 void GFXOutputDev::updateLineCap(GfxState *state)
1836 int c = state->getLineCap();
1839 void GFXOutputDev::updateLineJoin(GfxState *state)
1841 int j = state->getLineJoin();
1844 void GFXOutputDev::updateFillColor(GfxState *state)
1847 double opaq = state->getFillOpacity();
1848 state->getFillRGB(&rgb);
1850 void GFXOutputDev::updateFillOpacity(GfxState *state)
1853 double opaq = state->getFillOpacity();
1854 state->getFillRGB(&rgb);
1855 dbg("update fillopaq %f", opaq);
1857 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1859 double opaq = state->getFillOpacity();
1860 dbg("update strokeopaq %f", opaq);
1862 void GFXOutputDev::updateFillOverprint(GfxState *state)
1864 double opaq = state->getFillOverprint();
1865 dbg("update filloverprint %f", opaq);
1867 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1869 double opaq = state->getStrokeOverprint();
1870 dbg("update strokeoverprint %f", opaq);
1872 void GFXOutputDev::updateTransfer(GfxState *state)
1874 dbg("update transfer");
1878 void GFXOutputDev::updateStrokeColor(GfxState *state)
1881 double opaq = state->getStrokeOpacity();
1882 state->getStrokeRGB(&rgb);
1885 void GFXOutputDev::updateFont(GfxState *state)
1887 GfxFont* gfxFont = state->getFont();
1891 char*id = getFontID(gfxFont);
1892 msg("<verbose> Updating font to %s", id);
1893 if(gfxFont->getType() == fontType3) {
1894 infofeature("Type3 fonts");
1895 if(!config_extrafontdata) {
1900 msg("<error> Internal Error: FontID is null");
1904 this->current_fontinfo = this->info->getFont(id);
1906 if(!this->current_fontinfo) {
1907 msg("<error> Internal Error: no fontinfo for font %s", id);
1910 if(!this->current_fontinfo->seen) {
1911 dumpFontInfo("<verbose>", gfxFont);
1914 current_gfxfont = this->current_fontinfo->getGfxFont();
1915 device->addfont(device, current_gfxfont);
1918 updateFontMatrix(state);
1921 #define SQR(x) ((x)*(x))
1923 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1925 if((newwidth<1 || newheight<1) ||
1926 (width<=newwidth || height<=newheight))
1928 unsigned char*newdata;
1930 newdata= (unsigned char*)malloc(newwidth*newheight);
1931 double fx = ((double)width)/newwidth;
1932 double fy = ((double)height)/newheight;
1934 int blocksize = (int)(8192/(fx*fy));
1935 int r = 8192*256/palettesize;
1936 for(x=0;x<newwidth;x++) {
1937 double ex = px + fx;
1938 int fromx = (int)px;
1940 int xweight1 = (int)((1-(px-fromx))*256);
1941 int xweight2 = (int)((ex-tox)*256);
1943 for(y=0;y<newheight;y++) {
1944 double ey = py + fy;
1945 int fromy = (int)py;
1947 int yweight1 = (int)((1-(py-fromy))*256);
1948 int yweight2 = (int)((ey-toy)*256);
1955 for(xx=fromx;xx<=tox;xx++)
1956 for(yy=fromy;yy<=toy;yy++) {
1957 int b = 1-data[width*yy+xx];
1959 if(xx==fromx) weight = (weight*xweight1)/256;
1960 if(xx==tox) weight = (weight*xweight2)/256;
1961 if(yy==fromy) weight = (weight*yweight1)/256;
1962 if(yy==toy) weight = (weight*yweight2)/256;
1965 //if(a) a=(palettesize-1)*r/blocksize;
1966 newdata[y*newwidth+x] = (a*blocksize)/r;
1974 #define IMAGE_TYPE_JPEG 0
1975 #define IMAGE_TYPE_LOSSLESS 1
1977 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1978 double x1,double y1,
1979 double x2,double y2,
1980 double x3,double y3,
1981 double x4,double y4, int type, int multiply)
1983 gfxcolor_t*newpic=0;
1985 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1986 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1988 gfxline_t p1,p2,p3,p4,p5;
1989 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1990 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1991 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1992 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1993 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1995 {p1.x = (int)(p1.x*20)/20.0;
1996 p1.y = (int)(p1.y*20)/20.0;
1997 p2.x = (int)(p2.x*20)/20.0;
1998 p2.y = (int)(p2.y*20)/20.0;
1999 p3.x = (int)(p3.x*20)/20.0;
2000 p3.y = (int)(p3.y*20)/20.0;
2001 p4.x = (int)(p4.x*20)/20.0;
2002 p4.y = (int)(p4.y*20)/20.0;
2003 p5.x = (int)(p5.x*20)/20.0;
2004 p5.y = (int)(p5.y*20)/20.0;
2008 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2009 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2011 m.tx = p1.x - 0.5*multiply;
2012 m.ty = p1.y - 0.5*multiply;
2015 img.data = (gfxcolor_t*)data;
2019 if(type == IMAGE_TYPE_JPEG)
2020 /* TODO: pass image_dpi to device instead */
2021 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2024 dev->fillbitmap(dev, &p1, &img, &m, 0);
2027 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2028 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2030 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2033 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2034 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2036 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2040 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2041 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2042 GBool inlineImg, int mask, int*maskColors,
2043 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2045 /* the code in this function is *old*. It's not pretty, but it works. */
2047 double x1,y1,x2,y2,x3,y3,x4,y4;
2048 ImageStream *imgStr;
2053 unsigned char* maskbitmap = 0;
2056 ncomps = colorMap->getNumPixelComps();
2057 bits = colorMap->getBits();
2062 unsigned char buf[8];
2063 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2065 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2066 imgMaskStr->reset();
2067 unsigned char pal[256];
2068 int n = 1 << colorMap->getBits();
2073 maskColorMap->getGray(pixBuf, &gray);
2074 pal[t] = colToByte(gray);
2076 for (y = 0; y < maskHeight; y++) {
2077 for (x = 0; x < maskWidth; x++) {
2078 imgMaskStr->getPixel(buf);
2079 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2084 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2085 imgMaskStr->reset();
2086 for (y = 0; y < maskHeight; y++) {
2087 for (x = 0; x < maskWidth; x++) {
2088 imgMaskStr->getPixel(buf);
2090 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2098 imgStr = new ImageStream(str, width, ncomps,bits);
2101 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2103 msg("<verbose> Ignoring %d by %d image", width, height);
2104 unsigned char buf[8];
2106 for (y = 0; y < height; ++y)
2107 for (x = 0; x < width; ++x) {
2108 imgStr->getPixel(buf);
2116 this->transformXY(state, 0, 1, &x1, &y1);
2117 this->transformXY(state, 0, 0, &x2, &y2);
2118 this->transformXY(state, 1, 0, &x3, &y3);
2119 this->transformXY(state, 1, 1, &x4, &y4);
2122 /* as type 3 bitmaps are antialized, we need to place them
2123 at integer coordinates, otherwise flash player's antializing
2124 will kick in and make everything blurry */
2125 x1 = (int)(x1);y1 = (int)(y1);
2126 x2 = (int)(x2);y2 = (int)(y2);
2127 x3 = (int)(x3);y3 = (int)(y3);
2128 x4 = (int)(x4);y4 = (int)(y4);
2131 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2133 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2134 gfxglobals->pbminfo = 1;
2137 msg("<verbose> drawing %d by %d masked picture", width, height);
2139 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2140 msg("<notice> File contains jpeg pictures");
2141 gfxglobals->jpeginfo = 1;
2145 unsigned char buf[8];
2147 unsigned char*pic = new unsigned char[width*height];
2148 gfxcolor_t pal[256];
2150 state->getFillRGB(&rgb);
2152 memset(pal,255,sizeof(pal));
2153 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2154 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2155 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2156 pal[0].a = 255; pal[1].a = 0;
2159 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2160 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2161 for (y = 0; y < height; ++y)
2162 for (x = 0; x < width; ++x)
2164 imgStr->getPixel(buf);
2167 pic[width*y+x] = buf[0];
2171 unsigned char*pic2 = 0;
2174 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2183 height = realheight;
2187 /* make a black/white palette */
2189 float r = 255./(float)(numpalette-1);
2191 for(t=0;t<numpalette;t++) {
2192 pal[t].r = colToByte(rgb.r);
2193 pal[t].g = colToByte(rgb.g);
2194 pal[t].b = colToByte(rgb.b);
2195 pal[t].a = (unsigned char)(t*r);
2200 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2201 for (y = 0; y < height; ++y) {
2202 for (x = 0; x < width; ++x) {
2203 pic2[width*y+x] = pal[pic[y*width+x]];
2206 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2210 if(maskbitmap) free(maskbitmap);
2216 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2217 gfxcolor_t*pic=new gfxcolor_t[width*height];
2218 for (y = 0; y < height; ++y) {
2219 for (x = 0; x < width; ++x) {
2220 imgStr->getPixel(pixBuf);
2221 colorMap->getRGB(pixBuf, &rgb);
2222 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2223 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2224 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2225 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2227 int x1 = x*maskWidth/width;
2228 int y1 = y*maskHeight/height;
2229 int x2 = (x+1)*maskWidth/width;
2230 int y2 = (y+1)*maskHeight/height;
2232 unsigned int alpha=0;
2233 unsigned int count=0;
2234 for(xx=x1;xx<x2;xx++)
2235 for(yy=y1;yy<y2;yy++) {
2236 alpha += maskbitmap[yy*maskWidth+xx];
2240 pic[width*y+x].a = alpha / count;
2242 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2247 if(str->getKind()==strDCT)
2248 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2250 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2253 if(maskbitmap) free(maskbitmap);
2256 gfxcolor_t*pic=new gfxcolor_t[width*height];
2257 gfxcolor_t pal[256];
2258 int n = 1 << colorMap->getBits();
2260 for(t=0;t<256;t++) {
2262 colorMap->getRGB(pixBuf, &rgb);
2264 {/*if(maskColors && *maskColors==t) {
2265 msg("<notice> Color %d is transparent", t);
2266 if (imgData->maskColors) {
2268 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2269 if (pix[i] < imgData->maskColors[2*i] ||
2270 pix[i] > imgData->maskColors[2*i+1]) {
2285 pal[t].r = (unsigned char)(colToByte(rgb.r));
2286 pal[t].g = (unsigned char)(colToByte(rgb.g));
2287 pal[t].b = (unsigned char)(colToByte(rgb.b));
2288 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2291 for (y = 0; y < height; ++y) {
2292 for (x = 0; x < width; ++x) {
2293 imgStr->getPixel(pixBuf);
2294 pic[width*y+x] = pal[pixBuf[0]];
2298 if(maskWidth < width && maskHeight < height) {
2299 for(y = 0; y < height; y++) {
2300 for (x = 0; x < width; x++) {
2301 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2305 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2306 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2307 double dx = width / maskWidth;
2308 double dy = height / maskHeight;
2310 for(y = 0; y < maskHeight; y++) {
2312 for (x = 0; x < maskWidth; x++) {
2313 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2314 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2322 height = maskHeight;
2325 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2329 if(maskbitmap) free(maskbitmap);
2334 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2335 int width, int height, GBool invert,
2338 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2339 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2340 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2343 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2344 int width, int height, GfxImageColorMap *colorMap,
2345 int *maskColors, GBool inlineImg)
2347 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2348 colorMap?"colorMap":"no colorMap",
2349 maskColors?"maskColors":"no maskColors",
2351 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2352 colorMap?"colorMap":"no colorMap",
2353 maskColors?"maskColors":"no maskColors",
2356 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2357 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2358 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2361 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2362 int width, int height,
2363 GfxImageColorMap *colorMap,
2364 Stream *maskStr, int maskWidth, int maskHeight,
2367 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2368 colorMap?"colorMap":"no colorMap",
2369 maskWidth, maskHeight);
2370 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2371 colorMap?"colorMap":"no colorMap",
2372 maskWidth, maskHeight);
2374 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2375 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2376 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2379 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2380 int width, int height,
2381 GfxImageColorMap *colorMap,
2383 int maskWidth, int maskHeight,
2384 GfxImageColorMap *maskColorMap)
2386 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2387 colorMap?"colorMap":"no colorMap",
2388 maskWidth, maskHeight);
2389 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2390 colorMap?"colorMap":"no colorMap",
2391 maskWidth, maskHeight);
2393 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2394 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2395 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2398 void GFXOutputDev::stroke(GfxState *state)
2402 GfxPath * path = state->getPath();
2403 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2404 strokeGfxline(state, line, 0);
2408 void GFXOutputDev::fill(GfxState *state)
2410 gfxcolor_t col = getFillColor(state);
2411 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2413 GfxPath * path = state->getPath();
2414 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2415 if(config_optimize_polygons) {
2416 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2420 fillGfxLine(state, line);
2424 void GFXOutputDev::eoFill(GfxState *state)
2426 gfxcolor_t col = getFillColor(state);
2427 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2429 GfxPath * path = state->getPath();
2430 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2431 fillGfxLine(state, line);
2436 static const char* dirseparator()
2445 void addGlobalFont(const char*filename)
2447 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2448 memset(f, 0, sizeof(fontfile_t));
2449 f->filename = filename;
2450 int len = strlen(filename);
2451 char*r1 = strrchr(filename, '/');
2452 char*r2 = strrchr(filename, '\\');
2460 msg("<verbose> Adding font \"%s\".", filename);
2461 if(global_fonts_next) {
2462 global_fonts_next->next = f;
2463 global_fonts_next = global_fonts_next->next;
2465 global_fonts_next = global_fonts = f;
2469 void addGlobalLanguageDir(const char*dir)
2471 msg("<notice> Adding %s to language pack directories", dir);
2474 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2475 strcpy(config_file, dir);
2476 strcat(config_file, dirseparator());
2477 strcat(config_file, "add-to-xpdfrc");
2479 fi = fopen(config_file, "rb");
2481 msg("<error> Could not open %s", config_file);
2484 globalParams->parseFile(new GString(config_file), fi);
2488 void addGlobalFontDir(const char*dirname)
2490 #ifdef HAVE_DIRENT_H
2491 DIR*dir = opendir(dirname);
2493 msg("<warning> Couldn't open directory %s", dirname);
2499 ent = readdir (dir);
2503 char*name = ent->d_name;
2509 if(!strncasecmp(&name[l-4], ".pfa", 4))
2511 if(!strncasecmp(&name[l-4], ".pfb", 4))
2513 if(!strncasecmp(&name[l-4], ".ttf", 4))
2516 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2517 strcpy(fontname, dirname);
2518 strcat(fontname, dirseparator());
2519 strcat(fontname, name);
2520 addGlobalFont(fontname);
2524 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2527 msg("<warning> No dirent.h");
2531 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2532 GfxColorSpace *blendingColorSpace,
2533 GBool isolated, GBool knockout,
2536 const char*colormodename = "";
2538 if(blendingColorSpace) {
2539 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2541 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);
2542 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);
2544 //states[statepos].createsoftmask |= forSoftMask;
2545 states[statepos].createsoftmask = forSoftMask;
2546 states[statepos].transparencygroup = !forSoftMask;
2547 states[statepos].isolated = isolated;
2549 states[statepos].olddevice = this->device;
2550 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2551 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2553 gfxdevice_record_init(this->device);
2555 /*if(!forSoftMask) { ////???
2556 state->setFillOpacity(0.0);
2561 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2564 gfxdevice_t*r = this->device;
2566 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2568 this->device = states[statepos].olddevice;
2570 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2571 /* if these errors occur more often, we should build a seperate
2572 transparency group stack, like xpdf/SplashOutputDev.cc does */
2573 restoreState(state);
2574 this->device = states[statepos].olddevice;
2576 states[statepos].olddevice = 0;
2578 gfxresult_t*recording = r->finish(r);
2580 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2581 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2583 if(states[statepos].createsoftmask) {
2584 states[statepos-1].softmaskrecording = recording;
2586 states[statepos-1].grouprecording = recording;
2589 states[statepos].createsoftmask = 0;
2590 states[statepos].transparencygroup = 0;
2594 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2596 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2597 "colordodge","colorburn","hardlight","softlight","difference",
2598 "exclusion","hue","saturation","color","luminosity"};
2600 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2601 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2603 if(state->getBlendMode() == gfxBlendNormal)
2604 infofeature("transparency groups");
2607 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2608 warnfeature(buffer, 0);
2611 gfxresult_t*grouprecording = states[statepos].grouprecording;
2613 int blendmode = state->getBlendMode();
2614 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2615 int alpha = (int)(state->getFillOpacity()*255);
2616 if(blendmode == gfxBlendMultiply && alpha>200)
2619 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2620 gfxdevice_ops_init(&ops, this->device, alpha);
2621 gfxresult_record_replay(grouprecording, &ops);
2624 grouprecording->destroy(grouprecording);
2626 states[statepos].grouprecording = 0;
2629 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2631 if(states[statepos].softmask) {
2632 /* shouldn't happen, but *does* happen */
2633 clearSoftMask(state);
2636 /* alpha = 1: retrieve mask values from alpha layer
2637 alpha = 0: retrieve mask values from luminance */
2639 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2640 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2641 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2642 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2644 infofeature("soft masks");
2646 warnfeature("soft masks from alpha channel",0);
2648 if(states[statepos].olddevice) {
2649 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2652 states[statepos].olddevice = this->device;
2653 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2654 gfxdevice_record_init(this->device);
2656 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2658 states[statepos].softmask = 1;
2659 states[statepos].softmask_alpha = alpha;
2662 static inline Guchar div255(int x) {
2663 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2666 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2668 if(c < min) c = min;
2669 if(c > max) c = max;
2673 void GFXOutputDev::clearSoftMask(GfxState *state)
2675 if(!states[statepos].softmask)
2677 states[statepos].softmask = 0;
2678 dbg("clearSoftMask statepos=%d", statepos);
2679 msg("<verbose> clearSoftMask statepos=%d", statepos);
2681 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2682 msg("<error> Error in softmask/tgroup ordering");
2686 gfxresult_t*mask = states[statepos].softmaskrecording;
2687 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2688 this->device = states[statepos].olddevice;
2690 /* get outline of all objects below the soft mask */
2691 gfxdevice_t uniondev;
2692 gfxdevice_union_init(&uniondev, 0);
2693 gfxresult_record_replay(below, &uniondev);
2694 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2695 uniondev.finish(&uniondev);
2696 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2697 gfxline_free(belowoutline);belowoutline=0;
2699 this->device->startclip(this->device, belowoutline);
2700 gfxresult_record_replay(below, this->device);
2701 gfxresult_record_replay(mask, this->device);
2702 this->device->endclip(this->device);
2705 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2706 if(width<=0 || height<=0)
2709 gfxdevice_t belowrender;
2710 gfxdevice_render_init(&belowrender);
2711 if(states[statepos+1].isolated) {
2712 belowrender.setparameter(&belowrender, "fillwhite", "1");
2714 belowrender.setparameter(&belowrender, "antialize", "2");
2715 belowrender.startpage(&belowrender, width, height);
2716 gfxresult_record_replay(below, &belowrender);
2717 belowrender.endpage(&belowrender);
2718 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2719 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2720 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2722 gfxdevice_t maskrender;
2723 gfxdevice_render_init(&maskrender);
2724 maskrender.startpage(&maskrender, width, height);
2725 gfxresult_record_replay(mask, &maskrender);
2726 maskrender.endpage(&maskrender);
2727 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2728 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2730 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2731 msg("<fatal> Internal error in mask drawing");
2736 for(y=0;y<height;y++) {
2737 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2738 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2739 for(x=0;x<width;x++) {
2741 if(states[statepos].softmask_alpha) {
2744 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2747 l2->a = div255(alpha*l2->a);
2749 /* DON'T premultiply alpha- this is done by fillbitmap,
2750 depending on the output device */
2751 //l2->r = div255(alpha*l2->r);
2752 //l2->g = div255(alpha*l2->g);
2753 //l2->b = div255(alpha*l2->b);
2759 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2762 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2763 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2765 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2767 mask->destroy(mask);
2768 below->destroy(below);
2769 maskresult->destroy(maskresult);
2770 belowresult->destroy(belowresult);
2771 states[statepos].softmaskrecording = 0;
2776 // public: ~MemCheck()
2778 // delete globalParams;globalParams=0;
2779 // Object::memCheck(stderr);
2780 // gMemReport(stderr);