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;
607 memset(states, 0, sizeof(states));
610 void GFXOutputDev::setParameter(const char*key, const char*value)
612 if(!strcmp(key,"breakonwarning")) {
613 this->config_break_on_warning = atoi(value);
614 } else if(!strcmp(key,"remapunicode")) {
615 this->config_remapunicode = atoi(value);
616 } else if(!strcmp(key,"transparent")) {
617 this->config_transparent = atoi(value);
618 } else if(!strcmp(key,"extrafontdata")) {
619 this->config_extrafontdata = atoi(value);
620 } else if(!strcmp(key,"convertgradients")) {
621 this->config_convertgradients = atoi(value);
622 } else if(!strcmp(key,"multiply")) {
623 this->config_multiply = atoi(value);
624 if(this->config_multiply<1)
625 this->config_multiply=1;
626 } else if(!strcmp(key,"optimize_polygons")) {
627 this->config_optimize_polygons = atoi(value);
631 void GFXOutputDev::setDevice(gfxdevice_t*dev)
636 void GFXOutputDev::setMove(int x,int y)
638 this->user_movex = x;
639 this->user_movey = y;
642 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
644 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
645 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
647 this->user_clipx1 = x1;
648 this->user_clipy1 = y1;
649 this->user_clipx2 = x2;
650 this->user_clipy2 = y2;
653 static char*getFontName(GfxFont*font)
656 GString*gstr = font->getName();
657 char* fname = gstr==0?0:gstr->getCString();
661 sprintf(buf, "UFONT%d", r->num);
662 fontid = strdup(buf);
664 fontid = strdup(fname);
668 char* plus = strchr(fontid, '+');
669 if(plus && plus < &fontid[strlen(fontid)-1]) {
670 fontname = strdup(plus+1);
672 fontname = strdup(fontid);
678 static void dumpFontInfo(const char*loglevel, GfxFont*font);
679 static int lastdumps[1024];
680 static int lastdumppos = 0;
685 static void showFontError(GfxFont*font, int nr)
689 for(t=0;t<lastdumppos;t++)
690 if(lastdumps[t] == r->num)
694 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
695 lastdumps[lastdumppos++] = r->num;
697 msg("<warning> The following font caused problems:");
699 msg("<warning> The following font caused problems (substituting):");
701 msg("<warning> The following Type 3 Font will be rendered as graphics:");
702 dumpFontInfo("<warning>", font);
705 static void dumpFontInfo(const char*loglevel, GfxFont*font)
707 char* id = getFontID(font);
708 char* name = getFontName(font);
709 Ref* r=font->getID();
710 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
712 GString*gstr = font->getTag();
714 msg("%s| Tag: %s", loglevel, id);
716 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
718 GfxFontType type=font->getType();
720 case fontUnknownType:
721 msg("%s| Type: unknown",loglevel);
724 msg("%s| Type: 1",loglevel);
727 msg("%s| Type: 1C",loglevel);
730 msg("%s| Type: 3",loglevel);
733 msg("%s| Type: TrueType",loglevel);
736 msg("%s| Type: CIDType0",loglevel);
739 msg("%s| Type: CIDType0C",loglevel);
742 msg("%s| Type: CIDType2",loglevel);
747 GBool embedded = font->getEmbeddedFontID(&embRef);
749 if(font->getEmbeddedFontName()) {
750 embeddedName = font->getEmbeddedFontName()->getCString();
753 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
755 gstr = font->getExtFontFile();
757 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
759 // Get font descriptor flags.
760 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
761 if(font->isSerif()) msg("%s| is serif", loglevel);
762 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
763 if(font->isItalic()) msg("%s| is italic", loglevel);
764 if(font->isBold()) msg("%s| is bold", loglevel);
770 //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");}
771 //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");}
773 void dump_outline(gfxline_t*line)
775 /*gfxbbox_t*r = gfxline_isrectangle(line);
777 printf("is not a rectangle\n");
779 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
783 if(line->type == gfx_moveTo) {
784 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
785 } else if(line->type == gfx_lineTo) {
786 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
787 } else if(line->type == gfx_splineTo) {
788 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
794 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
796 int num = path->getNumSubpaths();
799 double lastx=0,lasty=0,posx=0,posy=0;
802 msg("<warning> empty path");
806 gfxdrawer_target_gfxline(&draw);
808 for(t = 0; t < num; t++) {
809 GfxSubpath *subpath = path->getSubpath(t);
810 int subnum = subpath->getNumPoints();
811 double bx=0,by=0,cx=0,cy=0;
813 for(s=0;s<subnum;s++) {
816 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
819 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
820 draw.lineTo(&draw, lastx, lasty);
822 draw.moveTo(&draw, x,y);
827 } else if(subpath->getCurve(s) && cpos==0) {
831 } else if(subpath->getCurve(s) && cpos==1) {
839 draw.lineTo(&draw, x,y);
841 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
848 /* fix non-closed lines */
849 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
850 draw.lineTo(&draw, lastx, lasty);
852 gfxline_t*result = (gfxline_t*)draw.result(&draw);
854 gfxline_optimize(result);
859 GBool GFXOutputDev::useTilingPatternFill()
861 infofeature("tiled patterns");
862 // if(config_convertgradients)
866 GBool GFXOutputDev::useShadedFills()
868 infofeature("shaded fills");
869 if(config_convertgradients)
874 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
876 state->transform(x,y,nx,ny);
877 *nx += user_movex + clipmovex;
878 *ny += user_movey + clipmovey;
882 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
883 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
884 int paintType, Dict *resDict,
885 double *mat, double *bbox,
886 int x0, int y0, int x1, int y1,
887 double xStep, double yStep)
889 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
890 int paintType, Dict *resDict,
891 double *mat, double *bbox,
892 int x0, int y0, int x1, int y1,
893 double xStep, double yStep)
896 msg("<debug> tilingPatternFill");
897 infofeature("tiling pattern fills");
900 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
902 msg("<error> functionShadedFill not supported yet");
903 infofeature("function shaded fills");
906 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
910 colspace->getRGB(col, &rgb);
911 c.r = colToByte(rgb.r);
912 c.g = colToByte(rgb.g);
913 c.b = colToByte(rgb.b);
918 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
920 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
921 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
924 this->transformXY(state, x0,y0, &x0,&y0);
925 this->transformXY(state, x1,y1, &x1,&y1);
926 this->transformXY(state, x2,y2, &x2,&y2);
931 shading->getColor(0.0, &color0);
932 shading->getColor(0.5, &color1);
933 shading->getColor(1.0, &color2);
935 GfxColorSpace* colspace = shading->getColorSpace();
937 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
938 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
939 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
940 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
941 infofeature("radial shaded fills");
943 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
947 g[0].color = col2col(colspace, &color0);
948 g[1].color = col2col(colspace, &color1);
949 g[2].color = col2col(colspace, &color2);
954 gfxbbox_t b = states[statepos].clipbbox;
955 gfxline_t p1,p2,p3,p4,p5;
956 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
957 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
958 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
959 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
960 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
963 //m.m00 = (x3-x0); m.m10 = (x1-x0);
964 //m.m01 = (y3-y0); m.m11 = (y1-y0);
965 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
966 m.m00 = (x1-x0); m.m10 = (x2-x0);
967 m.m01 = (y1-y0); m.m11 = (y2-y0);
971 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
975 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
978 shading->getCoords(&x0,&y0,&x1,&y1);
979 this->transformXY(state, x0,y0,&x0,&y0);
980 this->transformXY(state, x1,y1,&x1,&y1);
985 shading->getColor(0.0, &color0);
986 shading->getColor(0.5, &color1);
987 shading->getColor(1.0, &color2);
989 GfxColorSpace* colspace = shading->getColorSpace();
991 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
992 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
993 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
994 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
996 infofeature("axial shaded fills");
998 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1002 g[0].color = col2col(colspace, &color0);
1003 g[1].color = col2col(colspace, &color1);
1004 g[2].color = col2col(colspace, &color2);
1009 double xMin,yMin,xMax,yMax;
1010 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1011 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1012 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1015 xMin = 1024; yMin = 1024;
1017 gfxbbox_t b = states[statepos].clipbbox;
1018 gfxline_t p1,p2,p3,p4,p5;
1019 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1020 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1021 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1022 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1023 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1025 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1026 the middle of the two control points */
1028 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1029 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1030 m.tx = (x0 + x1)/2 - 0.5;
1031 m.ty = (y0 + y1)/2 - 0.5;
1033 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1039 GBool GFXOutputDev::useDrawForm()
1041 infofeature("forms");
1044 void GFXOutputDev::drawForm(Ref id)
1046 msg("<error> drawForm not implemented");
1048 GBool GFXOutputDev::needNonText()
1052 void GFXOutputDev::endPage()
1054 msg("<verbose> endPage (GfxOutputDev)");
1055 if(outer_clip_box) {
1056 device->endclip(device);
1059 /* notice: we're not fully done yet with this page- there might still be
1060 a few calls to drawLink() yet to come */
1063 static inline double sqr(double x) {return x*x;}
1065 #define STROKE_FILL 1
1066 #define STROKE_CLIP 2
1067 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1069 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1070 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1071 double miterLimit = state->getMiterLimit();
1072 double width = state->getTransformedLineWidth();
1075 double opaq = state->getStrokeOpacity();
1077 state->getFillRGB(&rgb);
1079 state->getStrokeRGB(&rgb);
1081 col.r = colToByte(rgb.r);
1082 col.g = colToByte(rgb.g);
1083 col.b = colToByte(rgb.b);
1084 col.a = (unsigned char)(opaq*255);
1086 gfx_capType capType = gfx_capRound;
1087 if(lineCap == 0) capType = gfx_capButt;
1088 else if(lineCap == 1) capType = gfx_capRound;
1089 else if(lineCap == 2) capType = gfx_capSquare;
1090 else msg("<error> Invalid line cap type");
1092 gfx_joinType joinType = gfx_joinRound;
1093 if(lineJoin == 0) joinType = gfx_joinMiter;
1094 else if(lineJoin == 1) joinType = gfx_joinRound;
1095 else if(lineJoin == 2) joinType = gfx_joinBevel;
1096 else msg("<error> Invalid line join type");
1098 gfxline_t*line2 = 0;
1100 int dashLength = states[statepos].dashLength;
1101 double*dashPattern = states[statepos].dashPattern;
1102 double dashStart = states[statepos].dashStart;
1103 if(dashLength && dashPattern) {
1104 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1107 /* try to find out how much the transformation matrix would
1108 stretch the dashes, and factor that into the dash lengths.
1109 This is not the entirely correct approach- it would be
1110 better to first convert the path to an unscaled version,
1111 then apply dashing, and then transform the path using
1112 the current transformation matrix. However there are few
1113 PDFs which actually stretch a dashed path in a non-orthonormal
1115 double tx1, ty1, tx2, ty2, tx3, ty3;
1116 this->transformXY(state, 0, 0, &tx1, &ty1);
1117 this->transformXY(state, 0, 1, &tx2, &ty2);
1118 this->transformXY(state, 1, 0, &tx3, &ty3);
1119 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1120 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1122 warnfeature("non-ortogonally dashed strokes", 0);
1123 double f = (d1+d2)/2;
1125 msg("<trace> %d dashes", dashLength);
1126 msg("<trace> | phase: %f", dashStart);
1127 for(t=0;t<dashLength;t++) {
1128 dash[t] = (float)dashPattern[t] * f;
1132 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1134 dash[dashLength] = -1;
1135 if(getLogLevel() >= LOGLEVEL_TRACE) {
1139 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1143 msg("<trace> After dashing:");
1146 if(getLogLevel() >= LOGLEVEL_TRACE) {
1147 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1149 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1150 lineCap==0?"butt": (lineCap==1?"round":"square"),
1152 col.r,col.g,col.b,col.a
1157 if(flags&STROKE_FILL) {
1158 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1159 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1160 if(getLogLevel() >= LOGLEVEL_TRACE) {
1161 dump_outline(gfxline);
1164 msg("<warning> Empty polygon (resulting from stroked line)");
1166 if(flags&STROKE_CLIP) {
1167 device->startclip(device, gfxline);
1168 states[statepos].clipping++;
1170 device->fill(device, gfxline, &col);
1172 gfxline_free(gfxline);
1175 if(flags&STROKE_CLIP)
1176 msg("<error> Stroke&clip not supported at the same time");
1177 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1181 gfxline_free(line2);
1184 gfxcolor_t getFillColor(GfxState * state)
1187 double opaq = state->getFillOpacity();
1188 state->getFillRGB(&rgb);
1190 col.r = colToByte(rgb.r);
1191 col.g = colToByte(rgb.g);
1192 col.b = colToByte(rgb.b);
1193 col.a = (unsigned char)(opaq*255);
1197 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1199 gfxcolor_t col = getFillColor(state);
1201 if(getLogLevel() >= LOGLEVEL_TRACE) {
1202 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1205 device->fill(device, line, &col);
1208 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1210 if(getLogLevel() >= LOGLEVEL_TRACE) {
1213 gfxbbox_t bbox = gfxline_getbbox(line);
1214 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1216 device->startclip(device, line);
1217 states[statepos].clipping++;
1220 void GFXOutputDev::clip(GfxState *state)
1222 GfxPath * path = state->getPath();
1223 msg("<trace> clip");
1224 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1225 if(config_optimize_polygons) {
1226 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1230 clipToGfxLine(state, line);
1234 void GFXOutputDev::eoClip(GfxState *state)
1236 GfxPath * path = state->getPath();
1237 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1238 clipToGfxLine(state, line);
1241 void GFXOutputDev::clipToStrokePath(GfxState *state)
1243 GfxPath * path = state->getPath();
1244 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1246 if(getLogLevel() >= LOGLEVEL_TRACE) {
1247 double width = state->getTransformedLineWidth();
1248 msg("<trace> cliptostrokepath width=%f", width);
1252 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1256 void GFXOutputDev::finish()
1258 if(outer_clip_box) {
1260 device->endclip(device);
1266 GFXOutputDev::~GFXOutputDev()
1270 GBool GFXOutputDev::upsideDown()
1274 GBool GFXOutputDev::useDrawChar()
1279 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1280 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1282 static char tmp_printstr[4096];
1283 char* makeStringPrintable(char*str)
1285 int len = strlen(str);
1292 for(t=0;t<len;t++) {
1297 tmp_printstr[t] = c;
1300 tmp_printstr[len++] = '.';
1301 tmp_printstr[len++] = '.';
1302 tmp_printstr[len++] = '.';
1304 tmp_printstr[len] = 0;
1305 return tmp_printstr;
1307 void GFXOutputDev::updateFontMatrix(GfxState*state)
1309 double* ctm = state->getCTM();
1310 double fontSize = state->getFontSize();
1311 double*textMat = state->getTextMat();
1313 /* taking the absolute value of horizScaling seems to be required for
1314 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1315 double hscale = fabs(state->getHorizScaling());
1317 // from xpdf-3.02/SplashOutputDev:updateFont
1318 double mm11 = textMat[0] * fontSize * hscale;
1319 double mm12 = textMat[1] * fontSize * hscale;
1320 double mm21 = textMat[2] * fontSize;
1321 double mm22 = textMat[3] * fontSize;
1323 // multiply with ctm, like state->getFontTransMat() does
1324 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1325 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1326 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1327 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1328 this->current_font_matrix.tx = 0;
1329 this->current_font_matrix.ty = 0;
1332 void GFXOutputDev::beginString(GfxState *state, GString *s)
1334 int render = state->getRender();
1335 if(current_text_stroke) {
1336 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1338 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1341 static gfxline_t* mkEmptyGfxShape(double x, double y)
1343 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1344 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1348 static char isValidUnicode(int c)
1350 if(c>=32 && c<0x2fffe)
1355 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1356 double dx, double dy,
1357 double originX, double originY,
1358 CharCode charid, int nBytes, Unicode *_u, int uLen)
1360 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1361 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1365 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1367 int render = state->getRender();
1368 gfxcolor_t col = getFillColor(state);
1370 // check for invisible text -- this is used by Acrobat Capture
1371 if (render == RENDER_INVISIBLE) {
1373 if(!config_extrafontdata)
1377 GfxFont*font = state->getFont();
1379 if(font->getType() == fontType3) {
1380 /* type 3 chars are passed as graphics */
1381 msg("<debug> type3 char at %f/%f", x, y);
1385 Unicode u = uLen?(_u[0]):0;
1387 gfxmatrix_t m = this->current_font_matrix;
1388 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1389 //m.tx += originX; m.ty += originY;
1391 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);
1393 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1394 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1396 msg("<debug> Drawing glyph %d as shape", charid);
1397 if(!gfxglobals->textmodeinfo) {
1398 msg("<notice> Some texts will be rendered as shape");
1399 gfxglobals->textmodeinfo = 1;
1401 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1402 gfxline_t*tglyph = gfxline_clone(glyph);
1403 gfxline_transform(tglyph, &m);
1404 if((render&3) != RENDER_INVISIBLE) {
1405 gfxline_t*add = gfxline_clone(tglyph);
1406 current_text_stroke = gfxline_append(current_text_stroke, add);
1408 if(render&RENDER_CLIP) {
1409 gfxline_t*add = gfxline_clone(tglyph);
1410 current_text_clip = gfxline_append(current_text_clip, add);
1411 if(!current_text_clip) {
1412 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1415 gfxline_free(tglyph);
1419 void GFXOutputDev::endString(GfxState *state)
1421 int render = state->getRender();
1422 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1424 if(current_text_stroke) {
1425 /* fillstroke and stroke text rendering objects we can process right
1426 now (as there may be texts of other rendering modes in this
1427 text object)- clipping objects have to wait until endTextObject,
1429 device->setparameter(device, "mark","TXT");
1430 if((render&3) == RENDER_FILL) {
1431 fillGfxLine(state, current_text_stroke);
1432 gfxline_free(current_text_stroke);
1433 current_text_stroke = 0;
1434 } else if((render&3) == RENDER_FILLSTROKE) {
1435 fillGfxLine(state, current_text_stroke);
1436 strokeGfxline(state, current_text_stroke,0);
1437 gfxline_free(current_text_stroke);
1438 current_text_stroke = 0;
1439 } else if((render&3) == RENDER_STROKE) {
1440 strokeGfxline(state, current_text_stroke,0);
1441 gfxline_free(current_text_stroke);
1442 current_text_stroke = 0;
1444 device->setparameter(device, "mark","");
1448 void GFXOutputDev::endTextObject(GfxState *state)
1450 int render = state->getRender();
1451 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1453 if(current_text_clip) {
1454 device->setparameter(device, "mark","TXT");
1455 clipToGfxLine(state, current_text_clip);
1456 device->setparameter(device, "mark","");
1457 gfxline_free(current_text_clip);
1458 current_text_clip = 0;
1462 /* the logic seems to be as following:
1463 first, beginType3Char is called, with the charcode and the coordinates.
1464 if this function returns true, it already knew about the char and has now drawn it.
1465 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1466 called with some parameters.
1467 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1468 at the position first passed to beginType3Char). the char ends with endType3Char.
1470 The drawing operations between beginType3Char and endType3Char are somewhat different to
1471 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1472 color determines the color of a font)
1475 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1477 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1480 if(config_extrafontdata && current_fontinfo) {
1482 gfxmatrix_t m = this->current_font_matrix;
1483 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1484 m.m00*=INTERNAL_FONT_SIZE;
1485 m.m01*=INTERNAL_FONT_SIZE;
1486 m.m10*=INTERNAL_FONT_SIZE;
1487 m.m11*=INTERNAL_FONT_SIZE;
1489 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1490 msg("<error> Invalid charid %d for font", charid);
1493 gfxcolor_t col={0,0,0,0};
1494 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1495 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1499 /* the character itself is going to be passed using the draw functions */
1500 return gFalse; /* gTrue= is_in_cache? */
1503 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1505 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1508 void GFXOutputDev::endType3Char(GfxState *state)
1511 msg("<debug> endType3Char");
1514 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1516 this->currentpage = pageNum;
1518 int rot = doc->getPageRotate(1);
1519 gfxcolor_t white = {255,255,255,255};
1520 gfxcolor_t black = {255,0,0,0};
1522 gfxline_t clippath[5];
1524 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1525 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1526 Use CropBox, not MediaBox, as page size
1533 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1534 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1536 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1537 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1539 this->clipmovex = -(int)x1;
1540 this->clipmovey = -(int)y1;
1542 /* apply user clip box */
1543 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1544 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1545 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1546 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1547 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1548 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1550 x1 += this->clipmovex;
1551 y1 += this->clipmovey;
1552 x2 += this->clipmovex;
1553 y2 += this->clipmovey;
1556 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1558 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);
1560 msg("<verbose> page is rotated %d degrees", rot);
1562 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1563 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1564 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1565 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1566 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1567 device->startclip(device, clippath); outer_clip_box = 1;
1568 if(!config_transparent) {
1569 device->fill(device, clippath, &white);
1571 states[statepos].clipbbox.xmin = x1;
1572 states[statepos].clipbbox.ymin = x1;
1573 states[statepos].clipbbox.xmax = x2;
1574 states[statepos].clipbbox.ymax = y2;
1576 states[statepos].dashPattern = 0;
1577 states[statepos].dashLength = 0;
1578 states[statepos].dashStart = 0;
1582 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1584 double x1, y1, x2, y2;
1585 gfxline_t points[5];
1588 msg("<debug> drawlink");
1590 link->getRect(&x1, &y1, &x2, &y2);
1591 cvtUserToDev(x1, y1, &x, &y);
1592 points[0].type = gfx_moveTo;
1593 points[0].x = points[4].x = x + user_movex + clipmovex;
1594 points[0].y = points[4].y = y + user_movey + clipmovey;
1595 points[0].next = &points[1];
1596 cvtUserToDev(x2, y1, &x, &y);
1597 points[1].type = gfx_lineTo;
1598 points[1].x = x + user_movex + clipmovex;
1599 points[1].y = y + user_movey + clipmovey;
1600 points[1].next = &points[2];
1601 cvtUserToDev(x2, y2, &x, &y);
1602 points[2].type = gfx_lineTo;
1603 points[2].x = x + user_movex + clipmovex;
1604 points[2].y = y + user_movey + clipmovey;
1605 points[2].next = &points[3];
1606 cvtUserToDev(x1, y2, &x, &y);
1607 points[3].type = gfx_lineTo;
1608 points[3].x = x + user_movex + clipmovex;
1609 points[3].y = y + user_movey + clipmovey;
1610 points[3].next = &points[4];
1611 cvtUserToDev(x1, y1, &x, &y);
1612 points[4].type = gfx_lineTo;
1613 points[4].x = x + user_movex + clipmovex;
1614 points[4].y = y + user_movey + clipmovey;
1617 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1618 points[0].x, points[0].y,
1619 points[1].x, points[1].y,
1620 points[2].x, points[2].y,
1621 points[3].x, points[3].y);
1623 if(getLogLevel() >= LOGLEVEL_TRACE) {
1624 dump_outline(points);
1627 LinkAction*action=link->getAction();
1630 const char*type = "-?-";
1633 msg("<trace> drawlink action=%d", action->getKind());
1634 switch(action->getKind())
1638 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1639 LinkDest *dest=NULL;
1640 if (ha->getDest()==NULL)
1641 dest=catalog->findDest(ha->getNamedDest());
1643 dest=ha->getDest()->copy();
1645 if (dest->isPageRef()){
1646 Ref pageref=dest->getPageRef();
1647 page=catalog->findPage(pageref.num,pageref.gen);
1649 else page=dest->getPageNum();
1650 sprintf(buf, "%d", page);
1658 LinkGoToR*l = (LinkGoToR*)action;
1659 GString*g = l->getFileName();
1661 s = strdup(g->getCString());
1663 /* if the GoToR link has no filename, then
1664 try to find a refernce in the *local*
1666 GString*g = l->getNamedDest();
1668 s = strdup(g->getCString());
1674 LinkNamed*l = (LinkNamed*)action;
1675 GString*name = l->getName();
1677 s = strdup(name->lowerCase()->getCString());
1678 named = name->getCString();
1681 if(strstr(s, "next") || strstr(s, "forward"))
1683 page = currentpage + 1;
1685 else if(strstr(s, "prev") || strstr(s, "back"))
1687 page = currentpage - 1;
1689 else if(strstr(s, "last") || strstr(s, "end"))
1691 if(this->page2page && this->num_pages) {
1692 page = this->page2page[this->num_pages-1];
1695 else if(strstr(s, "first") || strstr(s, "top"))
1703 case actionLaunch: {
1705 LinkLaunch*l = (LinkLaunch*)action;
1706 GString * str = new GString(l->getFileName());
1707 GString * params = l->getParams();
1709 str->append(params);
1710 s = strdup(str->getCString());
1717 LinkURI*l = (LinkURI*)action;
1718 GString*g = l->getURI();
1720 url = g->getCString();
1725 case actionUnknown: {
1727 LinkUnknown*l = (LinkUnknown*)action;
1732 msg("<error> Unknown link type!");
1737 if(!s) s = strdup("-?-");
1739 msg("<trace> drawlink s=%s", s);
1741 if(!gfxglobals->linkinfo && (page || s))
1743 msg("<notice> File contains links");
1744 gfxglobals->linkinfo = 1;
1750 for(t=1;t<=this->num_pages;t++) {
1751 if(this->page2page[t]==page) {
1761 sprintf(buf, "page%d", lpage);
1762 device->drawlink(device, points, buf);
1766 device->drawlink(device, points, s);
1769 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1773 void GFXOutputDev::saveState(GfxState *state) {
1774 dbg("saveState %08x", state); dbgindent+=2;
1776 msg("<trace> saveState %08x", state);
1779 msg("<fatal> Too many nested states in pdf.");
1783 states[statepos].state = state;
1784 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1785 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1786 states[statepos].clipping = 0;
1787 states[statepos].olddevice = 0;
1788 states[statepos].clipbbox = states[statepos-1].clipbbox;
1790 states[statepos].dashPattern = states[statepos-1].dashPattern;
1791 states[statepos].dashStart = states[statepos-1].dashStart;
1792 states[statepos].dashLength = states[statepos-1].dashLength;
1795 void GFXOutputDev::restoreState(GfxState *state) {
1796 dbgindent-=2; dbg("restoreState %08x", state);
1799 msg("<fatal> Invalid restoreState");
1802 msg("<trace> restoreState %08x%s%s", state,
1803 states[statepos].softmask?" (end softmask)":"",
1804 states[statepos].clipping?" (end clipping)":"");
1805 if(states[statepos].softmask) {
1806 clearSoftMask(state);
1809 if(states[statepos].dashPattern) {
1810 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1811 free(states[statepos].dashPattern);
1812 states[statepos].dashPattern = 0;
1818 while(states[statepos].clipping) {
1819 device->endclip(device);
1820 states[statepos].clipping--;
1822 if(states[statepos].state!=state) {
1823 msg("<fatal> bad state nesting");
1826 states[statepos].state=0;
1830 void GFXOutputDev::updateLineDash(GfxState *state)
1832 if(states[statepos].dashPattern &&
1833 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1834 free(states[statepos].dashPattern);
1835 states[statepos].dashPattern = 0;
1837 double *pattern = 0;
1840 state->getLineDash(&pattern, &dashLength, &dashStart);
1841 msg("<debug> updateLineDash, %d dashes", dashLength);
1843 states[statepos].dashPattern = 0;
1844 states[statepos].dashLength = 0;
1846 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1847 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1848 states[statepos].dashPattern = p;
1849 states[statepos].dashLength = dashLength;
1850 states[statepos].dashStart = dashStart;
1854 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1856 this->page2page = page2page;
1857 this->num_pages = num_pages;
1860 void GFXOutputDev::updateLineWidth(GfxState *state)
1862 double width = state->getTransformedLineWidth();
1865 void GFXOutputDev::updateLineCap(GfxState *state)
1867 int c = state->getLineCap();
1870 void GFXOutputDev::updateLineJoin(GfxState *state)
1872 int j = state->getLineJoin();
1875 void GFXOutputDev::updateFillColor(GfxState *state)
1878 double opaq = state->getFillOpacity();
1879 state->getFillRGB(&rgb);
1881 void GFXOutputDev::updateFillOpacity(GfxState *state)
1884 double opaq = state->getFillOpacity();
1885 state->getFillRGB(&rgb);
1886 dbg("update fillopaq %f", opaq);
1888 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1890 double opaq = state->getFillOpacity();
1891 dbg("update strokeopaq %f", opaq);
1893 void GFXOutputDev::updateFillOverprint(GfxState *state)
1895 double opaq = state->getFillOverprint();
1896 dbg("update filloverprint %f", opaq);
1898 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1900 double opaq = state->getStrokeOverprint();
1901 dbg("update strokeoverprint %f", opaq);
1903 void GFXOutputDev::updateTransfer(GfxState *state)
1905 dbg("update transfer");
1909 void GFXOutputDev::updateStrokeColor(GfxState *state)
1912 double opaq = state->getStrokeOpacity();
1913 state->getStrokeRGB(&rgb);
1916 void GFXOutputDev::updateFont(GfxState *state)
1918 GfxFont* gfxFont = state->getFont();
1922 char*id = getFontID(gfxFont);
1923 msg("<verbose> Updating font to %s", id);
1924 if(gfxFont->getType() == fontType3) {
1925 infofeature("Type3 fonts");
1926 if(!config_extrafontdata) {
1931 msg("<error> Internal Error: FontID is null");
1935 this->current_fontinfo = this->info->getFont(id);
1937 if(!this->current_fontinfo) {
1938 msg("<error> Internal Error: no fontinfo for font %s", id);
1941 if(!this->current_fontinfo->seen) {
1942 dumpFontInfo("<verbose>", gfxFont);
1945 current_gfxfont = this->current_fontinfo->getGfxFont();
1946 device->addfont(device, current_gfxfont);
1949 updateFontMatrix(state);
1952 #define SQR(x) ((x)*(x))
1954 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1956 if((newwidth<1 || newheight<1) ||
1957 (width<=newwidth || height<=newheight))
1959 unsigned char*newdata;
1961 newdata= (unsigned char*)malloc(newwidth*newheight);
1962 double fx = ((double)width)/newwidth;
1963 double fy = ((double)height)/newheight;
1965 int blocksize = (int)(8192/(fx*fy));
1966 int r = 8192*256/palettesize;
1967 for(x=0;x<newwidth;x++) {
1968 double ex = px + fx;
1969 int fromx = (int)px;
1971 int xweight1 = (int)((1-(px-fromx))*256);
1972 int xweight2 = (int)((ex-tox)*256);
1974 for(y=0;y<newheight;y++) {
1975 double ey = py + fy;
1976 int fromy = (int)py;
1978 int yweight1 = (int)((1-(py-fromy))*256);
1979 int yweight2 = (int)((ey-toy)*256);
1986 for(xx=fromx;xx<=tox;xx++)
1987 for(yy=fromy;yy<=toy;yy++) {
1988 int b = 1-data[width*yy+xx];
1990 if(xx==fromx) weight = (weight*xweight1)/256;
1991 if(xx==tox) weight = (weight*xweight2)/256;
1992 if(yy==fromy) weight = (weight*yweight1)/256;
1993 if(yy==toy) weight = (weight*yweight2)/256;
1996 //if(a) a=(palettesize-1)*r/blocksize;
1997 newdata[y*newwidth+x] = (a*blocksize)/r;
2005 #define IMAGE_TYPE_JPEG 0
2006 #define IMAGE_TYPE_LOSSLESS 1
2008 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2009 double x1,double y1,
2010 double x2,double y2,
2011 double x3,double y3,
2012 double x4,double y4, int type, int multiply)
2014 gfxcolor_t*newpic=0;
2016 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2017 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2019 gfxline_t p1,p2,p3,p4,p5;
2020 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2021 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2022 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2023 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2024 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2026 {p1.x = (int)(p1.x*20)/20.0;
2027 p1.y = (int)(p1.y*20)/20.0;
2028 p2.x = (int)(p2.x*20)/20.0;
2029 p2.y = (int)(p2.y*20)/20.0;
2030 p3.x = (int)(p3.x*20)/20.0;
2031 p3.y = (int)(p3.y*20)/20.0;
2032 p4.x = (int)(p4.x*20)/20.0;
2033 p4.y = (int)(p4.y*20)/20.0;
2034 p5.x = (int)(p5.x*20)/20.0;
2035 p5.y = (int)(p5.y*20)/20.0;
2039 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2040 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2042 m.tx = p1.x - 0.5*multiply;
2043 m.ty = p1.y - 0.5*multiply;
2046 img.data = (gfxcolor_t*)data;
2050 if(type == IMAGE_TYPE_JPEG)
2051 /* TODO: pass image_dpi to device instead */
2052 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2055 dev->fillbitmap(dev, &p1, &img, &m, 0);
2058 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2059 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2061 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2064 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2065 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2067 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2071 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2072 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2073 GBool inlineImg, int mask, int*maskColors,
2074 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2076 /* the code in this function is *old*. It's not pretty, but it works. */
2078 double x1,y1,x2,y2,x3,y3,x4,y4;
2079 ImageStream *imgStr;
2084 unsigned char* maskbitmap = 0;
2087 ncomps = colorMap->getNumPixelComps();
2088 bits = colorMap->getBits();
2093 unsigned char buf[8];
2094 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2096 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2097 imgMaskStr->reset();
2098 unsigned char pal[256];
2099 int n = 1 << colorMap->getBits();
2104 maskColorMap->getGray(pixBuf, &gray);
2105 pal[t] = colToByte(gray);
2107 for (y = 0; y < maskHeight; y++) {
2108 for (x = 0; x < maskWidth; x++) {
2109 imgMaskStr->getPixel(buf);
2110 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2115 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2116 imgMaskStr->reset();
2117 for (y = 0; y < maskHeight; y++) {
2118 for (x = 0; x < maskWidth; x++) {
2119 imgMaskStr->getPixel(buf);
2121 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2129 imgStr = new ImageStream(str, width, ncomps,bits);
2132 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2134 msg("<verbose> Ignoring %d by %d image", width, height);
2135 unsigned char buf[8];
2137 for (y = 0; y < height; ++y)
2138 for (x = 0; x < width; ++x) {
2139 imgStr->getPixel(buf);
2147 this->transformXY(state, 0, 1, &x1, &y1);
2148 this->transformXY(state, 0, 0, &x2, &y2);
2149 this->transformXY(state, 1, 0, &x3, &y3);
2150 this->transformXY(state, 1, 1, &x4, &y4);
2153 /* as type 3 bitmaps are antialized, we need to place them
2154 at integer coordinates, otherwise flash player's antializing
2155 will kick in and make everything blurry */
2156 x1 = (int)(x1);y1 = (int)(y1);
2157 x2 = (int)(x2);y2 = (int)(y2);
2158 x3 = (int)(x3);y3 = (int)(y3);
2159 x4 = (int)(x4);y4 = (int)(y4);
2162 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2164 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2165 gfxglobals->pbminfo = 1;
2168 msg("<verbose> drawing %d by %d masked picture", width, height);
2170 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2171 msg("<notice> File contains jpeg pictures");
2172 gfxglobals->jpeginfo = 1;
2176 unsigned char buf[8];
2178 unsigned char*pic = new unsigned char[width*height];
2179 gfxcolor_t pal[256];
2181 state->getFillRGB(&rgb);
2183 memset(pal,255,sizeof(pal));
2184 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2185 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2186 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2187 pal[0].a = 255; pal[1].a = 0;
2190 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2191 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2192 for (y = 0; y < height; ++y)
2193 for (x = 0; x < width; ++x)
2195 imgStr->getPixel(buf);
2198 pic[width*y+x] = buf[0];
2202 unsigned char*pic2 = 0;
2205 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2214 height = realheight;
2218 /* make a black/white palette */
2220 float r = 255./(float)(numpalette-1);
2222 for(t=0;t<numpalette;t++) {
2223 pal[t].r = colToByte(rgb.r);
2224 pal[t].g = colToByte(rgb.g);
2225 pal[t].b = colToByte(rgb.b);
2226 pal[t].a = (unsigned char)(t*r);
2231 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2232 for (y = 0; y < height; ++y) {
2233 for (x = 0; x < width; ++x) {
2234 pic2[width*y+x] = pal[pic[y*width+x]];
2237 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2241 if(maskbitmap) free(maskbitmap);
2247 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2248 gfxcolor_t*pic=new gfxcolor_t[width*height];
2249 for (y = 0; y < height; ++y) {
2250 for (x = 0; x < width; ++x) {
2251 imgStr->getPixel(pixBuf);
2252 colorMap->getRGB(pixBuf, &rgb);
2253 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2254 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2255 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2256 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2258 int x1 = x*maskWidth/width;
2259 int y1 = y*maskHeight/height;
2260 int x2 = (x+1)*maskWidth/width;
2261 int y2 = (y+1)*maskHeight/height;
2263 unsigned int alpha=0;
2264 unsigned int count=0;
2265 for(xx=x1;xx<x2;xx++)
2266 for(yy=y1;yy<y2;yy++) {
2267 alpha += maskbitmap[yy*maskWidth+xx];
2271 pic[width*y+x].a = alpha / count;
2273 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2278 if(str->getKind()==strDCT)
2279 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2281 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2284 if(maskbitmap) free(maskbitmap);
2287 gfxcolor_t*pic=new gfxcolor_t[width*height];
2288 gfxcolor_t pal[256];
2289 int n = 1 << colorMap->getBits();
2291 for(t=0;t<256;t++) {
2293 colorMap->getRGB(pixBuf, &rgb);
2295 {/*if(maskColors && *maskColors==t) {
2296 msg("<notice> Color %d is transparent", t);
2297 if (imgData->maskColors) {
2299 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2300 if (pix[i] < imgData->maskColors[2*i] ||
2301 pix[i] > imgData->maskColors[2*i+1]) {
2316 pal[t].r = (unsigned char)(colToByte(rgb.r));
2317 pal[t].g = (unsigned char)(colToByte(rgb.g));
2318 pal[t].b = (unsigned char)(colToByte(rgb.b));
2319 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2322 for (y = 0; y < height; ++y) {
2323 for (x = 0; x < width; ++x) {
2324 imgStr->getPixel(pixBuf);
2325 pic[width*y+x] = pal[pixBuf[0]];
2329 if(maskWidth < width && maskHeight < height) {
2330 for(y = 0; y < height; y++) {
2331 for (x = 0; x < width; x++) {
2332 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2336 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2337 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2338 double dx = width / maskWidth;
2339 double dy = height / maskHeight;
2341 for(y = 0; y < maskHeight; y++) {
2343 for (x = 0; x < maskWidth; x++) {
2344 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2345 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2353 height = maskHeight;
2356 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2360 if(maskbitmap) free(maskbitmap);
2365 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2366 int width, int height, GBool invert,
2369 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2370 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2371 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2374 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2375 int width, int height, GfxImageColorMap *colorMap,
2376 int *maskColors, GBool inlineImg)
2378 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2379 colorMap?"colorMap":"no colorMap",
2380 maskColors?"maskColors":"no maskColors",
2382 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2383 colorMap?"colorMap":"no colorMap",
2384 maskColors?"maskColors":"no maskColors",
2387 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2388 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2389 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2392 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2393 int width, int height,
2394 GfxImageColorMap *colorMap,
2395 Stream *maskStr, int maskWidth, int maskHeight,
2398 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2399 colorMap?"colorMap":"no colorMap",
2400 maskWidth, maskHeight);
2401 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2402 colorMap?"colorMap":"no colorMap",
2403 maskWidth, maskHeight);
2405 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2406 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2407 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2410 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2411 int width, int height,
2412 GfxImageColorMap *colorMap,
2414 int maskWidth, int maskHeight,
2415 GfxImageColorMap *maskColorMap)
2417 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2418 colorMap?"colorMap":"no colorMap",
2419 maskWidth, maskHeight);
2420 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2421 colorMap?"colorMap":"no colorMap",
2422 maskWidth, maskHeight);
2424 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2425 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2426 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2429 void GFXOutputDev::stroke(GfxState *state)
2433 GfxPath * path = state->getPath();
2434 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2435 strokeGfxline(state, line, 0);
2439 void GFXOutputDev::fill(GfxState *state)
2441 gfxcolor_t col = getFillColor(state);
2442 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2444 GfxPath * path = state->getPath();
2445 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2446 if(config_optimize_polygons) {
2447 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2451 fillGfxLine(state, line);
2455 void GFXOutputDev::eoFill(GfxState *state)
2457 gfxcolor_t col = getFillColor(state);
2458 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2460 GfxPath * path = state->getPath();
2461 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2462 fillGfxLine(state, line);
2467 static const char* dirseparator()
2476 void addGlobalFont(const char*filename)
2478 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2479 memset(f, 0, sizeof(fontfile_t));
2480 f->filename = filename;
2481 int len = strlen(filename);
2482 char*r1 = strrchr(filename, '/');
2483 char*r2 = strrchr(filename, '\\');
2491 msg("<verbose> Adding font \"%s\".", filename);
2492 if(global_fonts_next) {
2493 global_fonts_next->next = f;
2494 global_fonts_next = global_fonts_next->next;
2496 global_fonts_next = global_fonts = f;
2500 void addGlobalLanguageDir(const char*dir)
2502 msg("<notice> Adding %s to language pack directories", dir);
2505 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2506 strcpy(config_file, dir);
2507 strcat(config_file, dirseparator());
2508 strcat(config_file, "add-to-xpdfrc");
2510 fi = fopen(config_file, "rb");
2512 msg("<error> Could not open %s", config_file);
2515 globalParams->parseFile(new GString(config_file), fi);
2519 void addGlobalFontDir(const char*dirname)
2521 #ifdef HAVE_DIRENT_H
2522 DIR*dir = opendir(dirname);
2524 msg("<warning> Couldn't open directory %s", dirname);
2530 ent = readdir (dir);
2534 char*name = ent->d_name;
2540 if(!strncasecmp(&name[l-4], ".pfa", 4))
2542 if(!strncasecmp(&name[l-4], ".pfb", 4))
2544 if(!strncasecmp(&name[l-4], ".ttf", 4))
2547 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2548 strcpy(fontname, dirname);
2549 strcat(fontname, dirseparator());
2550 strcat(fontname, name);
2551 addGlobalFont(fontname);
2555 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2558 msg("<warning> No dirent.h");
2562 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2563 GfxColorSpace *blendingColorSpace,
2564 GBool isolated, GBool knockout,
2567 const char*colormodename = "";
2569 if(blendingColorSpace) {
2570 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2572 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);
2573 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);
2575 //states[statepos].createsoftmask |= forSoftMask;
2576 states[statepos].createsoftmask = forSoftMask;
2577 states[statepos].transparencygroup = !forSoftMask;
2578 states[statepos].isolated = isolated;
2580 states[statepos].olddevice = this->device;
2581 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2582 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2584 gfxdevice_record_init(this->device);
2586 /*if(!forSoftMask) { ////???
2587 state->setFillOpacity(0.0);
2592 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2595 gfxdevice_t*r = this->device;
2597 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2599 this->device = states[statepos].olddevice;
2601 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2602 /* if these errors occur more often, we should build a seperate
2603 transparency group stack, like xpdf/SplashOutputDev.cc does */
2604 restoreState(state);
2605 this->device = states[statepos].olddevice;
2607 states[statepos].olddevice = 0;
2609 gfxresult_t*recording = r->finish(r);
2611 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2612 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2614 if(states[statepos].createsoftmask) {
2615 states[statepos-1].softmaskrecording = recording;
2617 states[statepos-1].grouprecording = recording;
2620 states[statepos].createsoftmask = 0;
2621 states[statepos].transparencygroup = 0;
2625 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2627 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2628 "colordodge","colorburn","hardlight","softlight","difference",
2629 "exclusion","hue","saturation","color","luminosity"};
2631 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2632 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2634 if(state->getBlendMode() == gfxBlendNormal)
2635 infofeature("transparency groups");
2638 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2639 warnfeature(buffer, 0);
2642 gfxresult_t*grouprecording = states[statepos].grouprecording;
2644 int blendmode = state->getBlendMode();
2645 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2646 int alpha = (int)(state->getFillOpacity()*255);
2647 if(blendmode == gfxBlendMultiply && alpha>200)
2650 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2651 gfxdevice_ops_init(&ops, this->device, alpha);
2652 gfxresult_record_replay(grouprecording, &ops);
2655 grouprecording->destroy(grouprecording);
2657 states[statepos].grouprecording = 0;
2660 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2662 if(states[statepos].softmask) {
2663 /* shouldn't happen, but *does* happen */
2664 clearSoftMask(state);
2667 /* alpha = 1: retrieve mask values from alpha layer
2668 alpha = 0: retrieve mask values from luminance */
2670 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2671 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2672 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2673 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2675 infofeature("soft masks");
2677 warnfeature("soft masks from alpha channel",0);
2679 if(states[statepos].olddevice) {
2680 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2683 states[statepos].olddevice = this->device;
2684 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2685 gfxdevice_record_init(this->device);
2687 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2689 states[statepos].softmask = 1;
2690 states[statepos].softmask_alpha = alpha;
2693 static inline Guchar div255(int x) {
2694 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2697 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2699 if(c < min) c = min;
2700 if(c > max) c = max;
2704 void GFXOutputDev::clearSoftMask(GfxState *state)
2706 if(!states[statepos].softmask)
2708 states[statepos].softmask = 0;
2709 dbg("clearSoftMask statepos=%d", statepos);
2710 msg("<verbose> clearSoftMask statepos=%d", statepos);
2712 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2713 msg("<error> Error in softmask/tgroup ordering");
2717 gfxresult_t*mask = states[statepos].softmaskrecording;
2718 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2719 this->device = states[statepos].olddevice;
2721 /* get outline of all objects below the soft mask */
2722 gfxdevice_t uniondev;
2723 gfxdevice_union_init(&uniondev, 0);
2724 gfxresult_record_replay(below, &uniondev);
2725 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2726 uniondev.finish(&uniondev);
2727 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2728 gfxline_free(belowoutline);belowoutline=0;
2730 this->device->startclip(this->device, belowoutline);
2731 gfxresult_record_replay(below, this->device);
2732 gfxresult_record_replay(mask, this->device);
2733 this->device->endclip(this->device);
2736 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2737 if(width<=0 || height<=0)
2740 gfxdevice_t belowrender;
2741 gfxdevice_render_init(&belowrender);
2742 if(states[statepos+1].isolated) {
2743 belowrender.setparameter(&belowrender, "fillwhite", "1");
2745 belowrender.setparameter(&belowrender, "antialize", "2");
2746 belowrender.startpage(&belowrender, width, height);
2747 gfxresult_record_replay(below, &belowrender);
2748 belowrender.endpage(&belowrender);
2749 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2750 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2751 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2753 gfxdevice_t maskrender;
2754 gfxdevice_render_init(&maskrender);
2755 maskrender.startpage(&maskrender, width, height);
2756 gfxresult_record_replay(mask, &maskrender);
2757 maskrender.endpage(&maskrender);
2758 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2759 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2761 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2762 msg("<fatal> Internal error in mask drawing");
2767 for(y=0;y<height;y++) {
2768 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2769 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2770 for(x=0;x<width;x++) {
2772 if(states[statepos].softmask_alpha) {
2775 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2778 l2->a = div255(alpha*l2->a);
2780 /* DON'T premultiply alpha- this is done by fillbitmap,
2781 depending on the output device */
2782 //l2->r = div255(alpha*l2->r);
2783 //l2->g = div255(alpha*l2->g);
2784 //l2->b = div255(alpha*l2->b);
2790 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2793 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2794 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2796 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2798 mask->destroy(mask);
2799 below->destroy(below);
2800 maskresult->destroy(maskresult);
2801 belowresult->destroy(belowresult);
2802 states[statepos].softmaskrecording = 0;
2807 // public: ~MemCheck()
2809 // delete globalParams;globalParams=0;
2810 // Object::memCheck(stderr);
2811 // gMemReport(stderr);