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());
1630 else dest=ha->getDest();
1632 if (dest->isPageRef()){
1633 Ref pageref=dest->getPageRef();
1634 page=catalog->findPage(pageref.num,pageref.gen);
1636 else page=dest->getPageNum();
1637 sprintf(buf, "%d", page);
1644 LinkGoToR*l = (LinkGoToR*)action;
1645 GString*g = l->getFileName();
1647 s = strdup(g->getCString());
1649 /* if the GoToR link has no filename, then
1650 try to find a refernce in the *local*
1652 GString*g = l->getNamedDest();
1654 s = strdup(g->getCString());
1660 LinkNamed*l = (LinkNamed*)action;
1661 GString*name = l->getName();
1663 s = strdup(name->lowerCase()->getCString());
1664 named = name->getCString();
1667 if(strstr(s, "next") || strstr(s, "forward"))
1669 page = currentpage + 1;
1671 else if(strstr(s, "prev") || strstr(s, "back"))
1673 page = currentpage - 1;
1675 else if(strstr(s, "last") || strstr(s, "end"))
1677 if(this->page2page && this->num_pages) {
1678 page = this->page2page[this->num_pages-1];
1681 else if(strstr(s, "first") || strstr(s, "top"))
1689 case actionLaunch: {
1691 LinkLaunch*l = (LinkLaunch*)action;
1692 GString * str = new GString(l->getFileName());
1693 GString * params = l->getParams();
1695 str->append(params);
1696 s = strdup(str->getCString());
1703 LinkURI*l = (LinkURI*)action;
1704 GString*g = l->getURI();
1706 url = g->getCString();
1711 case actionUnknown: {
1713 LinkUnknown*l = (LinkUnknown*)action;
1718 msg("<error> Unknown link type!");
1723 if(!s) s = strdup("-?-");
1725 msg("<trace> drawlink s=%s", s);
1727 if(!gfxglobals->linkinfo && (page || s))
1729 msg("<notice> File contains links");
1730 gfxglobals->linkinfo = 1;
1736 for(t=1;t<=this->num_pages;t++) {
1737 if(this->page2page[t]==page) {
1747 sprintf(buf, "page%d", lpage);
1748 device->drawlink(device, points, buf);
1752 device->drawlink(device, points, s);
1755 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1759 void GFXOutputDev::saveState(GfxState *state) {
1760 dbg("saveState %08x", state); dbgindent+=2;
1762 msg("<trace> saveState %08x", state);
1765 msg("<fatal> Too many nested states in pdf.");
1769 states[statepos].state = state;
1770 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1771 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1772 states[statepos].clipping = 0;
1773 states[statepos].olddevice = 0;
1774 states[statepos].clipbbox = states[statepos-1].clipbbox;
1777 void GFXOutputDev::restoreState(GfxState *state) {
1778 dbgindent-=2; dbg("restoreState %08x", state);
1781 msg("<fatal> Invalid restoreState");
1784 msg("<trace> restoreState %08x%s%s", state,
1785 states[statepos].softmask?" (end softmask)":"",
1786 states[statepos].clipping?" (end clipping)":"");
1787 if(states[statepos].softmask) {
1788 clearSoftMask(state);
1792 while(states[statepos].clipping) {
1793 device->endclip(device);
1794 states[statepos].clipping--;
1796 if(states[statepos].state!=state) {
1797 msg("<fatal> bad state nesting");
1800 states[statepos].state=0;
1804 void GFXOutputDev::updateLineDash(GfxState *state)
1806 if(this->dashPattern) {
1807 free(this->dashPattern);this->dashPattern = 0;
1809 double *pattern = 0;
1810 state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1811 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1812 if(!this->dashLength) {
1813 this->dashPattern = 0;
1815 double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1816 memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1817 this->dashPattern = p;
1821 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1823 this->page2page = page2page;
1824 this->num_pages = num_pages;
1827 void GFXOutputDev::updateLineWidth(GfxState *state)
1829 double width = state->getTransformedLineWidth();
1832 void GFXOutputDev::updateLineCap(GfxState *state)
1834 int c = state->getLineCap();
1837 void GFXOutputDev::updateLineJoin(GfxState *state)
1839 int j = state->getLineJoin();
1842 void GFXOutputDev::updateFillColor(GfxState *state)
1845 double opaq = state->getFillOpacity();
1846 state->getFillRGB(&rgb);
1848 void GFXOutputDev::updateFillOpacity(GfxState *state)
1851 double opaq = state->getFillOpacity();
1852 state->getFillRGB(&rgb);
1853 dbg("update fillopaq %f", opaq);
1855 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1857 double opaq = state->getFillOpacity();
1858 dbg("update strokeopaq %f", opaq);
1860 void GFXOutputDev::updateFillOverprint(GfxState *state)
1862 double opaq = state->getFillOverprint();
1863 dbg("update filloverprint %f", opaq);
1865 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1867 double opaq = state->getStrokeOverprint();
1868 dbg("update strokeoverprint %f", opaq);
1870 void GFXOutputDev::updateTransfer(GfxState *state)
1872 dbg("update transfer");
1876 void GFXOutputDev::updateStrokeColor(GfxState *state)
1879 double opaq = state->getStrokeOpacity();
1880 state->getStrokeRGB(&rgb);
1883 void GFXOutputDev::updateFont(GfxState *state)
1885 GfxFont* gfxFont = state->getFont();
1889 char*id = getFontID(gfxFont);
1890 msg("<verbose> Updating font to %s", id);
1891 if(gfxFont->getType() == fontType3) {
1892 infofeature("Type3 fonts");
1893 if(!config_extrafontdata) {
1898 msg("<error> Internal Error: FontID is null");
1902 this->current_fontinfo = this->info->getFont(id);
1904 if(!this->current_fontinfo) {
1905 msg("<error> Internal Error: no fontinfo for font %s", id);
1908 if(!this->current_fontinfo->seen) {
1909 dumpFontInfo("<verbose>", gfxFont);
1912 current_gfxfont = this->current_fontinfo->getGfxFont();
1913 device->addfont(device, current_gfxfont);
1916 updateFontMatrix(state);
1919 #define SQR(x) ((x)*(x))
1921 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1923 if((newwidth<1 || newheight<1) ||
1924 (width<=newwidth || height<=newheight))
1926 unsigned char*newdata;
1928 newdata= (unsigned char*)malloc(newwidth*newheight);
1929 double fx = ((double)width)/newwidth;
1930 double fy = ((double)height)/newheight;
1932 int blocksize = (int)(8192/(fx*fy));
1933 int r = 8192*256/palettesize;
1934 for(x=0;x<newwidth;x++) {
1935 double ex = px + fx;
1936 int fromx = (int)px;
1938 int xweight1 = (int)((1-(px-fromx))*256);
1939 int xweight2 = (int)((ex-tox)*256);
1941 for(y=0;y<newheight;y++) {
1942 double ey = py + fy;
1943 int fromy = (int)py;
1945 int yweight1 = (int)((1-(py-fromy))*256);
1946 int yweight2 = (int)((ey-toy)*256);
1953 for(xx=fromx;xx<=tox;xx++)
1954 for(yy=fromy;yy<=toy;yy++) {
1955 int b = 1-data[width*yy+xx];
1957 if(xx==fromx) weight = (weight*xweight1)/256;
1958 if(xx==tox) weight = (weight*xweight2)/256;
1959 if(yy==fromy) weight = (weight*yweight1)/256;
1960 if(yy==toy) weight = (weight*yweight2)/256;
1963 //if(a) a=(palettesize-1)*r/blocksize;
1964 newdata[y*newwidth+x] = (a*blocksize)/r;
1972 #define IMAGE_TYPE_JPEG 0
1973 #define IMAGE_TYPE_LOSSLESS 1
1975 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1976 double x1,double y1,
1977 double x2,double y2,
1978 double x3,double y3,
1979 double x4,double y4, int type, int multiply)
1981 gfxcolor_t*newpic=0;
1983 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1984 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1986 gfxline_t p1,p2,p3,p4,p5;
1987 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1988 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1989 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1990 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1991 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1993 {p1.x = (int)(p1.x*20)/20.0;
1994 p1.y = (int)(p1.y*20)/20.0;
1995 p2.x = (int)(p2.x*20)/20.0;
1996 p2.y = (int)(p2.y*20)/20.0;
1997 p3.x = (int)(p3.x*20)/20.0;
1998 p3.y = (int)(p3.y*20)/20.0;
1999 p4.x = (int)(p4.x*20)/20.0;
2000 p4.y = (int)(p4.y*20)/20.0;
2001 p5.x = (int)(p5.x*20)/20.0;
2002 p5.y = (int)(p5.y*20)/20.0;
2006 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2007 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2009 m.tx = p1.x - 0.5*multiply;
2010 m.ty = p1.y - 0.5*multiply;
2013 img.data = (gfxcolor_t*)data;
2017 if(type == IMAGE_TYPE_JPEG)
2018 /* TODO: pass image_dpi to device instead */
2019 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2022 dev->fillbitmap(dev, &p1, &img, &m, 0);
2025 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2026 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2028 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2031 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2032 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2034 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2038 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2039 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2040 GBool inlineImg, int mask, int*maskColors,
2041 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2043 /* the code in this function is *old*. It's not pretty, but it works. */
2045 double x1,y1,x2,y2,x3,y3,x4,y4;
2046 ImageStream *imgStr;
2051 unsigned char* maskbitmap = 0;
2054 ncomps = colorMap->getNumPixelComps();
2055 bits = colorMap->getBits();
2060 unsigned char buf[8];
2061 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2063 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2064 imgMaskStr->reset();
2065 unsigned char pal[256];
2066 int n = 1 << colorMap->getBits();
2071 maskColorMap->getGray(pixBuf, &gray);
2072 pal[t] = colToByte(gray);
2074 for (y = 0; y < maskHeight; y++) {
2075 for (x = 0; x < maskWidth; x++) {
2076 imgMaskStr->getPixel(buf);
2077 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2082 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2083 imgMaskStr->reset();
2084 for (y = 0; y < maskHeight; y++) {
2085 for (x = 0; x < maskWidth; x++) {
2086 imgMaskStr->getPixel(buf);
2088 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2096 imgStr = new ImageStream(str, width, ncomps,bits);
2099 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2101 msg("<verbose> Ignoring %d by %d image", width, height);
2102 unsigned char buf[8];
2104 for (y = 0; y < height; ++y)
2105 for (x = 0; x < width; ++x) {
2106 imgStr->getPixel(buf);
2114 this->transformXY(state, 0, 1, &x1, &y1);
2115 this->transformXY(state, 0, 0, &x2, &y2);
2116 this->transformXY(state, 1, 0, &x3, &y3);
2117 this->transformXY(state, 1, 1, &x4, &y4);
2120 /* as type 3 bitmaps are antialized, we need to place them
2121 at integer coordinates, otherwise flash player's antializing
2122 will kick in and make everything blurry */
2123 x1 = (int)(x1);y1 = (int)(y1);
2124 x2 = (int)(x2);y2 = (int)(y2);
2125 x3 = (int)(x3);y3 = (int)(y3);
2126 x4 = (int)(x4);y4 = (int)(y4);
2129 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2131 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2132 gfxglobals->pbminfo = 1;
2135 msg("<verbose> drawing %d by %d masked picture", width, height);
2137 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2138 msg("<notice> File contains jpeg pictures");
2139 gfxglobals->jpeginfo = 1;
2143 unsigned char buf[8];
2145 unsigned char*pic = new unsigned char[width*height];
2146 gfxcolor_t pal[256];
2148 state->getFillRGB(&rgb);
2150 memset(pal,255,sizeof(pal));
2151 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2152 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2153 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2154 pal[0].a = 255; pal[1].a = 0;
2157 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2158 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2159 for (y = 0; y < height; ++y)
2160 for (x = 0; x < width; ++x)
2162 imgStr->getPixel(buf);
2165 pic[width*y+x] = buf[0];
2169 unsigned char*pic2 = 0;
2172 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2181 height = realheight;
2185 /* make a black/white palette */
2187 float r = 255./(float)(numpalette-1);
2189 for(t=0;t<numpalette;t++) {
2190 pal[t].r = colToByte(rgb.r);
2191 pal[t].g = colToByte(rgb.g);
2192 pal[t].b = colToByte(rgb.b);
2193 pal[t].a = (unsigned char)(t*r);
2198 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2199 for (y = 0; y < height; ++y) {
2200 for (x = 0; x < width; ++x) {
2201 pic2[width*y+x] = pal[pic[y*width+x]];
2204 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2208 if(maskbitmap) free(maskbitmap);
2214 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2215 gfxcolor_t*pic=new gfxcolor_t[width*height];
2216 for (y = 0; y < height; ++y) {
2217 for (x = 0; x < width; ++x) {
2218 imgStr->getPixel(pixBuf);
2219 colorMap->getRGB(pixBuf, &rgb);
2220 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2221 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2222 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2223 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2225 int x1 = x*maskWidth/width;
2226 int y1 = y*maskHeight/height;
2227 int x2 = (x+1)*maskWidth/width;
2228 int y2 = (y+1)*maskHeight/height;
2230 unsigned int alpha=0;
2231 unsigned int count=0;
2232 for(xx=x1;xx<x2;xx++)
2233 for(yy=y1;yy<y2;yy++) {
2234 alpha += maskbitmap[yy*maskWidth+xx];
2238 pic[width*y+x].a = alpha / count;
2240 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2245 if(str->getKind()==strDCT)
2246 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2248 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2251 if(maskbitmap) free(maskbitmap);
2254 gfxcolor_t*pic=new gfxcolor_t[width*height];
2255 gfxcolor_t pal[256];
2256 int n = 1 << colorMap->getBits();
2258 for(t=0;t<256;t++) {
2260 colorMap->getRGB(pixBuf, &rgb);
2262 {/*if(maskColors && *maskColors==t) {
2263 msg("<notice> Color %d is transparent", t);
2264 if (imgData->maskColors) {
2266 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2267 if (pix[i] < imgData->maskColors[2*i] ||
2268 pix[i] > imgData->maskColors[2*i+1]) {
2283 pal[t].r = (unsigned char)(colToByte(rgb.r));
2284 pal[t].g = (unsigned char)(colToByte(rgb.g));
2285 pal[t].b = (unsigned char)(colToByte(rgb.b));
2286 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2289 for (y = 0; y < height; ++y) {
2290 for (x = 0; x < width; ++x) {
2291 imgStr->getPixel(pixBuf);
2292 pic[width*y+x] = pal[pixBuf[0]];
2296 if(maskWidth < width && maskHeight < height) {
2297 for(y = 0; y < height; y++) {
2298 for (x = 0; x < width; x++) {
2299 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2303 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2304 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2305 double dx = width / maskWidth;
2306 double dy = height / maskHeight;
2308 for(y = 0; y < maskHeight; y++) {
2310 for (x = 0; x < maskWidth; x++) {
2311 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2312 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2320 height = maskHeight;
2323 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2327 if(maskbitmap) free(maskbitmap);
2332 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2333 int width, int height, GBool invert,
2336 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2337 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2338 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2341 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2342 int width, int height, GfxImageColorMap *colorMap,
2343 int *maskColors, GBool inlineImg)
2345 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2346 colorMap?"colorMap":"no colorMap",
2347 maskColors?"maskColors":"no maskColors",
2349 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2350 colorMap?"colorMap":"no colorMap",
2351 maskColors?"maskColors":"no maskColors",
2354 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2355 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2356 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2359 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2360 int width, int height,
2361 GfxImageColorMap *colorMap,
2362 Stream *maskStr, int maskWidth, int maskHeight,
2365 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2366 colorMap?"colorMap":"no colorMap",
2367 maskWidth, maskHeight);
2368 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2369 colorMap?"colorMap":"no colorMap",
2370 maskWidth, maskHeight);
2372 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2373 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2374 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2377 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2378 int width, int height,
2379 GfxImageColorMap *colorMap,
2381 int maskWidth, int maskHeight,
2382 GfxImageColorMap *maskColorMap)
2384 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2385 colorMap?"colorMap":"no colorMap",
2386 maskWidth, maskHeight);
2387 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2388 colorMap?"colorMap":"no colorMap",
2389 maskWidth, maskHeight);
2391 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2392 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2393 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2396 void GFXOutputDev::stroke(GfxState *state)
2400 GfxPath * path = state->getPath();
2401 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2402 strokeGfxline(state, line, 0);
2406 void GFXOutputDev::fill(GfxState *state)
2408 gfxcolor_t col = getFillColor(state);
2409 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2411 GfxPath * path = state->getPath();
2412 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2413 if(config_optimize_polygons) {
2414 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2418 fillGfxLine(state, line);
2422 void GFXOutputDev::eoFill(GfxState *state)
2424 gfxcolor_t col = getFillColor(state);
2425 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2427 GfxPath * path = state->getPath();
2428 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2429 fillGfxLine(state, line);
2434 static const char* dirseparator()
2443 void addGlobalFont(const char*filename)
2445 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2446 memset(f, 0, sizeof(fontfile_t));
2447 f->filename = filename;
2448 int len = strlen(filename);
2449 char*r1 = strrchr(filename, '/');
2450 char*r2 = strrchr(filename, '\\');
2458 msg("<verbose> Adding font \"%s\".", filename);
2459 if(global_fonts_next) {
2460 global_fonts_next->next = f;
2461 global_fonts_next = global_fonts_next->next;
2463 global_fonts_next = global_fonts = f;
2467 void addGlobalLanguageDir(const char*dir)
2469 msg("<notice> Adding %s to language pack directories", dir);
2472 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2473 strcpy(config_file, dir);
2474 strcat(config_file, dirseparator());
2475 strcat(config_file, "add-to-xpdfrc");
2477 fi = fopen(config_file, "rb");
2479 msg("<error> Could not open %s", config_file);
2482 globalParams->parseFile(new GString(config_file), fi);
2486 void addGlobalFontDir(const char*dirname)
2488 #ifdef HAVE_DIRENT_H
2489 DIR*dir = opendir(dirname);
2491 msg("<warning> Couldn't open directory %s", dirname);
2497 ent = readdir (dir);
2501 char*name = ent->d_name;
2507 if(!strncasecmp(&name[l-4], ".pfa", 4))
2509 if(!strncasecmp(&name[l-4], ".pfb", 4))
2511 if(!strncasecmp(&name[l-4], ".ttf", 4))
2514 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2515 strcpy(fontname, dirname);
2516 strcat(fontname, dirseparator());
2517 strcat(fontname, name);
2518 addGlobalFont(fontname);
2522 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2525 msg("<warning> No dirent.h");
2529 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2530 GfxColorSpace *blendingColorSpace,
2531 GBool isolated, GBool knockout,
2534 const char*colormodename = "";
2536 if(blendingColorSpace) {
2537 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2539 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);
2540 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);
2542 //states[statepos].createsoftmask |= forSoftMask;
2543 states[statepos].createsoftmask = forSoftMask;
2544 states[statepos].transparencygroup = !forSoftMask;
2545 states[statepos].isolated = isolated;
2547 states[statepos].olddevice = this->device;
2548 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2549 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2551 gfxdevice_record_init(this->device);
2553 /*if(!forSoftMask) { ////???
2554 state->setFillOpacity(0.0);
2559 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2562 gfxdevice_t*r = this->device;
2564 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2566 this->device = states[statepos].olddevice;
2568 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2569 /* if these errors occur more often, we should build a seperate
2570 transparency group stack, like xpdf/SplashOutputDev.cc does */
2571 restoreState(state);
2572 this->device = states[statepos].olddevice;
2574 states[statepos].olddevice = 0;
2576 gfxresult_t*recording = r->finish(r);
2578 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2579 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2581 if(states[statepos].createsoftmask) {
2582 states[statepos-1].softmaskrecording = recording;
2584 states[statepos-1].grouprecording = recording;
2587 states[statepos].createsoftmask = 0;
2588 states[statepos].transparencygroup = 0;
2592 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2594 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2595 "colordodge","colorburn","hardlight","softlight","difference",
2596 "exclusion","hue","saturation","color","luminosity"};
2598 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2599 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2601 if(state->getBlendMode() == gfxBlendNormal)
2602 infofeature("transparency groups");
2605 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2606 warnfeature(buffer, 0);
2609 gfxresult_t*grouprecording = states[statepos].grouprecording;
2611 int blendmode = state->getBlendMode();
2612 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2613 int alpha = (int)(state->getFillOpacity()*255);
2614 if(blendmode == gfxBlendMultiply && alpha>200)
2617 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2618 gfxdevice_ops_init(&ops, this->device, alpha);
2619 gfxresult_record_replay(grouprecording, &ops);
2622 grouprecording->destroy(grouprecording);
2624 states[statepos].grouprecording = 0;
2627 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2629 if(states[statepos].softmask) {
2630 /* shouldn't happen, but *does* happen */
2631 clearSoftMask(state);
2634 /* alpha = 1: retrieve mask values from alpha layer
2635 alpha = 0: retrieve mask values from luminance */
2637 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2638 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2639 msg("<verbose> 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]));
2642 infofeature("soft masks");
2644 warnfeature("soft masks from alpha channel",0);
2646 if(states[statepos].olddevice) {
2647 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2650 states[statepos].olddevice = this->device;
2651 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2652 gfxdevice_record_init(this->device);
2654 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2656 states[statepos].softmask = 1;
2657 states[statepos].softmask_alpha = alpha;
2660 static inline Guchar div255(int x) {
2661 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2664 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2666 if(c < min) c = min;
2667 if(c > max) c = max;
2671 void GFXOutputDev::clearSoftMask(GfxState *state)
2673 if(!states[statepos].softmask)
2675 states[statepos].softmask = 0;
2676 dbg("clearSoftMask statepos=%d", statepos);
2677 msg("<verbose> clearSoftMask statepos=%d", statepos);
2679 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2680 msg("<error> Error in softmask/tgroup ordering");
2684 gfxresult_t*mask = states[statepos].softmaskrecording;
2685 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2686 this->device = states[statepos].olddevice;
2688 /* get outline of all objects below the soft mask */
2689 gfxdevice_t uniondev;
2690 gfxdevice_union_init(&uniondev, 0);
2691 gfxresult_record_replay(below, &uniondev);
2692 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2693 uniondev.finish(&uniondev);
2694 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2695 gfxline_free(belowoutline);belowoutline=0;
2697 this->device->startclip(this->device, belowoutline);
2698 gfxresult_record_replay(below, this->device);
2699 gfxresult_record_replay(mask, this->device);
2700 this->device->endclip(this->device);
2703 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2704 if(width<=0 || height<=0)
2707 gfxdevice_t belowrender;
2708 gfxdevice_render_init(&belowrender);
2709 if(states[statepos+1].isolated) {
2710 belowrender.setparameter(&belowrender, "fillwhite", "1");
2712 belowrender.setparameter(&belowrender, "antialize", "2");
2713 belowrender.startpage(&belowrender, width, height);
2714 gfxresult_record_replay(below, &belowrender);
2715 belowrender.endpage(&belowrender);
2716 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2717 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2718 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2720 gfxdevice_t maskrender;
2721 gfxdevice_render_init(&maskrender);
2722 maskrender.startpage(&maskrender, width, height);
2723 gfxresult_record_replay(mask, &maskrender);
2724 maskrender.endpage(&maskrender);
2725 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2726 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2728 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2729 msg("<fatal> Internal error in mask drawing");
2734 for(y=0;y<height;y++) {
2735 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2736 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2737 for(x=0;x<width;x++) {
2739 if(states[statepos].softmask_alpha) {
2742 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2745 l2->a = div255(alpha*l2->a);
2747 /* DON'T premultiply alpha- this is done by fillbitmap,
2748 depending on the output device */
2749 //l2->r = div255(alpha*l2->r);
2750 //l2->g = div255(alpha*l2->g);
2751 //l2->b = div255(alpha*l2->b);
2757 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2760 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2761 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2763 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2765 mask->destroy(mask);
2766 below->destroy(below);
2767 maskresult->destroy(maskresult);
2768 belowresult->destroy(belowresult);
2769 states[statepos].softmaskrecording = 0;
2774 // public: ~MemCheck()
2776 // delete globalParams;globalParams=0;
2777 // Object::memCheck(stderr);
2778 // gMemReport(stderr);