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);
1331 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1334 static gfxline_t* mkEmptyGfxShape(double x, double y)
1336 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1337 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1341 static char isValidUnicode(int c)
1343 if(c>=32 && c<0x2fffe)
1348 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1349 double dx, double dy,
1350 double originX, double originY,
1351 CharCode charid, int nBytes, Unicode *_u, int uLen)
1353 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1354 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1358 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1360 int render = state->getRender();
1361 gfxcolor_t col = getFillColor(state);
1363 // check for invisible text -- this is used by Acrobat Capture
1364 if (render == RENDER_INVISIBLE) {
1366 if(!config_extrafontdata)
1370 GfxFont*font = state->getFont();
1372 if(font->getType() == fontType3) {
1373 /* type 3 chars are passed as graphics */
1374 msg("<debug> type3 char at %f/%f", x, y);
1378 Unicode u = uLen?(_u[0]):0;
1380 gfxmatrix_t m = this->current_font_matrix;
1381 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1382 //m.tx += originX; m.ty += originY;
1384 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d font=%08x",m.tx,m.ty,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
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);
1543 x1 += this->clipmovex;
1544 y1 += this->clipmovey;
1545 x2 += this->clipmovex;
1546 y2 += this->clipmovey;
1549 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1551 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);
1553 msg("<verbose> page is rotated %d degrees", rot);
1555 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1556 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1557 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1558 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1559 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1560 device->startclip(device, clippath); outer_clip_box = 1;
1561 if(!config_transparent) {
1562 device->fill(device, clippath, &white);
1564 states[statepos].clipbbox.xmin = x1;
1565 states[statepos].clipbbox.ymin = x1;
1566 states[statepos].clipbbox.xmax = x2;
1567 states[statepos].clipbbox.ymax = y2;
1569 this->dashPattern = 0;
1570 this->dashLength = 0;
1571 this->dashStart = 0;
1575 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1577 double x1, y1, x2, y2;
1578 gfxline_t points[5];
1581 msg("<debug> drawlink");
1583 link->getRect(&x1, &y1, &x2, &y2);
1584 cvtUserToDev(x1, y1, &x, &y);
1585 points[0].type = gfx_moveTo;
1586 points[0].x = points[4].x = x + user_movex + clipmovex;
1587 points[0].y = points[4].y = y + user_movey + clipmovey;
1588 points[0].next = &points[1];
1589 cvtUserToDev(x2, y1, &x, &y);
1590 points[1].type = gfx_lineTo;
1591 points[1].x = x + user_movex + clipmovex;
1592 points[1].y = y + user_movey + clipmovey;
1593 points[1].next = &points[2];
1594 cvtUserToDev(x2, y2, &x, &y);
1595 points[2].type = gfx_lineTo;
1596 points[2].x = x + user_movex + clipmovex;
1597 points[2].y = y + user_movey + clipmovey;
1598 points[2].next = &points[3];
1599 cvtUserToDev(x1, y2, &x, &y);
1600 points[3].type = gfx_lineTo;
1601 points[3].x = x + user_movex + clipmovex;
1602 points[3].y = y + user_movey + clipmovey;
1603 points[3].next = &points[4];
1604 cvtUserToDev(x1, y1, &x, &y);
1605 points[4].type = gfx_lineTo;
1606 points[4].x = x + user_movex + clipmovex;
1607 points[4].y = y + user_movey + clipmovey;
1610 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1611 points[0].x, points[0].y,
1612 points[1].x, points[1].y,
1613 points[2].x, points[2].y,
1614 points[3].x, points[3].y);
1616 if(getLogLevel() >= LOGLEVEL_TRACE) {
1617 dump_outline(points);
1620 LinkAction*action=link->getAction();
1623 const char*type = "-?-";
1626 msg("<trace> drawlink action=%d", action->getKind());
1627 switch(action->getKind())
1631 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1632 LinkDest *dest=NULL;
1633 if (ha->getDest()==NULL)
1634 dest=catalog->findDest(ha->getNamedDest());
1636 dest=ha->getDest()->copy();
1638 if (dest->isPageRef()){
1639 Ref pageref=dest->getPageRef();
1640 page=catalog->findPage(pageref.num,pageref.gen);
1642 else page=dest->getPageNum();
1643 sprintf(buf, "%d", page);
1651 LinkGoToR*l = (LinkGoToR*)action;
1652 GString*g = l->getFileName();
1654 s = strdup(g->getCString());
1656 /* if the GoToR link has no filename, then
1657 try to find a refernce in the *local*
1659 GString*g = l->getNamedDest();
1661 s = strdup(g->getCString());
1667 LinkNamed*l = (LinkNamed*)action;
1668 GString*name = l->getName();
1670 s = strdup(name->lowerCase()->getCString());
1671 named = name->getCString();
1674 if(strstr(s, "next") || strstr(s, "forward"))
1676 page = currentpage + 1;
1678 else if(strstr(s, "prev") || strstr(s, "back"))
1680 page = currentpage - 1;
1682 else if(strstr(s, "last") || strstr(s, "end"))
1684 if(this->page2page && this->num_pages) {
1685 page = this->page2page[this->num_pages-1];
1688 else if(strstr(s, "first") || strstr(s, "top"))
1696 case actionLaunch: {
1698 LinkLaunch*l = (LinkLaunch*)action;
1699 GString * str = new GString(l->getFileName());
1700 GString * params = l->getParams();
1702 str->append(params);
1703 s = strdup(str->getCString());
1710 LinkURI*l = (LinkURI*)action;
1711 GString*g = l->getURI();
1713 url = g->getCString();
1718 case actionUnknown: {
1720 LinkUnknown*l = (LinkUnknown*)action;
1725 msg("<error> Unknown link type!");
1730 if(!s) s = strdup("-?-");
1732 msg("<trace> drawlink s=%s", s);
1734 if(!gfxglobals->linkinfo && (page || s))
1736 msg("<notice> File contains links");
1737 gfxglobals->linkinfo = 1;
1743 for(t=1;t<=this->num_pages;t++) {
1744 if(this->page2page[t]==page) {
1754 sprintf(buf, "page%d", lpage);
1755 device->drawlink(device, points, buf);
1759 device->drawlink(device, points, s);
1762 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1766 void GFXOutputDev::saveState(GfxState *state) {
1767 dbg("saveState %08x", state); dbgindent+=2;
1769 msg("<trace> saveState %08x", state);
1772 msg("<fatal> Too many nested states in pdf.");
1776 states[statepos].state = state;
1777 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1778 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1779 states[statepos].clipping = 0;
1780 states[statepos].olddevice = 0;
1781 states[statepos].clipbbox = states[statepos-1].clipbbox;
1784 void GFXOutputDev::restoreState(GfxState *state) {
1785 dbgindent-=2; dbg("restoreState %08x", state);
1788 msg("<fatal> Invalid restoreState");
1791 msg("<trace> restoreState %08x%s%s", state,
1792 states[statepos].softmask?" (end softmask)":"",
1793 states[statepos].clipping?" (end clipping)":"");
1794 if(states[statepos].softmask) {
1795 clearSoftMask(state);
1799 while(states[statepos].clipping) {
1800 device->endclip(device);
1801 states[statepos].clipping--;
1803 if(states[statepos].state!=state) {
1804 msg("<fatal> bad state nesting");
1807 states[statepos].state=0;
1811 void GFXOutputDev::updateLineDash(GfxState *state)
1813 if(this->dashPattern) {
1814 free(this->dashPattern);this->dashPattern = 0;
1816 double *pattern = 0;
1817 state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1818 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1819 if(!this->dashLength) {
1820 this->dashPattern = 0;
1822 double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1823 memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1824 this->dashPattern = p;
1828 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1830 this->page2page = page2page;
1831 this->num_pages = num_pages;
1834 void GFXOutputDev::updateLineWidth(GfxState *state)
1836 double width = state->getTransformedLineWidth();
1839 void GFXOutputDev::updateLineCap(GfxState *state)
1841 int c = state->getLineCap();
1844 void GFXOutputDev::updateLineJoin(GfxState *state)
1846 int j = state->getLineJoin();
1849 void GFXOutputDev::updateFillColor(GfxState *state)
1852 double opaq = state->getFillOpacity();
1853 state->getFillRGB(&rgb);
1855 void GFXOutputDev::updateFillOpacity(GfxState *state)
1858 double opaq = state->getFillOpacity();
1859 state->getFillRGB(&rgb);
1860 dbg("update fillopaq %f", opaq);
1862 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1864 double opaq = state->getFillOpacity();
1865 dbg("update strokeopaq %f", opaq);
1867 void GFXOutputDev::updateFillOverprint(GfxState *state)
1869 double opaq = state->getFillOverprint();
1870 dbg("update filloverprint %f", opaq);
1872 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1874 double opaq = state->getStrokeOverprint();
1875 dbg("update strokeoverprint %f", opaq);
1877 void GFXOutputDev::updateTransfer(GfxState *state)
1879 dbg("update transfer");
1883 void GFXOutputDev::updateStrokeColor(GfxState *state)
1886 double opaq = state->getStrokeOpacity();
1887 state->getStrokeRGB(&rgb);
1890 void GFXOutputDev::updateFont(GfxState *state)
1892 GfxFont* gfxFont = state->getFont();
1896 char*id = getFontID(gfxFont);
1897 msg("<verbose> Updating font to %s", id);
1898 if(gfxFont->getType() == fontType3) {
1899 infofeature("Type3 fonts");
1900 if(!config_extrafontdata) {
1905 msg("<error> Internal Error: FontID is null");
1909 this->current_fontinfo = this->info->getFont(id);
1911 if(!this->current_fontinfo) {
1912 msg("<error> Internal Error: no fontinfo for font %s", id);
1915 if(!this->current_fontinfo->seen) {
1916 dumpFontInfo("<verbose>", gfxFont);
1919 current_gfxfont = this->current_fontinfo->getGfxFont();
1920 device->addfont(device, current_gfxfont);
1923 updateFontMatrix(state);
1926 #define SQR(x) ((x)*(x))
1928 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1930 if((newwidth<1 || newheight<1) ||
1931 (width<=newwidth || height<=newheight))
1933 unsigned char*newdata;
1935 newdata= (unsigned char*)malloc(newwidth*newheight);
1936 double fx = ((double)width)/newwidth;
1937 double fy = ((double)height)/newheight;
1939 int blocksize = (int)(8192/(fx*fy));
1940 int r = 8192*256/palettesize;
1941 for(x=0;x<newwidth;x++) {
1942 double ex = px + fx;
1943 int fromx = (int)px;
1945 int xweight1 = (int)((1-(px-fromx))*256);
1946 int xweight2 = (int)((ex-tox)*256);
1948 for(y=0;y<newheight;y++) {
1949 double ey = py + fy;
1950 int fromy = (int)py;
1952 int yweight1 = (int)((1-(py-fromy))*256);
1953 int yweight2 = (int)((ey-toy)*256);
1960 for(xx=fromx;xx<=tox;xx++)
1961 for(yy=fromy;yy<=toy;yy++) {
1962 int b = 1-data[width*yy+xx];
1964 if(xx==fromx) weight = (weight*xweight1)/256;
1965 if(xx==tox) weight = (weight*xweight2)/256;
1966 if(yy==fromy) weight = (weight*yweight1)/256;
1967 if(yy==toy) weight = (weight*yweight2)/256;
1970 //if(a) a=(palettesize-1)*r/blocksize;
1971 newdata[y*newwidth+x] = (a*blocksize)/r;
1979 #define IMAGE_TYPE_JPEG 0
1980 #define IMAGE_TYPE_LOSSLESS 1
1982 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1983 double x1,double y1,
1984 double x2,double y2,
1985 double x3,double y3,
1986 double x4,double y4, int type, int multiply)
1988 gfxcolor_t*newpic=0;
1990 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1991 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1993 gfxline_t p1,p2,p3,p4,p5;
1994 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1995 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1996 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1997 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1998 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2000 {p1.x = (int)(p1.x*20)/20.0;
2001 p1.y = (int)(p1.y*20)/20.0;
2002 p2.x = (int)(p2.x*20)/20.0;
2003 p2.y = (int)(p2.y*20)/20.0;
2004 p3.x = (int)(p3.x*20)/20.0;
2005 p3.y = (int)(p3.y*20)/20.0;
2006 p4.x = (int)(p4.x*20)/20.0;
2007 p4.y = (int)(p4.y*20)/20.0;
2008 p5.x = (int)(p5.x*20)/20.0;
2009 p5.y = (int)(p5.y*20)/20.0;
2013 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2014 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2016 m.tx = p1.x - 0.5*multiply;
2017 m.ty = p1.y - 0.5*multiply;
2020 img.data = (gfxcolor_t*)data;
2024 if(type == IMAGE_TYPE_JPEG)
2025 /* TODO: pass image_dpi to device instead */
2026 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2029 dev->fillbitmap(dev, &p1, &img, &m, 0);
2032 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2033 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2035 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2038 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2039 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2041 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2045 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2046 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2047 GBool inlineImg, int mask, int*maskColors,
2048 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2050 /* the code in this function is *old*. It's not pretty, but it works. */
2052 double x1,y1,x2,y2,x3,y3,x4,y4;
2053 ImageStream *imgStr;
2058 unsigned char* maskbitmap = 0;
2061 ncomps = colorMap->getNumPixelComps();
2062 bits = colorMap->getBits();
2067 unsigned char buf[8];
2068 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2070 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2071 imgMaskStr->reset();
2072 unsigned char pal[256];
2073 int n = 1 << colorMap->getBits();
2078 maskColorMap->getGray(pixBuf, &gray);
2079 pal[t] = colToByte(gray);
2081 for (y = 0; y < maskHeight; y++) {
2082 for (x = 0; x < maskWidth; x++) {
2083 imgMaskStr->getPixel(buf);
2084 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2089 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2090 imgMaskStr->reset();
2091 for (y = 0; y < maskHeight; y++) {
2092 for (x = 0; x < maskWidth; x++) {
2093 imgMaskStr->getPixel(buf);
2095 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2103 imgStr = new ImageStream(str, width, ncomps,bits);
2106 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2108 msg("<verbose> Ignoring %d by %d image", width, height);
2109 unsigned char buf[8];
2111 for (y = 0; y < height; ++y)
2112 for (x = 0; x < width; ++x) {
2113 imgStr->getPixel(buf);
2121 this->transformXY(state, 0, 1, &x1, &y1);
2122 this->transformXY(state, 0, 0, &x2, &y2);
2123 this->transformXY(state, 1, 0, &x3, &y3);
2124 this->transformXY(state, 1, 1, &x4, &y4);
2127 /* as type 3 bitmaps are antialized, we need to place them
2128 at integer coordinates, otherwise flash player's antializing
2129 will kick in and make everything blurry */
2130 x1 = (int)(x1);y1 = (int)(y1);
2131 x2 = (int)(x2);y2 = (int)(y2);
2132 x3 = (int)(x3);y3 = (int)(y3);
2133 x4 = (int)(x4);y4 = (int)(y4);
2136 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2138 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2139 gfxglobals->pbminfo = 1;
2142 msg("<verbose> drawing %d by %d masked picture", width, height);
2144 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2145 msg("<notice> File contains jpeg pictures");
2146 gfxglobals->jpeginfo = 1;
2150 unsigned char buf[8];
2152 unsigned char*pic = new unsigned char[width*height];
2153 gfxcolor_t pal[256];
2155 state->getFillRGB(&rgb);
2157 memset(pal,255,sizeof(pal));
2158 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2159 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2160 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2161 pal[0].a = 255; pal[1].a = 0;
2164 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2165 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2166 for (y = 0; y < height; ++y)
2167 for (x = 0; x < width; ++x)
2169 imgStr->getPixel(buf);
2172 pic[width*y+x] = buf[0];
2176 unsigned char*pic2 = 0;
2179 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2188 height = realheight;
2192 /* make a black/white palette */
2194 float r = 255./(float)(numpalette-1);
2196 for(t=0;t<numpalette;t++) {
2197 pal[t].r = colToByte(rgb.r);
2198 pal[t].g = colToByte(rgb.g);
2199 pal[t].b = colToByte(rgb.b);
2200 pal[t].a = (unsigned char)(t*r);
2205 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2206 for (y = 0; y < height; ++y) {
2207 for (x = 0; x < width; ++x) {
2208 pic2[width*y+x] = pal[pic[y*width+x]];
2211 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2215 if(maskbitmap) free(maskbitmap);
2221 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2222 gfxcolor_t*pic=new gfxcolor_t[width*height];
2223 for (y = 0; y < height; ++y) {
2224 for (x = 0; x < width; ++x) {
2225 imgStr->getPixel(pixBuf);
2226 colorMap->getRGB(pixBuf, &rgb);
2227 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2228 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2229 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2230 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2232 int x1 = x*maskWidth/width;
2233 int y1 = y*maskHeight/height;
2234 int x2 = (x+1)*maskWidth/width;
2235 int y2 = (y+1)*maskHeight/height;
2237 unsigned int alpha=0;
2238 unsigned int count=0;
2239 for(xx=x1;xx<x2;xx++)
2240 for(yy=y1;yy<y2;yy++) {
2241 alpha += maskbitmap[yy*maskWidth+xx];
2245 pic[width*y+x].a = alpha / count;
2247 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2252 if(str->getKind()==strDCT)
2253 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2255 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2258 if(maskbitmap) free(maskbitmap);
2261 gfxcolor_t*pic=new gfxcolor_t[width*height];
2262 gfxcolor_t pal[256];
2263 int n = 1 << colorMap->getBits();
2265 for(t=0;t<256;t++) {
2267 colorMap->getRGB(pixBuf, &rgb);
2269 {/*if(maskColors && *maskColors==t) {
2270 msg("<notice> Color %d is transparent", t);
2271 if (imgData->maskColors) {
2273 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2274 if (pix[i] < imgData->maskColors[2*i] ||
2275 pix[i] > imgData->maskColors[2*i+1]) {
2290 pal[t].r = (unsigned char)(colToByte(rgb.r));
2291 pal[t].g = (unsigned char)(colToByte(rgb.g));
2292 pal[t].b = (unsigned char)(colToByte(rgb.b));
2293 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2296 for (y = 0; y < height; ++y) {
2297 for (x = 0; x < width; ++x) {
2298 imgStr->getPixel(pixBuf);
2299 pic[width*y+x] = pal[pixBuf[0]];
2303 if(maskWidth < width && maskHeight < height) {
2304 for(y = 0; y < height; y++) {
2305 for (x = 0; x < width; x++) {
2306 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2310 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2311 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2312 double dx = width / maskWidth;
2313 double dy = height / maskHeight;
2315 for(y = 0; y < maskHeight; y++) {
2317 for (x = 0; x < maskWidth; x++) {
2318 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2319 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2327 height = maskHeight;
2330 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2334 if(maskbitmap) free(maskbitmap);
2339 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2340 int width, int height, GBool invert,
2343 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2344 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2345 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2348 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2349 int width, int height, GfxImageColorMap *colorMap,
2350 int *maskColors, GBool inlineImg)
2352 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2353 colorMap?"colorMap":"no colorMap",
2354 maskColors?"maskColors":"no maskColors",
2356 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2357 colorMap?"colorMap":"no colorMap",
2358 maskColors?"maskColors":"no maskColors",
2361 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2362 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2363 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2366 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2367 int width, int height,
2368 GfxImageColorMap *colorMap,
2369 Stream *maskStr, int maskWidth, int maskHeight,
2372 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2373 colorMap?"colorMap":"no colorMap",
2374 maskWidth, maskHeight);
2375 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2376 colorMap?"colorMap":"no colorMap",
2377 maskWidth, maskHeight);
2379 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2380 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2381 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2384 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2385 int width, int height,
2386 GfxImageColorMap *colorMap,
2388 int maskWidth, int maskHeight,
2389 GfxImageColorMap *maskColorMap)
2391 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2392 colorMap?"colorMap":"no colorMap",
2393 maskWidth, maskHeight);
2394 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2395 colorMap?"colorMap":"no colorMap",
2396 maskWidth, maskHeight);
2398 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2399 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2400 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2403 void GFXOutputDev::stroke(GfxState *state)
2407 GfxPath * path = state->getPath();
2408 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2409 strokeGfxline(state, line, 0);
2413 void GFXOutputDev::fill(GfxState *state)
2415 gfxcolor_t col = getFillColor(state);
2416 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2418 GfxPath * path = state->getPath();
2419 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2420 if(config_optimize_polygons) {
2421 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2425 fillGfxLine(state, line);
2429 void GFXOutputDev::eoFill(GfxState *state)
2431 gfxcolor_t col = getFillColor(state);
2432 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2434 GfxPath * path = state->getPath();
2435 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2436 fillGfxLine(state, line);
2441 static const char* dirseparator()
2450 void addGlobalFont(const char*filename)
2452 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2453 memset(f, 0, sizeof(fontfile_t));
2454 f->filename = filename;
2455 int len = strlen(filename);
2456 char*r1 = strrchr(filename, '/');
2457 char*r2 = strrchr(filename, '\\');
2465 msg("<verbose> Adding font \"%s\".", filename);
2466 if(global_fonts_next) {
2467 global_fonts_next->next = f;
2468 global_fonts_next = global_fonts_next->next;
2470 global_fonts_next = global_fonts = f;
2474 void addGlobalLanguageDir(const char*dir)
2476 msg("<notice> Adding %s to language pack directories", dir);
2479 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2480 strcpy(config_file, dir);
2481 strcat(config_file, dirseparator());
2482 strcat(config_file, "add-to-xpdfrc");
2484 fi = fopen(config_file, "rb");
2486 msg("<error> Could not open %s", config_file);
2489 globalParams->parseFile(new GString(config_file), fi);
2493 void addGlobalFontDir(const char*dirname)
2495 #ifdef HAVE_DIRENT_H
2496 DIR*dir = opendir(dirname);
2498 msg("<warning> Couldn't open directory %s", dirname);
2504 ent = readdir (dir);
2508 char*name = ent->d_name;
2514 if(!strncasecmp(&name[l-4], ".pfa", 4))
2516 if(!strncasecmp(&name[l-4], ".pfb", 4))
2518 if(!strncasecmp(&name[l-4], ".ttf", 4))
2521 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2522 strcpy(fontname, dirname);
2523 strcat(fontname, dirseparator());
2524 strcat(fontname, name);
2525 addGlobalFont(fontname);
2529 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2532 msg("<warning> No dirent.h");
2536 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2537 GfxColorSpace *blendingColorSpace,
2538 GBool isolated, GBool knockout,
2541 const char*colormodename = "";
2543 if(blendingColorSpace) {
2544 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2546 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);
2547 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);
2549 //states[statepos].createsoftmask |= forSoftMask;
2550 states[statepos].createsoftmask = forSoftMask;
2551 states[statepos].transparencygroup = !forSoftMask;
2552 states[statepos].isolated = isolated;
2554 states[statepos].olddevice = this->device;
2555 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2556 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2558 gfxdevice_record_init(this->device);
2560 /*if(!forSoftMask) { ////???
2561 state->setFillOpacity(0.0);
2566 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2569 gfxdevice_t*r = this->device;
2571 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2573 this->device = states[statepos].olddevice;
2575 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2576 /* if these errors occur more often, we should build a seperate
2577 transparency group stack, like xpdf/SplashOutputDev.cc does */
2578 restoreState(state);
2579 this->device = states[statepos].olddevice;
2581 states[statepos].olddevice = 0;
2583 gfxresult_t*recording = r->finish(r);
2585 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2586 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2588 if(states[statepos].createsoftmask) {
2589 states[statepos-1].softmaskrecording = recording;
2591 states[statepos-1].grouprecording = recording;
2594 states[statepos].createsoftmask = 0;
2595 states[statepos].transparencygroup = 0;
2599 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2601 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2602 "colordodge","colorburn","hardlight","softlight","difference",
2603 "exclusion","hue","saturation","color","luminosity"};
2605 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2606 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2608 if(state->getBlendMode() == gfxBlendNormal)
2609 infofeature("transparency groups");
2612 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2613 warnfeature(buffer, 0);
2616 gfxresult_t*grouprecording = states[statepos].grouprecording;
2618 int blendmode = state->getBlendMode();
2619 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2620 int alpha = (int)(state->getFillOpacity()*255);
2621 if(blendmode == gfxBlendMultiply && alpha>200)
2624 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2625 gfxdevice_ops_init(&ops, this->device, alpha);
2626 gfxresult_record_replay(grouprecording, &ops);
2629 grouprecording->destroy(grouprecording);
2631 states[statepos].grouprecording = 0;
2634 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2636 if(states[statepos].softmask) {
2637 /* shouldn't happen, but *does* happen */
2638 clearSoftMask(state);
2641 /* alpha = 1: retrieve mask values from alpha layer
2642 alpha = 0: retrieve mask values from luminance */
2644 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2645 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2646 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2647 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2649 infofeature("soft masks");
2651 warnfeature("soft masks from alpha channel",0);
2653 if(states[statepos].olddevice) {
2654 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2657 states[statepos].olddevice = this->device;
2658 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2659 gfxdevice_record_init(this->device);
2661 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2663 states[statepos].softmask = 1;
2664 states[statepos].softmask_alpha = alpha;
2667 static inline Guchar div255(int x) {
2668 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2671 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2673 if(c < min) c = min;
2674 if(c > max) c = max;
2678 void GFXOutputDev::clearSoftMask(GfxState *state)
2680 if(!states[statepos].softmask)
2682 states[statepos].softmask = 0;
2683 dbg("clearSoftMask statepos=%d", statepos);
2684 msg("<verbose> clearSoftMask statepos=%d", statepos);
2686 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2687 msg("<error> Error in softmask/tgroup ordering");
2691 gfxresult_t*mask = states[statepos].softmaskrecording;
2692 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2693 this->device = states[statepos].olddevice;
2695 /* get outline of all objects below the soft mask */
2696 gfxdevice_t uniondev;
2697 gfxdevice_union_init(&uniondev, 0);
2698 gfxresult_record_replay(below, &uniondev);
2699 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2700 uniondev.finish(&uniondev);
2701 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2702 gfxline_free(belowoutline);belowoutline=0;
2704 this->device->startclip(this->device, belowoutline);
2705 gfxresult_record_replay(below, this->device);
2706 gfxresult_record_replay(mask, this->device);
2707 this->device->endclip(this->device);
2710 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2711 if(width<=0 || height<=0)
2714 gfxdevice_t belowrender;
2715 gfxdevice_render_init(&belowrender);
2716 if(states[statepos+1].isolated) {
2717 belowrender.setparameter(&belowrender, "fillwhite", "1");
2719 belowrender.setparameter(&belowrender, "antialize", "2");
2720 belowrender.startpage(&belowrender, width, height);
2721 gfxresult_record_replay(below, &belowrender);
2722 belowrender.endpage(&belowrender);
2723 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2724 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2725 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2727 gfxdevice_t maskrender;
2728 gfxdevice_render_init(&maskrender);
2729 maskrender.startpage(&maskrender, width, height);
2730 gfxresult_record_replay(mask, &maskrender);
2731 maskrender.endpage(&maskrender);
2732 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2733 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2735 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2736 msg("<fatal> Internal error in mask drawing");
2741 for(y=0;y<height;y++) {
2742 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2743 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2744 for(x=0;x<width;x++) {
2746 if(states[statepos].softmask_alpha) {
2749 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2752 l2->a = div255(alpha*l2->a);
2754 /* DON'T premultiply alpha- this is done by fillbitmap,
2755 depending on the output device */
2756 //l2->r = div255(alpha*l2->r);
2757 //l2->g = div255(alpha*l2->g);
2758 //l2->b = div255(alpha*l2->b);
2764 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2767 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2768 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2770 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2772 mask->destroy(mask);
2773 below->destroy(below);
2774 maskresult->destroy(maskresult);
2775 belowresult->destroy(belowresult);
2776 states[statepos].softmaskrecording = 0;
2781 // public: ~MemCheck()
2783 // delete globalParams;globalParams=0;
2784 // Object::memCheck(stderr);
2785 // gMemReport(stderr);