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 vsnprintf(buf, sizeof(buf)-1, 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 stralphacmp(const char*s1, const char*s2)
341 /* skip over space, minus, comma etc. */
342 while(*s1>=32 && *s1<=63) s1++;
343 while(*s2>=32 && *s2<=63) s2++;
351 static char fc_ismatch(FcPattern*match, char*family, char*style)
353 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
354 FcBool scalable=FcFalse, outline=FcFalse;
355 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
356 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
357 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
358 FcPatternGetBool(match, "outline", 0, &outline);
359 FcPatternGetBool(match, "scalable", 0, &scalable);
361 if(scalable!=FcTrue || outline!=FcTrue)
364 if (!stralphacmp(fcfamily, family)) {
365 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
368 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
374 char* fontconfig_searchForFont(char*name)
376 #ifdef HAVE_FONTCONFIG
377 if(!config_use_fontconfig)
380 // call init ony once
384 // check whether we have a config file
385 char* configfile = (char*)FcConfigFilename(0);
386 int configexists = 0;
387 FILE*fi = fopen(configfile, "rb");
389 configexists = 1;fclose(fi);
390 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
392 msg("<debug> Initializing FontConfig (no configfile)");
396 /* A fontconfig instance which didn't find a configfile is unbelievably
397 cranky, so let's just write out a small xml file and make fontconfig
399 FcConfig*c = FcConfigCreate();
401 char* tmpFileName = mktmpname(namebuf);
402 FILE*fi = fopen(tmpFileName, "wb");
403 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
405 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
407 fprintf(fi, "<dir>~/.fonts</dir>\n");
409 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
411 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
412 fprintf(fi, "</fontconfig>\n");
414 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
415 FcConfigBuildFonts(c);
416 FcConfigSetCurrent(c);
420 msg("<debug> FontConfig Initialization failed. Disabling.");
421 config_use_fontconfig = 0;
424 FcConfig * config = FcConfigGetCurrent();
426 msg("<debug> FontConfig Config Initialization failed. Disabling.");
427 config_use_fontconfig = 0;
431 /* add external fonts to fontconfig's config, too. */
432 fontfile_t*fd = global_fonts;
434 FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
435 msg("<debug> Adding font %s to fontconfig", fd->filename);
439 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
440 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
441 if(!set || !set->nfont) {
442 msg("<debug> FontConfig has zero fonts. Disabling.");
443 config_use_fontconfig = 0;
447 if(getLogLevel() >= LOGLEVEL_TRACE) {
451 for(t=0;t<set->nfont;t++) {
452 char*fcfamily=0,*fcstyle=0,*filename=0;
453 FcBool scalable=FcFalse, outline=FcFalse;
454 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
455 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
456 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
457 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
458 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
459 if(scalable && outline) {
460 msg("<trace> %s (%s) -> %s", fcfamily, fcstyle, filename);
463 set = FcConfigGetFonts(config, FcSetApplication);
468 char*family = strdup(name);
470 char*dash = strchr(family, '-');
471 if(!dash) dash = strchr(family, ',');
473 FcPattern*pattern = 0;
477 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
478 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
480 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
481 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
485 FcConfigSubstitute(0, pattern, FcMatchPattern);
486 FcDefaultSubstitute(pattern);
488 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
491 for(t=0;t<set->nfont;t++) {
492 FcPattern*match = set->fonts[t];
493 //FcPattern*match = FcFontMatch(0, pattern, &result);
494 if(fc_ismatch(match, family, style)) {
496 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
497 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
500 //FcPatternDestroy(match);
501 msg("<debug> fontconfig: returning filename %s", filename);
503 FcPatternDestroy(pattern);
504 FcFontSetDestroy(set);
505 return filename?strdup(filename):0;
510 FcPatternDestroy(pattern);
511 FcFontSetDestroy(set);
518 static DisplayFontParamKind detectFontType(const char*filename)
520 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
521 return displayFontTT;
522 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
523 return displayFontT1;
524 return displayFontTT;
527 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
529 msg("<verbose> looking for font %s", fontName->getCString());
531 char*name = fontName->getCString();
533 /* see if it is a pdf standard font */
535 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
536 if(!strcmp(name, pdf2t1map[t].pdffont)) {
537 if(!pdf2t1map[t].fullfilename) {
538 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
539 if(!pdf2t1map[t].fullfilename) {
540 msg("<error> Couldn't save default font- is the Temp Directory writable?");
542 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
545 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
546 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
551 int bestlen = 0x7fffffff;
552 const char*bestfilename = 0;
554 #ifndef HAVE_FONTCONFIG
555 /* if we don't have fontconfig, try a simple filename-comparison approach */
556 fontfile_t*f = global_fonts;
558 if(strstr(f->filename, name)) {
559 if(f->len < bestlen) {
561 bestfilename = f->filename;
568 /* if we didn't find anything up to now, try looking for the
569 font via fontconfig */
572 filename = fontconfig_searchForFont(name);
574 filename = strdup(bestfilename);
578 msg("<verbose> Font %s maps to %s\n", name, filename);
579 DisplayFontParamKind kind = detectFontType(filename);
580 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
581 if(kind == displayFontTT) {
582 dfp->tt.fileName = new GString(filename);
584 dfp->t1.fileName = new GString(filename);
589 msg("<verbose> Font %s not found\n", name);
590 return GlobalParams::getDisplayFont(fontName);
594 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
597 gfxglobals = new GFXOutputGlobals();
601 this->xref = doc->getXRef();
603 this->type3active = 0;
606 this->user_movex = 0;
607 this->user_movey = 0;
610 this->user_clipx1 = 0;
611 this->user_clipy1 = 0;
612 this->user_clipx2 = 0;
613 this->user_clipy2 = 0;
614 this->current_gfxfont = 0;
615 this->current_fontinfo = 0;
616 this->current_text_stroke = 0;
617 this->current_text_clip = 0;
618 this->outer_clip_box = 0;
619 this->config_bigchar=0;
620 this->config_convertgradients=1;
621 this->config_break_on_warning=0;
622 this->config_remapunicode=0;
623 this->config_transparent=0;
624 this->config_extrafontdata = 0;
625 this->config_drawonlyshapes = 0;
626 this->config_disable_polygon_conversion = 0;
627 this->config_multiply = 1;
631 memset(states, 0, sizeof(states));
634 void GFXOutputDev::setParameter(const char*key, const char*value)
636 if(!strcmp(key,"breakonwarning")) {
637 this->config_break_on_warning = atoi(value);
638 } else if(!strcmp(key,"remapunicode")) {
639 this->config_remapunicode = atoi(value);
640 } else if(!strcmp(key,"transparent")) {
641 this->config_transparent = atoi(value);
642 } else if(!strcmp(key,"drawonlyshapes")) {
643 this->config_drawonlyshapes = atoi(value);
644 } else if(!strcmp(key,"extrafontdata")) {
645 this->config_extrafontdata = atoi(value);
646 } else if(!strcmp(key,"convertgradients")) {
647 this->config_convertgradients = atoi(value);
648 } else if(!strcmp(key,"multiply")) {
649 this->config_multiply = atoi(value);
650 if(this->config_multiply<1)
651 this->config_multiply=1;
652 } else if(!strcmp(key,"disable_polygon_conversion")) {
653 this->config_disable_polygon_conversion = atoi(value);
657 void GFXOutputDev::setDevice(gfxdevice_t*dev)
662 void GFXOutputDev::setMove(int x,int y)
664 this->user_movex = x;
665 this->user_movey = y;
668 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
670 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
671 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
673 this->user_clipx1 = x1;
674 this->user_clipy1 = y1;
675 this->user_clipx2 = x2;
676 this->user_clipy2 = y2;
679 static char*getFontName(GfxFont*font)
682 GString*gstr = font->getName();
683 char* fname = gstr==0?0:gstr->getCString();
687 sprintf(buf, "UFONT%d", r->num);
688 fontid = strdup(buf);
690 fontid = strdup(fname);
694 char* plus = strchr(fontid, '+');
695 if(plus && plus < &fontid[strlen(fontid)-1]) {
696 fontname = strdup(plus+1);
698 fontname = strdup(fontid);
704 static void dumpFontInfo(const char*loglevel, GfxFont*font);
705 static int lastdumps[1024];
706 static int lastdumppos = 0;
711 static void showFontError(GfxFont*font, int nr)
715 for(t=0;t<lastdumppos;t++)
716 if(lastdumps[t] == r->num)
720 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
721 lastdumps[lastdumppos++] = r->num;
723 msg("<warning> The following font caused problems:");
725 msg("<warning> The following font caused problems (substituting):");
727 msg("<warning> The following Type 3 Font will be rendered as graphics:");
728 dumpFontInfo("<warning>", font);
731 static void dumpFontInfo(const char*loglevel, GfxFont*font)
733 char* id = getFontID(font);
734 char* name = getFontName(font);
735 Ref* r=font->getID();
736 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
738 GString*gstr = font->getTag();
740 msg("%s| Tag: %s", loglevel, id);
742 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
744 GfxFontType type=font->getType();
746 case fontUnknownType:
747 msg("%s| Type: unknown",loglevel);
750 msg("%s| Type: 1",loglevel);
753 msg("%s| Type: 1C",loglevel);
756 msg("%s| Type: 3",loglevel);
759 msg("%s| Type: TrueType",loglevel);
762 msg("%s| Type: CIDType0",loglevel);
765 msg("%s| Type: CIDType0C",loglevel);
768 msg("%s| Type: CIDType2",loglevel);
773 GBool embedded = font->getEmbeddedFontID(&embRef);
775 if(font->getEmbeddedFontName()) {
776 embeddedName = font->getEmbeddedFontName()->getCString();
779 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
781 gstr = font->getExtFontFile();
783 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
785 // Get font descriptor flags.
786 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
787 if(font->isSerif()) msg("%s| is serif", loglevel);
788 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
789 if(font->isItalic()) msg("%s| is italic", loglevel);
790 if(font->isBold()) msg("%s| is bold", loglevel);
796 //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");}
797 //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");}
799 void dump_outline(gfxline_t*line)
801 /*gfxbbox_t*r = gfxline_isrectangle(line);
803 printf("is not a rectangle\n");
805 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
809 if(line->type == gfx_moveTo) {
810 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
811 } else if(line->type == gfx_lineTo) {
812 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
813 } else if(line->type == gfx_splineTo) {
814 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
820 void gfxPath_dump(GfxPath*path)
822 int num = path->getNumSubpaths();
825 for(t = 0; t < num; t++) {
826 GfxSubpath *subpath = path->getSubpath(t);
827 int subnum = subpath->getNumPoints();
829 for(s=0;s<subnum;s++) {
830 double x=subpath->getX(s);
831 double y=subpath->getY(s);
832 if(s==0 && !subpath->getCurve(s)) {
833 printf("M %f %f\n", x, y);
834 } else if(s==0 && subpath->getCurve(s)) {
835 printf("E %f %f\n", x, y);
836 } else if(subpath->getCurve(s)) {
837 printf("C %f %f\n", x, y);
839 printf("T %f %f\n", x, y);
845 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
847 int num = path->getNumSubpaths();
850 double lastx=0,lasty=0,posx=0,posy=0;
853 msg("<warning> empty path");
857 gfxdrawer_target_gfxline(&draw);
859 for(t = 0; t < num; t++) {
860 GfxSubpath *subpath = path->getSubpath(t);
861 int subnum = subpath->getNumPoints();
862 double bx=0,by=0,cx=0,cy=0;
864 for(s=0;s<subnum;s++) {
867 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
870 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
871 draw.lineTo(&draw, lastx, lasty);
873 draw.moveTo(&draw, x,y);
878 } else if(subpath->getCurve(s) && cpos==0) {
882 } else if(subpath->getCurve(s) && cpos==1) {
890 draw.lineTo(&draw, x,y);
892 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
899 /* fix non-closed lines */
900 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
901 draw.lineTo(&draw, lastx, lasty);
903 gfxline_t*result = (gfxline_t*)draw.result(&draw);
905 gfxline_optimize(result);
910 GBool GFXOutputDev::useTilingPatternFill()
912 infofeature("tiled patterns");
913 // if(config_convertgradients)
917 GBool GFXOutputDev::useShadedFills()
919 infofeature("shaded fills");
920 if(config_convertgradients)
925 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
927 state->transform(x,y,nx,ny);
928 *nx += user_movex + clipmovex;
929 *ny += user_movey + clipmovey;
933 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
934 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
935 int paintType, Dict *resDict,
936 double *mat, double *bbox,
937 int x0, int y0, int x1, int y1,
938 double xStep, double yStep)
940 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
941 int paintType, Dict *resDict,
942 double *mat, double *bbox,
943 int x0, int y0, int x1, int y1,
944 double xStep, double yStep)
947 msg("<debug> tilingPatternFill");
948 infofeature("tiling pattern fills");
951 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
953 msg("<error> functionShadedFill not supported yet");
954 infofeature("function shaded fills");
957 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
961 colspace->getRGB(col, &rgb);
962 c.r = colToByte(rgb.r);
963 c.g = colToByte(rgb.g);
964 c.b = colToByte(rgb.b);
969 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
971 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
972 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
975 this->transformXY(state, x0,y0, &x0,&y0);
976 this->transformXY(state, x1,y1, &x1,&y1);
977 this->transformXY(state, x2,y2, &x2,&y2);
982 shading->getColor(0.0, &color0);
983 shading->getColor(0.5, &color1);
984 shading->getColor(1.0, &color2);
986 GfxColorSpace* colspace = shading->getColorSpace();
988 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
989 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
990 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
991 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
992 infofeature("radial shaded fills");
994 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
998 g[0].color = col2col(colspace, &color0);
999 g[1].color = col2col(colspace, &color1);
1000 g[2].color = col2col(colspace, &color2);
1005 gfxbbox_t b = states[statepos].clipbbox;
1006 gfxline_t p1,p2,p3,p4,p5;
1007 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1008 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1009 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1010 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1011 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1014 //m.m00 = (x3-x0); m.m10 = (x1-x0);
1015 //m.m01 = (y3-y0); m.m11 = (y1-y0);
1016 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1017 m.m00 = (x1-x0); m.m10 = (x2-x0);
1018 m.m01 = (y1-y0); m.m11 = (y2-y0);
1022 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1026 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1029 shading->getCoords(&x0,&y0,&x1,&y1);
1030 this->transformXY(state, x0,y0,&x0,&y0);
1031 this->transformXY(state, x1,y1,&x1,&y1);
1036 shading->getColor(0.0, &color0);
1037 shading->getColor(0.5, &color1);
1038 shading->getColor(1.0, &color2);
1040 GfxColorSpace* colspace = shading->getColorSpace();
1042 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1043 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1044 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1045 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1047 infofeature("axial shaded fills");
1049 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1053 g[0].color = col2col(colspace, &color0);
1054 g[1].color = col2col(colspace, &color1);
1055 g[2].color = col2col(colspace, &color2);
1060 double xMin,yMin,xMax,yMax;
1061 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1062 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1063 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1066 xMin = 1024; yMin = 1024;
1068 gfxbbox_t b = states[statepos].clipbbox;
1069 gfxline_t p1,p2,p3,p4,p5;
1070 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1071 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1072 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1073 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1074 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1076 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1077 the middle of the two control points */
1079 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1080 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1081 m.tx = (x0 + x1)/2 - 0.5;
1082 m.ty = (y0 + y1)/2 - 0.5;
1084 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1090 GBool GFXOutputDev::useDrawForm()
1092 infofeature("forms");
1095 void GFXOutputDev::drawForm(Ref id)
1097 msg("<error> drawForm not implemented");
1099 GBool GFXOutputDev::needNonText()
1103 void GFXOutputDev::endPage()
1105 msg("<verbose> endPage (GfxOutputDev)");
1106 if(outer_clip_box) {
1107 device->endclip(device);
1110 /* notice: we're not fully done yet with this page- there might still be
1111 a few calls to drawLink() yet to come */
1114 static inline double sqr(double x) {return x*x;}
1116 #define STROKE_FILL 1
1117 #define STROKE_CLIP 2
1118 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1120 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1121 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1122 double miterLimit = state->getMiterLimit();
1123 double width = state->getTransformedLineWidth();
1126 double opaq = state->getStrokeOpacity();
1128 state->getFillRGB(&rgb);
1130 state->getStrokeRGB(&rgb);
1132 col.r = colToByte(rgb.r);
1133 col.g = colToByte(rgb.g);
1134 col.b = colToByte(rgb.b);
1135 col.a = (unsigned char)(opaq*255);
1137 gfx_capType capType = gfx_capRound;
1138 if(lineCap == 0) capType = gfx_capButt;
1139 else if(lineCap == 1) capType = gfx_capRound;
1140 else if(lineCap == 2) capType = gfx_capSquare;
1141 else msg("<error> Invalid line cap type");
1143 gfx_joinType joinType = gfx_joinRound;
1144 if(lineJoin == 0) joinType = gfx_joinMiter;
1145 else if(lineJoin == 1) joinType = gfx_joinRound;
1146 else if(lineJoin == 2) joinType = gfx_joinBevel;
1147 else msg("<error> Invalid line join type");
1149 gfxline_t*line2 = 0;
1151 int dashLength = states[statepos].dashLength;
1152 double*dashPattern = states[statepos].dashPattern;
1153 double dashStart = states[statepos].dashStart;
1154 if(dashLength && dashPattern) {
1155 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1158 /* try to find out how much the transformation matrix would
1159 stretch the dashes, and factor that into the dash lengths.
1160 This is not the entirely correct approach- it would be
1161 better to first convert the path to an unscaled version,
1162 then apply dashing, and then transform the path using
1163 the current transformation matrix. However there are few
1164 PDFs which actually stretch a dashed path in a non-orthonormal
1166 double tx1, ty1, tx2, ty2, tx3, ty3;
1167 this->transformXY(state, 0, 0, &tx1, &ty1);
1168 this->transformXY(state, 0, 1, &tx2, &ty2);
1169 this->transformXY(state, 1, 0, &tx3, &ty3);
1170 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1171 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1173 warnfeature("non-ortogonally dashed strokes", 0);
1174 double f = (d1+d2)/2;
1176 msg("<trace> %d dashes", dashLength);
1177 msg("<trace> | phase: %f", dashStart);
1178 for(t=0;t<dashLength;t++) {
1179 dash[t] = (float)dashPattern[t] * f;
1183 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1185 dash[dashLength] = -1;
1186 if(getLogLevel() >= LOGLEVEL_TRACE) {
1190 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1194 msg("<trace> After dashing:");
1197 if(getLogLevel() >= LOGLEVEL_TRACE) {
1198 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1200 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1201 lineCap==0?"butt": (lineCap==1?"round":"square"),
1203 col.r,col.g,col.b,col.a
1208 if(flags&STROKE_FILL) {
1209 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1210 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1211 if(getLogLevel() >= LOGLEVEL_TRACE) {
1212 dump_outline(gfxline);
1215 msg("<warning> Empty polygon (resulting from stroked line)");
1217 if(flags&STROKE_CLIP) {
1218 device->startclip(device, gfxline);
1219 states[statepos].clipping++;
1221 device->fill(device, gfxline, &col);
1223 gfxline_free(gfxline);
1224 gfxpoly_destroy(poly);
1226 if(flags&STROKE_CLIP)
1227 msg("<error> Stroke&clip not supported at the same time");
1228 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1232 gfxline_free(line2);
1235 gfxcolor_t getFillColor(GfxState * state)
1238 double opaq = state->getFillOpacity();
1239 state->getFillRGB(&rgb);
1241 col.r = colToByte(rgb.r);
1242 col.g = colToByte(rgb.g);
1243 col.b = colToByte(rgb.b);
1244 col.a = (unsigned char)(opaq*255);
1248 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1250 gfxcolor_t col = getFillColor(state);
1252 if(getLogLevel() >= LOGLEVEL_TRACE) {
1253 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1256 device->fill(device, line, &col);
1259 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1261 if(getLogLevel() >= LOGLEVEL_TRACE) {
1262 msg("<trace> %sclip", evenodd?"eo":"");
1265 gfxbbox_t bbox = gfxline_getbbox(line);
1266 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1268 device->startclip(device, line);
1269 states[statepos].clipping++;
1272 void GFXOutputDev::clip(GfxState *state)
1274 GfxPath * path = state->getPath();
1275 msg("<trace> clip");
1276 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1277 if(!config_disable_polygon_conversion) {
1278 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1282 clipToGfxLine(state, line, 0);
1286 void GFXOutputDev::eoClip(GfxState *state)
1288 GfxPath * path = state->getPath();
1289 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1290 clipToGfxLine(state, line, 1);
1293 void GFXOutputDev::clipToStrokePath(GfxState *state)
1295 GfxPath * path = state->getPath();
1296 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1298 if(getLogLevel() >= LOGLEVEL_TRACE) {
1299 double width = state->getTransformedLineWidth();
1300 msg("<trace> cliptostrokepath width=%f", width);
1304 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1308 void GFXOutputDev::finish()
1310 if(outer_clip_box) {
1312 device->endclip(device);
1318 GFXOutputDev::~GFXOutputDev()
1322 GBool GFXOutputDev::upsideDown()
1326 GBool GFXOutputDev::useDrawChar()
1331 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1332 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1334 static char tmp_printstr[4096];
1335 char* makeStringPrintable(char*str)
1337 int len = strlen(str);
1344 for(t=0;t<len;t++) {
1349 tmp_printstr[t] = c;
1352 tmp_printstr[len++] = '.';
1353 tmp_printstr[len++] = '.';
1354 tmp_printstr[len++] = '.';
1356 tmp_printstr[len] = 0;
1357 return tmp_printstr;
1359 void GFXOutputDev::updateFontMatrix(GfxState*state)
1361 double* ctm = state->getCTM();
1362 double fontSize = state->getFontSize();
1363 double*textMat = state->getTextMat();
1365 /* taking the absolute value of horizScaling seems to be required for
1366 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1367 double hscale = fabs(state->getHorizScaling());
1369 // from xpdf-3.02/SplashOutputDev:updateFont
1370 double mm11 = textMat[0] * fontSize * hscale;
1371 double mm12 = textMat[1] * fontSize * hscale;
1372 double mm21 = textMat[2] * fontSize;
1373 double mm22 = textMat[3] * fontSize;
1375 // multiply with ctm, like state->getFontTransMat() does
1376 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1377 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1378 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1379 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1380 this->current_font_matrix.tx = 0;
1381 this->current_font_matrix.ty = 0;
1384 void GFXOutputDev::beginString(GfxState *state, GString *s)
1386 int render = state->getRender();
1387 if(current_text_stroke) {
1388 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1390 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1393 static gfxline_t* mkEmptyGfxShape(double x, double y)
1395 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1396 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1400 static char isValidUnicode(int c)
1402 if(c>=32 && c<0x2fffe)
1407 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1408 double dx, double dy,
1409 double originX, double originY,
1410 CharCode charid, int nBytes, Unicode *_u, int uLen)
1412 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1413 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1417 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1419 int render = state->getRender();
1420 gfxcolor_t col = getFillColor(state);
1422 // check for invisible text -- this is used by Acrobat Capture
1423 if (render == RENDER_INVISIBLE) {
1425 if(!config_extrafontdata)
1429 GfxFont*font = state->getFont();
1431 if(font->getType() == fontType3) {
1432 /* type 3 chars are passed as graphics */
1433 msg("<debug> type3 char at %f/%f", x, y);
1437 Unicode u = uLen?(_u[0]):0;
1439 gfxmatrix_t m = this->current_font_matrix;
1440 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1441 //m.tx += originX; m.ty += originY;
1443 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);
1445 if((render == RENDER_FILL && !config_drawonlyshapes) || render == RENDER_INVISIBLE) {
1446 int space = this->current_fontinfo->space_char;
1447 if(config_extrafontdata && space>=0 && m.m00 && !m.m01) {
1448 /* space char detection */
1449 if(last_char_gfxfont == current_gfxfont &&
1450 last_char_y == m.ty &&
1451 !last_char_was_space) {
1452 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1453 int space = this->current_fontinfo->space_char;
1454 if(m.tx - expected_x >= m.m00*64) {
1455 msg("<debug> There's a %f (%f) pixel gap between char %d and char %d, I'm inserting a space here",
1457 (m.tx-expected_x)/m.m00,
1458 last_char, glyphid);
1460 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1461 if(m2.tx < expected_x) m2.tx = expected_x;
1462 device->drawchar(device, current_gfxfont, space, &col, &m2);
1465 last_char_gfxfont = current_gfxfont;
1466 last_char = glyphid;
1469 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1471 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1473 msg("<debug> Drawing glyph %d as shape", charid);
1474 if(!gfxglobals->textmodeinfo) {
1475 msg("<notice> Some texts will be rendered as shape");
1476 gfxglobals->textmodeinfo = 1;
1478 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1479 gfxline_t*tglyph = gfxline_clone(glyph);
1480 gfxline_transform(tglyph, &m);
1481 if((render&3) != RENDER_INVISIBLE) {
1482 gfxline_t*add = gfxline_clone(tglyph);
1483 current_text_stroke = gfxline_append(current_text_stroke, add);
1485 if(render&RENDER_CLIP) {
1486 gfxline_t*add = gfxline_clone(tglyph);
1487 current_text_clip = gfxline_append(current_text_clip, add);
1488 if(!current_text_clip) {
1489 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1492 gfxline_free(tglyph);
1496 void GFXOutputDev::endString(GfxState *state)
1498 int render = state->getRender();
1499 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1501 if(current_text_stroke) {
1502 /* fillstroke and stroke text rendering objects we can process right
1503 now (as there may be texts of other rendering modes in this
1504 text object)- clipping objects have to wait until endTextObject,
1506 device->setparameter(device, "mark","TXT");
1507 if((render&3) == RENDER_FILL) {
1508 fillGfxLine(state, current_text_stroke, 0);
1509 gfxline_free(current_text_stroke);
1510 current_text_stroke = 0;
1511 } else if((render&3) == RENDER_FILLSTROKE) {
1512 fillGfxLine(state, current_text_stroke, 0);
1513 strokeGfxline(state, current_text_stroke,0);
1514 gfxline_free(current_text_stroke);
1515 current_text_stroke = 0;
1516 } else if((render&3) == RENDER_STROKE) {
1517 strokeGfxline(state, current_text_stroke,0);
1518 gfxline_free(current_text_stroke);
1519 current_text_stroke = 0;
1521 device->setparameter(device, "mark","");
1525 void GFXOutputDev::endTextObject(GfxState *state)
1527 int render = state->getRender();
1528 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1530 if(current_text_clip) {
1531 device->setparameter(device, "mark","TXT");
1532 clipToGfxLine(state, current_text_clip, 0);
1533 device->setparameter(device, "mark","");
1534 gfxline_free(current_text_clip);
1535 current_text_clip = 0;
1539 /* the logic seems to be as following:
1540 first, beginType3Char is called, with the charcode and the coordinates.
1541 if this function returns true, it already knew about the char and has now drawn it.
1542 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1543 called with some parameters.
1544 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1545 at the position first passed to beginType3Char). the char ends with endType3Char.
1547 The drawing operations between beginType3Char and endType3Char are somewhat different to
1548 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1549 color determines the color of a font)
1552 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1554 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1557 if(config_extrafontdata && current_fontinfo) {
1559 gfxmatrix_t m = this->current_font_matrix;
1560 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1561 m.m00*=INTERNAL_FONT_SIZE;
1562 m.m01*=INTERNAL_FONT_SIZE;
1563 m.m10*=INTERNAL_FONT_SIZE;
1564 m.m11*=INTERNAL_FONT_SIZE;
1566 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1567 msg("<error> Invalid charid %d for font", charid);
1570 gfxcolor_t col={0,0,0,0};
1571 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1572 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1576 /* the character itself is going to be passed using the draw functions */
1577 return gFalse; /* gTrue= is_in_cache? */
1580 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1582 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1585 void GFXOutputDev::endType3Char(GfxState *state)
1588 msg("<debug> endType3Char");
1591 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1593 this->currentpage = pageNum;
1595 int rot = doc->getPageRotate(1);
1596 gfxcolor_t white = {255,255,255,255};
1597 gfxcolor_t black = {255,0,0,0};
1599 gfxline_t clippath[5];
1601 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1602 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1603 Use CropBox, not MediaBox, as page size
1610 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1611 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1613 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1614 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1616 this->clipmovex = -(int)x1;
1617 this->clipmovey = -(int)y1;
1619 /* apply user clip box */
1620 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1621 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1622 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1623 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1624 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1625 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1627 x1 += this->clipmovex;
1628 y1 += this->clipmovey;
1629 x2 += this->clipmovex;
1630 y2 += this->clipmovey;
1633 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1635 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);
1637 msg("<verbose> page is rotated %d degrees", rot);
1639 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1640 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1641 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1642 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1643 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1644 device->startclip(device, clippath); outer_clip_box = 1;
1645 if(!config_transparent) {
1646 device->fill(device, clippath, &white);
1648 states[statepos].clipbbox.xmin = x1;
1649 states[statepos].clipbbox.ymin = x1;
1650 states[statepos].clipbbox.xmax = x2;
1651 states[statepos].clipbbox.ymax = y2;
1653 states[statepos].dashPattern = 0;
1654 states[statepos].dashLength = 0;
1655 states[statepos].dashStart = 0;
1657 this->last_char_gfxfont = 0;
1661 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1663 double x1, y1, x2, y2;
1664 gfxline_t points[5];
1667 msg("<debug> drawlink");
1669 link->getRect(&x1, &y1, &x2, &y2);
1670 cvtUserToDev(x1, y1, &x, &y);
1671 points[0].type = gfx_moveTo;
1672 points[0].x = points[4].x = x + user_movex + clipmovex;
1673 points[0].y = points[4].y = y + user_movey + clipmovey;
1674 points[0].next = &points[1];
1675 cvtUserToDev(x2, y1, &x, &y);
1676 points[1].type = gfx_lineTo;
1677 points[1].x = x + user_movex + clipmovex;
1678 points[1].y = y + user_movey + clipmovey;
1679 points[1].next = &points[2];
1680 cvtUserToDev(x2, y2, &x, &y);
1681 points[2].type = gfx_lineTo;
1682 points[2].x = x + user_movex + clipmovex;
1683 points[2].y = y + user_movey + clipmovey;
1684 points[2].next = &points[3];
1685 cvtUserToDev(x1, y2, &x, &y);
1686 points[3].type = gfx_lineTo;
1687 points[3].x = x + user_movex + clipmovex;
1688 points[3].y = y + user_movey + clipmovey;
1689 points[3].next = &points[4];
1690 cvtUserToDev(x1, y1, &x, &y);
1691 points[4].type = gfx_lineTo;
1692 points[4].x = x + user_movex + clipmovex;
1693 points[4].y = y + user_movey + clipmovey;
1696 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1697 points[0].x, points[0].y,
1698 points[1].x, points[1].y,
1699 points[2].x, points[2].y,
1700 points[3].x, points[3].y);
1702 if(getLogLevel() >= LOGLEVEL_TRACE) {
1703 dump_outline(points);
1706 LinkAction*action=link->getAction();
1709 const char*type = "-?-";
1712 msg("<trace> drawlink action=%d", action->getKind());
1713 switch(action->getKind())
1717 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1718 LinkDest *dest=NULL;
1719 if (ha->getDest()==NULL)
1720 dest=catalog->findDest(ha->getNamedDest());
1722 dest=ha->getDest()->copy();
1724 if (dest->isPageRef()){
1725 Ref pageref=dest->getPageRef();
1726 page=catalog->findPage(pageref.num,pageref.gen);
1728 else page=dest->getPageNum();
1729 sprintf(buf, "%d", page);
1737 LinkGoToR*l = (LinkGoToR*)action;
1738 GString*g = l->getFileName();
1740 s = strdup(g->getCString());
1742 /* if the GoToR link has no filename, then
1743 try to find a refernce in the *local*
1745 GString*g = l->getNamedDest();
1747 s = strdup(g->getCString());
1753 LinkNamed*l = (LinkNamed*)action;
1754 GString*name = l->getName();
1756 s = strdup(name->lowerCase()->getCString());
1757 named = name->getCString();
1760 if(strstr(s, "next") || strstr(s, "forward"))
1762 page = currentpage + 1;
1764 else if(strstr(s, "prev") || strstr(s, "back"))
1766 page = currentpage - 1;
1768 else if(strstr(s, "last") || strstr(s, "end"))
1770 if(this->page2page && this->num_pages) {
1771 page = this->page2page[this->num_pages-1];
1774 else if(strstr(s, "first") || strstr(s, "top"))
1782 case actionLaunch: {
1784 LinkLaunch*l = (LinkLaunch*)action;
1785 GString * str = new GString(l->getFileName());
1786 GString * params = l->getParams();
1788 str->append(params);
1789 s = strdup(str->getCString());
1796 LinkURI*l = (LinkURI*)action;
1797 GString*g = l->getURI();
1799 url = g->getCString();
1804 case actionUnknown: {
1806 LinkUnknown*l = (LinkUnknown*)action;
1811 msg("<error> Unknown link type!");
1816 if(!s) s = strdup("-?-");
1818 msg("<trace> drawlink s=%s", s);
1820 if(!gfxglobals->linkinfo && (page || s))
1822 msg("<notice> File contains links");
1823 gfxglobals->linkinfo = 1;
1829 for(t=1;t<=this->num_pages;t++) {
1830 if(this->page2page[t]==page) {
1840 sprintf(buf, "page%d", lpage);
1841 device->drawlink(device, points, buf);
1845 device->drawlink(device, points, s);
1848 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1852 void GFXOutputDev::saveState(GfxState *state) {
1853 dbg("saveState %08x", state); dbgindent+=2;
1855 msg("<trace> saveState %08x", state);
1858 msg("<fatal> Too many nested states in pdf.");
1862 states[statepos].state = state;
1863 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1864 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1865 states[statepos].clipping = 0;
1866 states[statepos].olddevice = 0;
1867 states[statepos].clipbbox = states[statepos-1].clipbbox;
1869 states[statepos].dashPattern = states[statepos-1].dashPattern;
1870 states[statepos].dashStart = states[statepos-1].dashStart;
1871 states[statepos].dashLength = states[statepos-1].dashLength;
1874 void GFXOutputDev::restoreState(GfxState *state) {
1875 dbgindent-=2; dbg("restoreState %08x", state);
1878 msg("<fatal> Invalid restoreState");
1881 msg("<trace> restoreState %08x%s%s", state,
1882 states[statepos].softmask?" (end softmask)":"",
1883 states[statepos].clipping?" (end clipping)":"");
1884 if(states[statepos].softmask) {
1885 clearSoftMask(state);
1888 if(states[statepos].dashPattern) {
1889 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1890 free(states[statepos].dashPattern);
1891 states[statepos].dashPattern = 0;
1897 while(states[statepos].clipping) {
1898 device->endclip(device);
1899 states[statepos].clipping--;
1901 if(states[statepos].state!=state) {
1902 msg("<fatal> bad state nesting");
1905 for(t=0;t<=statepos;t++) {
1906 printf("%08x ", states[t].state);
1912 states[statepos].state=0;
1916 void GFXOutputDev::updateLineDash(GfxState *state)
1918 if(states[statepos].dashPattern &&
1919 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1920 free(states[statepos].dashPattern);
1921 states[statepos].dashPattern = 0;
1923 double *pattern = 0;
1926 state->getLineDash(&pattern, &dashLength, &dashStart);
1927 msg("<debug> updateLineDash, %d dashes", dashLength);
1929 states[statepos].dashPattern = 0;
1930 states[statepos].dashLength = 0;
1932 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1933 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1934 states[statepos].dashPattern = p;
1935 states[statepos].dashLength = dashLength;
1936 states[statepos].dashStart = dashStart;
1940 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1942 this->page2page = page2page;
1943 this->num_pages = num_pages;
1946 void GFXOutputDev::updateLineWidth(GfxState *state)
1948 double width = state->getTransformedLineWidth();
1951 void GFXOutputDev::updateLineCap(GfxState *state)
1953 int c = state->getLineCap();
1956 void GFXOutputDev::updateLineJoin(GfxState *state)
1958 int j = state->getLineJoin();
1961 void GFXOutputDev::updateFillColor(GfxState *state)
1964 double opaq = state->getFillOpacity();
1965 state->getFillRGB(&rgb);
1967 void GFXOutputDev::updateFillOpacity(GfxState *state)
1970 double opaq = state->getFillOpacity();
1971 state->getFillRGB(&rgb);
1972 dbg("update fillopaq %f", opaq);
1974 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1976 double opaq = state->getFillOpacity();
1977 dbg("update strokeopaq %f", opaq);
1979 void GFXOutputDev::updateFillOverprint(GfxState *state)
1981 double opaq = state->getFillOverprint();
1982 dbg("update filloverprint %f", opaq);
1984 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1986 double opaq = state->getStrokeOverprint();
1987 dbg("update strokeoverprint %f", opaq);
1989 void GFXOutputDev::updateTransfer(GfxState *state)
1991 dbg("update transfer");
1995 void GFXOutputDev::updateStrokeColor(GfxState *state)
1998 double opaq = state->getStrokeOpacity();
1999 state->getStrokeRGB(&rgb);
2002 void GFXOutputDev::updateFont(GfxState *state)
2004 GfxFont* gfxFont = state->getFont();
2008 char*id = getFontID(gfxFont);
2009 msg("<verbose> Updating font to %s", id);
2010 if(gfxFont->getType() == fontType3) {
2011 infofeature("Type3 fonts");
2012 if(!config_extrafontdata) {
2017 msg("<error> Internal Error: FontID is null");
2021 this->current_fontinfo = this->info->getFont(id);
2023 if(!this->current_fontinfo) {
2024 msg("<error> Internal Error: no fontinfo for font %s", id);
2027 if(!this->current_fontinfo->seen) {
2028 dumpFontInfo("<verbose>", gfxFont);
2031 current_gfxfont = this->current_fontinfo->getGfxFont();
2032 device->addfont(device, current_gfxfont);
2035 updateFontMatrix(state);
2038 #define SQR(x) ((x)*(x))
2040 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2042 if((newwidth<1 || newheight<1) ||
2043 (width<=newwidth || height<=newheight))
2045 unsigned char*newdata;
2047 newdata= (unsigned char*)malloc(newwidth*newheight);
2048 double fx = ((double)width)/newwidth;
2049 double fy = ((double)height)/newheight;
2051 int blocksize = (int)(8192/(fx*fy));
2052 int r = 8192*256/palettesize;
2053 for(x=0;x<newwidth;x++) {
2054 double ex = px + fx;
2055 int fromx = (int)px;
2057 int xweight1 = (int)((1-(px-fromx))*256);
2058 int xweight2 = (int)((ex-tox)*256);
2060 for(y=0;y<newheight;y++) {
2061 double ey = py + fy;
2062 int fromy = (int)py;
2064 int yweight1 = (int)((1-(py-fromy))*256);
2065 int yweight2 = (int)((ey-toy)*256);
2072 for(xx=fromx;xx<=tox;xx++)
2073 for(yy=fromy;yy<=toy;yy++) {
2074 int b = 1-data[width*yy+xx];
2076 if(xx==fromx) weight = (weight*xweight1)/256;
2077 if(xx==tox) weight = (weight*xweight2)/256;
2078 if(yy==fromy) weight = (weight*yweight1)/256;
2079 if(yy==toy) weight = (weight*yweight2)/256;
2082 //if(a) a=(palettesize-1)*r/blocksize;
2083 newdata[y*newwidth+x] = (a*blocksize)/r;
2091 #define IMAGE_TYPE_JPEG 0
2092 #define IMAGE_TYPE_LOSSLESS 1
2094 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2095 double x1,double y1,
2096 double x2,double y2,
2097 double x3,double y3,
2098 double x4,double y4, int type, int multiply)
2100 gfxcolor_t*newpic=0;
2102 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2103 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2105 gfxline_t p1,p2,p3,p4,p5;
2106 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2107 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2108 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2109 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2110 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2112 {p1.x = (int)(p1.x*20)/20.0;
2113 p1.y = (int)(p1.y*20)/20.0;
2114 p2.x = (int)(p2.x*20)/20.0;
2115 p2.y = (int)(p2.y*20)/20.0;
2116 p3.x = (int)(p3.x*20)/20.0;
2117 p3.y = (int)(p3.y*20)/20.0;
2118 p4.x = (int)(p4.x*20)/20.0;
2119 p4.y = (int)(p4.y*20)/20.0;
2120 p5.x = (int)(p5.x*20)/20.0;
2121 p5.y = (int)(p5.y*20)/20.0;
2125 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2126 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2128 m.tx = p1.x - 0.5*multiply;
2129 m.ty = p1.y - 0.5*multiply;
2132 img.data = (gfxcolor_t*)data;
2136 if(type == IMAGE_TYPE_JPEG)
2137 /* TODO: pass image_dpi to device instead */
2138 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2141 dev->fillbitmap(dev, &p1, &img, &m, 0);
2144 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2145 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2147 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2150 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2151 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2153 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2157 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2158 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2159 GBool inlineImg, int mask, int*maskColors,
2160 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2162 /* the code in this function is *old*. It's not pretty, but it works. */
2164 double x1,y1,x2,y2,x3,y3,x4,y4;
2165 ImageStream *imgStr;
2170 unsigned char* maskbitmap = 0;
2173 ncomps = colorMap->getNumPixelComps();
2174 bits = colorMap->getBits();
2179 unsigned char buf[8];
2180 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2182 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2183 imgMaskStr->reset();
2184 unsigned char pal[256];
2185 int n = 1 << colorMap->getBits();
2190 maskColorMap->getGray(pixBuf, &gray);
2191 pal[t] = colToByte(gray);
2193 for (y = 0; y < maskHeight; y++) {
2194 for (x = 0; x < maskWidth; x++) {
2195 imgMaskStr->getPixel(buf);
2196 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2201 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2202 imgMaskStr->reset();
2203 for (y = 0; y < maskHeight; y++) {
2204 for (x = 0; x < maskWidth; x++) {
2205 imgMaskStr->getPixel(buf);
2207 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2215 imgStr = new ImageStream(str, width, ncomps,bits);
2218 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2220 msg("<verbose> Ignoring %d by %d image", width, height);
2221 unsigned char buf[8];
2223 for (y = 0; y < height; ++y)
2224 for (x = 0; x < width; ++x) {
2225 imgStr->getPixel(buf);
2233 this->transformXY(state, 0, 1, &x1, &y1);
2234 this->transformXY(state, 0, 0, &x2, &y2);
2235 this->transformXY(state, 1, 0, &x3, &y3);
2236 this->transformXY(state, 1, 1, &x4, &y4);
2239 /* as type 3 bitmaps are antialized, we need to place them
2240 at integer coordinates, otherwise flash player's antializing
2241 will kick in and make everything blurry */
2242 x1 = (int)(x1);y1 = (int)(y1);
2243 x2 = (int)(x2);y2 = (int)(y2);
2244 x3 = (int)(x3);y3 = (int)(y3);
2245 x4 = (int)(x4);y4 = (int)(y4);
2248 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2250 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2251 gfxglobals->pbminfo = 1;
2254 msg("<verbose> drawing %d by %d masked picture", width, height);
2256 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2257 msg("<notice> File contains jpeg pictures");
2258 gfxglobals->jpeginfo = 1;
2262 unsigned char buf[8];
2264 unsigned char*pic = new unsigned char[width*height];
2265 gfxcolor_t pal[256];
2267 state->getFillRGB(&rgb);
2269 memset(pal,255,sizeof(pal));
2270 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2271 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2272 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2273 pal[0].a = 255; pal[1].a = 0;
2276 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2277 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2278 for (y = 0; y < height; ++y)
2279 for (x = 0; x < width; ++x)
2281 imgStr->getPixel(buf);
2284 pic[width*y+x] = buf[0];
2288 unsigned char*pic2 = 0;
2291 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2300 height = realheight;
2304 /* make a black/white palette */
2306 float r = 255./(float)(numpalette-1);
2308 for(t=0;t<numpalette;t++) {
2309 pal[t].r = colToByte(rgb.r);
2310 pal[t].g = colToByte(rgb.g);
2311 pal[t].b = colToByte(rgb.b);
2312 pal[t].a = (unsigned char)(t*r);
2317 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2318 for (y = 0; y < height; ++y) {
2319 for (x = 0; x < width; ++x) {
2320 pic2[width*y+x] = pal[pic[y*width+x]];
2323 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2327 if(maskbitmap) free(maskbitmap);
2333 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2334 gfxcolor_t*pic=new gfxcolor_t[width*height];
2335 for (y = 0; y < height; ++y) {
2336 for (x = 0; x < width; ++x) {
2337 imgStr->getPixel(pixBuf);
2338 colorMap->getRGB(pixBuf, &rgb);
2339 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2340 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2341 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2342 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2344 int x1 = x*maskWidth/width;
2345 int y1 = y*maskHeight/height;
2346 int x2 = (x+1)*maskWidth/width;
2347 int y2 = (y+1)*maskHeight/height;
2349 unsigned int alpha=0;
2350 unsigned int count=0;
2351 for(xx=x1;xx<x2;xx++)
2352 for(yy=y1;yy<y2;yy++) {
2353 alpha += maskbitmap[yy*maskWidth+xx];
2357 pic[width*y+x].a = alpha / count;
2359 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2364 if(str->getKind()==strDCT)
2365 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2367 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2370 if(maskbitmap) free(maskbitmap);
2373 gfxcolor_t*pic=new gfxcolor_t[width*height];
2374 gfxcolor_t pal[256];
2375 int n = 1 << colorMap->getBits();
2377 for(t=0;t<256;t++) {
2379 colorMap->getRGB(pixBuf, &rgb);
2381 {/*if(maskColors && *maskColors==t) {
2382 msg("<notice> Color %d is transparent", t);
2383 if (imgData->maskColors) {
2385 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2386 if (pix[i] < imgData->maskColors[2*i] ||
2387 pix[i] > imgData->maskColors[2*i+1]) {
2402 pal[t].r = (unsigned char)(colToByte(rgb.r));
2403 pal[t].g = (unsigned char)(colToByte(rgb.g));
2404 pal[t].b = (unsigned char)(colToByte(rgb.b));
2405 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2408 for (y = 0; y < height; ++y) {
2409 for (x = 0; x < width; ++x) {
2410 imgStr->getPixel(pixBuf);
2411 pic[width*y+x] = pal[pixBuf[0]];
2415 if(maskWidth < width && maskHeight < height) {
2416 for(y = 0; y < height; y++) {
2417 for (x = 0; x < width; x++) {
2418 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2422 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2423 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2424 double dx = width / maskWidth;
2425 double dy = height / maskHeight;
2427 for(y = 0; y < maskHeight; y++) {
2429 for (x = 0; x < maskWidth; x++) {
2430 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2431 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2439 height = maskHeight;
2442 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2446 if(maskbitmap) free(maskbitmap);
2451 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2452 int width, int height, GBool invert,
2455 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2456 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2457 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2460 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2461 int width, int height, GfxImageColorMap *colorMap,
2462 int *maskColors, GBool inlineImg)
2464 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2465 colorMap?"colorMap":"no colorMap",
2466 maskColors?"maskColors":"no maskColors",
2468 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2469 colorMap?"colorMap":"no colorMap",
2470 maskColors?"maskColors":"no maskColors",
2473 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2474 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2475 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2478 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2479 int width, int height,
2480 GfxImageColorMap *colorMap,
2481 Stream *maskStr, int maskWidth, int maskHeight,
2484 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2485 colorMap?"colorMap":"no colorMap",
2486 maskWidth, maskHeight);
2487 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2488 colorMap?"colorMap":"no colorMap",
2489 maskWidth, maskHeight);
2491 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2492 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2493 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2496 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2497 int width, int height,
2498 GfxImageColorMap *colorMap,
2500 int maskWidth, int maskHeight,
2501 GfxImageColorMap *maskColorMap)
2503 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2504 colorMap?"colorMap":"no colorMap",
2505 maskWidth, maskHeight);
2506 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2507 colorMap?"colorMap":"no colorMap",
2508 maskWidth, maskHeight);
2510 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2511 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2512 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2515 void GFXOutputDev::stroke(GfxState *state)
2519 GfxPath * path = state->getPath();
2520 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2521 strokeGfxline(state, line, 0);
2525 void GFXOutputDev::fill(GfxState *state)
2527 gfxcolor_t col = getFillColor(state);
2528 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2530 GfxPath * path = state->getPath();
2531 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2532 if(!config_disable_polygon_conversion) {
2533 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2537 fillGfxLine(state, line, 0);
2541 void GFXOutputDev::eoFill(GfxState *state)
2543 gfxcolor_t col = getFillColor(state);
2544 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2546 GfxPath * path = state->getPath();
2547 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2548 fillGfxLine(state, line, 1);
2553 static const char* dirseparator()
2562 void addGlobalFont(const char*filename)
2564 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2565 memset(f, 0, sizeof(fontfile_t));
2566 f->filename = filename;
2567 int len = strlen(filename);
2568 char*r1 = strrchr((char*)filename, '/');
2569 char*r2 = strrchr((char*)filename, '\\');
2577 msg("<verbose> Adding font \"%s\".", filename);
2578 if(global_fonts_next) {
2579 global_fonts_next->next = f;
2580 global_fonts_next = global_fonts_next->next;
2582 global_fonts_next = global_fonts = f;
2586 void addGlobalLanguageDir(const char*dir)
2588 msg("<notice> Adding %s to language pack directories", dir);
2591 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2592 strcpy(config_file, dir);
2593 strcat(config_file, dirseparator());
2594 strcat(config_file, "add-to-xpdfrc");
2596 fi = fopen(config_file, "rb");
2598 msg("<error> Could not open %s", config_file);
2601 globalParams->parseFile(new GString(config_file), fi);
2605 void addGlobalFontDir(const char*dirname)
2607 #ifdef HAVE_DIRENT_H
2608 DIR*dir = opendir(dirname);
2610 msg("<warning> Couldn't open directory %s", dirname);
2616 ent = readdir (dir);
2620 char*name = ent->d_name;
2626 if(!strncasecmp(&name[l-4], ".pfa", 4))
2628 if(!strncasecmp(&name[l-4], ".pfb", 4))
2630 if(!strncasecmp(&name[l-4], ".ttf", 4))
2633 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2634 strcpy(fontname, dirname);
2635 strcat(fontname, dirseparator());
2636 strcat(fontname, name);
2637 addGlobalFont(fontname);
2641 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2644 msg("<warning> No dirent.h");
2648 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2649 GfxColorSpace *blendingColorSpace,
2650 GBool isolated, GBool knockout,
2653 const char*colormodename = "";
2655 if(blendingColorSpace) {
2656 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2658 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);
2659 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);
2661 //states[statepos].createsoftmask |= forSoftMask;
2662 states[statepos].createsoftmask = forSoftMask;
2663 states[statepos].transparencygroup = !forSoftMask;
2664 states[statepos].isolated = isolated;
2666 states[statepos].olddevice = this->device;
2667 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2668 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2670 gfxdevice_record_init(this->device);
2672 /*if(!forSoftMask) { ////???
2673 state->setFillOpacity(0.0);
2678 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2681 gfxdevice_t*r = this->device;
2683 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2685 this->device = states[statepos].olddevice;
2687 msg("<error> Invalid state nesting");
2689 states[statepos].olddevice = 0;
2691 gfxresult_t*recording = r->finish(r);
2693 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2694 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2696 if(states[statepos].createsoftmask) {
2697 states[statepos-1].softmaskrecording = recording;
2699 states[statepos-1].grouprecording = recording;
2702 states[statepos].createsoftmask = 0;
2703 states[statepos].transparencygroup = 0;
2707 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2709 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2710 "colordodge","colorburn","hardlight","softlight","difference",
2711 "exclusion","hue","saturation","color","luminosity"};
2713 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2714 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2716 if(state->getBlendMode() == gfxBlendNormal)
2717 infofeature("transparency groups");
2720 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2721 warnfeature(buffer, 0);
2724 gfxresult_t*grouprecording = states[statepos].grouprecording;
2726 int blendmode = state->getBlendMode();
2727 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2728 int alpha = (int)(state->getFillOpacity()*255);
2729 if(blendmode == gfxBlendMultiply && alpha>200)
2732 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2733 gfxdevice_ops_init(&ops, this->device, alpha);
2734 gfxresult_record_replay(grouprecording, &ops);
2737 grouprecording->destroy(grouprecording);
2739 states[statepos].grouprecording = 0;
2742 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2744 if(states[statepos].softmask) {
2745 /* shouldn't happen, but *does* happen */
2746 clearSoftMask(state);
2749 /* alpha = 1: retrieve mask values from alpha layer
2750 alpha = 0: retrieve mask values from luminance */
2752 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2753 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2754 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2755 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2757 infofeature("soft masks");
2759 warnfeature("soft masks from alpha channel",0);
2761 if(states[statepos].olddevice) {
2762 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2765 states[statepos].olddevice = this->device;
2766 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2767 gfxdevice_record_init(this->device);
2769 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2771 states[statepos].softmask = 1;
2772 states[statepos].softmask_alpha = alpha;
2775 static inline Guchar div255(int x) {
2776 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2779 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2781 if(c < min) c = min;
2782 if(c > max) c = max;
2786 void GFXOutputDev::clearSoftMask(GfxState *state)
2788 if(!states[statepos].softmask)
2790 states[statepos].softmask = 0;
2791 dbg("clearSoftMask statepos=%d", statepos);
2792 msg("<verbose> clearSoftMask statepos=%d", statepos);
2794 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2795 msg("<error> Error in softmask/tgroup ordering");
2799 gfxresult_t*mask = states[statepos].softmaskrecording;
2800 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2801 this->device = states[statepos].olddevice;
2803 /* get outline of all objects below the soft mask */
2804 gfxdevice_t uniondev;
2805 gfxdevice_union_init(&uniondev, 0);
2806 gfxresult_record_replay(below, &uniondev);
2807 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2808 uniondev.finish(&uniondev);
2809 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2810 gfxline_free(belowoutline);belowoutline=0;
2812 this->device->startclip(this->device, belowoutline);
2813 gfxresult_record_replay(below, this->device);
2814 gfxresult_record_replay(mask, this->device);
2815 this->device->endclip(this->device);
2818 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2819 if(width<=0 || height<=0)
2822 gfxdevice_t belowrender;
2823 gfxdevice_render_init(&belowrender);
2824 if(states[statepos+1].isolated) {
2825 belowrender.setparameter(&belowrender, "fillwhite", "1");
2827 belowrender.setparameter(&belowrender, "antialize", "2");
2828 belowrender.startpage(&belowrender, width, height);
2829 gfxresult_record_replay(below, &belowrender);
2830 belowrender.endpage(&belowrender);
2831 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2832 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2833 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2835 gfxdevice_t maskrender;
2836 gfxdevice_render_init(&maskrender);
2837 maskrender.startpage(&maskrender, width, height);
2838 gfxresult_record_replay(mask, &maskrender);
2839 maskrender.endpage(&maskrender);
2840 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2841 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2843 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2844 msg("<fatal> Internal error in mask drawing");
2849 for(y=0;y<height;y++) {
2850 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2851 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2852 for(x=0;x<width;x++) {
2854 if(states[statepos].softmask_alpha) {
2857 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2860 l2->a = div255(alpha*l2->a);
2862 /* DON'T premultiply alpha- this is done by fillbitmap,
2863 depending on the output device */
2864 //l2->r = div255(alpha*l2->r);
2865 //l2->g = div255(alpha*l2->g);
2866 //l2->b = div255(alpha*l2->b);
2872 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2875 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2876 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2878 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2880 mask->destroy(mask);
2881 below->destroy(below);
2882 maskresult->destroy(maskresult);
2883 belowresult->destroy(belowresult);
2884 states[statepos].softmaskrecording = 0;
2889 // public: ~MemCheck()
2891 // delete globalParams;globalParams=0;
2892 // Object::memCheck(stderr);
2893 // gMemReport(stderr);