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) {
452 for(t=0;t<set->nfont;t++) {
453 char*fcfamily=0,*fcstyle=0,*filename=0;
454 FcBool scalable=FcFalse, outline=FcFalse;
455 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
456 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
457 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
458 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
459 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
460 if(scalable && outline) {
461 msg("<trace> %s (%s) -> %s", fcfamily, fcstyle, filename);
465 set = FcConfigGetFonts(config, FcSetApplication);
470 char*family = strdup(name);
472 char*dash = strchr(family, '-');
473 if(!dash) dash = strchr(family, ',');
475 FcPattern*pattern = 0;
479 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
480 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
482 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
483 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
487 FcConfigSubstitute(0, pattern, FcMatchPattern);
488 FcDefaultSubstitute(pattern);
490 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
493 for(t=0;t<set->nfont;t++) {
494 FcPattern*match = set->fonts[t];
495 //FcPattern*match = FcFontMatch(0, pattern, &result);
496 if(fc_ismatch(match, family, style)) {
498 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
499 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
502 //FcPatternDestroy(match);
503 msg("<debug> fontconfig: returning filename %s", filename);
505 FcPatternDestroy(pattern);
506 FcFontSetDestroy(set);
507 return filename?strdup(filename):0;
512 FcPatternDestroy(pattern);
513 FcFontSetDestroy(set);
520 static DisplayFontParamKind detectFontType(const char*filename)
522 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
523 return displayFontTT;
524 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
525 return displayFontT1;
526 return displayFontTT;
529 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
531 msg("<verbose> looking for font %s", fontName->getCString());
533 char*name = fontName->getCString();
535 /* see if it is a pdf standard font */
537 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
538 if(!strcmp(name, pdf2t1map[t].pdffont)) {
539 if(!pdf2t1map[t].fullfilename) {
540 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
541 if(!pdf2t1map[t].fullfilename) {
542 msg("<error> Couldn't save default font- is the Temp Directory writable?");
544 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
547 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
548 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
553 int bestlen = 0x7fffffff;
554 const char*bestfilename = 0;
556 #ifndef HAVE_FONTCONFIG
557 /* if we don't have fontconfig, try a simple filename-comparison approach */
558 fontfile_t*f = global_fonts;
560 if(strstr(f->filename, name)) {
561 if(f->len < bestlen) {
563 bestfilename = f->filename;
570 /* if we didn't find anything up to now, try looking for the
571 font via fontconfig */
574 filename = fontconfig_searchForFont(name);
576 filename = strdup(bestfilename);
580 msg("<verbose> Font %s maps to %s\n", name, filename);
581 DisplayFontParamKind kind = detectFontType(filename);
582 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
583 if(kind == displayFontTT) {
584 dfp->tt.fileName = new GString(filename);
586 dfp->t1.fileName = new GString(filename);
591 msg("<verbose> Font %s not found\n", name);
592 return GlobalParams::getDisplayFont(fontName);
596 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
599 gfxglobals = new GFXOutputGlobals();
603 this->xref = doc->getXRef();
605 this->type3active = 0;
608 this->user_movex = 0;
609 this->user_movey = 0;
612 this->user_clipx1 = 0;
613 this->user_clipy1 = 0;
614 this->user_clipx2 = 0;
615 this->user_clipy2 = 0;
616 this->current_gfxfont = 0;
617 this->current_fontinfo = 0;
618 this->current_text_stroke = 0;
619 this->current_text_clip = 0;
620 this->outer_clip_box = 0;
621 this->config_bigchar=0;
622 this->config_convertgradients=1;
623 this->config_break_on_warning=0;
624 this->config_remapunicode=0;
625 this->config_transparent=0;
626 this->config_extrafontdata = 0;
627 this->config_drawonlyshapes = 0;
628 this->config_disable_polygon_conversion = 0;
629 this->config_multiply = 1;
630 this->config_linkdatafile = 0;
634 memset(states, 0, sizeof(states));
637 void GFXOutputDev::setParameter(const char*key, const char*value)
639 if(!strcmp(key,"breakonwarning")) {
640 this->config_break_on_warning = atoi(value);
641 } else if(!strcmp(key,"remapunicode")) {
642 this->config_remapunicode = atoi(value);
643 } else if(!strcmp(key,"transparent")) {
644 this->config_transparent = atoi(value);
645 } else if(!strcmp(key,"drawonlyshapes")) {
646 this->config_drawonlyshapes = atoi(value);
647 } else if(!strcmp(key,"extrafontdata")) {
648 this->config_extrafontdata = atoi(value);
649 } else if(!strcmp(key,"linkdatafile")) {
650 this->config_linkdatafile = strdup(value);
651 } else if(!strcmp(key,"convertgradients")) {
652 this->config_convertgradients = atoi(value);
653 } else if(!strcmp(key,"multiply")) {
654 this->config_multiply = atoi(value);
655 if(this->config_multiply<1)
656 this->config_multiply=1;
657 } else if(!strcmp(key,"disable_polygon_conversion")) {
658 this->config_disable_polygon_conversion = atoi(value);
662 void GFXOutputDev::setDevice(gfxdevice_t*dev)
667 void GFXOutputDev::setMove(int x,int y)
669 this->user_movex = x;
670 this->user_movey = y;
673 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
675 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
676 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
678 this->user_clipx1 = x1;
679 this->user_clipy1 = y1;
680 this->user_clipx2 = x2;
681 this->user_clipy2 = y2;
684 static char*getFontName(GfxFont*font)
687 GString*gstr = font->getName();
688 char* fname = gstr==0?0:gstr->getCString();
692 sprintf(buf, "UFONT%d", r->num);
693 fontid = strdup(buf);
695 fontid = strdup(fname);
699 char* plus = strchr(fontid, '+');
700 if(plus && plus < &fontid[strlen(fontid)-1]) {
701 fontname = strdup(plus+1);
703 fontname = strdup(fontid);
709 static void dumpFontInfo(const char*loglevel, GfxFont*font);
710 static int lastdumps[1024];
711 static int lastdumppos = 0;
716 static void showFontError(GfxFont*font, int nr)
720 for(t=0;t<lastdumppos;t++)
721 if(lastdumps[t] == r->num)
725 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
726 lastdumps[lastdumppos++] = r->num;
728 msg("<warning> The following font caused problems:");
730 msg("<warning> The following font caused problems (substituting):");
732 msg("<warning> The following Type 3 Font will be rendered as graphics:");
733 dumpFontInfo("<warning>", font);
736 static void dumpFontInfo(const char*loglevel, GfxFont*font)
738 char* id = getFontID(font);
739 char* name = getFontName(font);
740 Ref* r=font->getID();
741 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
743 GString*gstr = font->getTag();
745 msg("%s| Tag: %s", loglevel, id);
747 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
749 GfxFontType type=font->getType();
751 case fontUnknownType:
752 msg("%s| Type: unknown",loglevel);
755 msg("%s| Type: 1",loglevel);
758 msg("%s| Type: 1C",loglevel);
761 msg("%s| Type: 3",loglevel);
764 msg("%s| Type: TrueType",loglevel);
767 msg("%s| Type: CIDType0",loglevel);
770 msg("%s| Type: CIDType0C",loglevel);
773 msg("%s| Type: CIDType2",loglevel);
778 GBool embedded = font->getEmbeddedFontID(&embRef);
780 if(font->getEmbeddedFontName()) {
781 embeddedName = font->getEmbeddedFontName()->getCString();
784 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
786 gstr = font->getExtFontFile();
788 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
790 // Get font descriptor flags.
791 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
792 if(font->isSerif()) msg("%s| is serif", loglevel);
793 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
794 if(font->isItalic()) msg("%s| is italic", loglevel);
795 if(font->isBold()) msg("%s| is bold", loglevel);
801 //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");}
802 //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");}
804 void dump_outline(gfxline_t*line)
806 /*gfxbbox_t*r = gfxline_isrectangle(line);
808 printf("is not a rectangle\n");
810 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
814 if(line->type == gfx_moveTo) {
815 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
816 } else if(line->type == gfx_lineTo) {
817 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
818 } else if(line->type == gfx_splineTo) {
819 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
825 void gfxPath_dump(GfxPath*path)
827 int num = path->getNumSubpaths();
830 for(t = 0; t < num; t++) {
831 GfxSubpath *subpath = path->getSubpath(t);
832 int subnum = subpath->getNumPoints();
834 for(s=0;s<subnum;s++) {
835 double x=subpath->getX(s);
836 double y=subpath->getY(s);
837 if(s==0 && !subpath->getCurve(s)) {
838 printf("M %f %f\n", x, y);
839 } else if(s==0 && subpath->getCurve(s)) {
840 printf("E %f %f\n", x, y);
841 } else if(subpath->getCurve(s)) {
842 printf("C %f %f\n", x, y);
844 printf("T %f %f\n", x, y);
850 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
852 int num = path->getNumSubpaths();
855 double lastx=0,lasty=0,posx=0,posy=0;
858 msg("<warning> empty path");
862 gfxdrawer_target_gfxline(&draw);
864 for(t = 0; t < num; t++) {
865 GfxSubpath *subpath = path->getSubpath(t);
866 int subnum = subpath->getNumPoints();
867 double bx=0,by=0,cx=0,cy=0;
869 for(s=0;s<subnum;s++) {
872 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
875 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
876 draw.lineTo(&draw, lastx, lasty);
878 draw.moveTo(&draw, x,y);
883 } else if(subpath->getCurve(s) && cpos==0) {
887 } else if(subpath->getCurve(s) && cpos==1) {
895 draw.lineTo(&draw, x,y);
897 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
904 /* fix non-closed lines */
905 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
906 draw.lineTo(&draw, lastx, lasty);
908 gfxline_t*result = (gfxline_t*)draw.result(&draw);
910 gfxline_optimize(result);
915 GBool GFXOutputDev::useTilingPatternFill()
917 infofeature("tiled patterns");
918 // if(config_convertgradients)
922 GBool GFXOutputDev::useShadedFills()
924 infofeature("shaded fills");
925 if(config_convertgradients)
930 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
932 state->transform(x,y,nx,ny);
933 *nx += user_movex + clipmovex;
934 *ny += user_movey + clipmovey;
938 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
939 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
940 int paintType, Dict *resDict,
941 double *mat, double *bbox,
942 int x0, int y0, int x1, int y1,
943 double xStep, double yStep)
945 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
946 int paintType, Dict *resDict,
947 double *mat, double *bbox,
948 int x0, int y0, int x1, int y1,
949 double xStep, double yStep)
952 msg("<debug> tilingPatternFill");
953 infofeature("tiling pattern fills");
956 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
958 msg("<error> functionShadedFill not supported yet");
959 infofeature("function shaded fills");
962 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
966 colspace->getRGB(col, &rgb);
967 c.r = colToByte(rgb.r);
968 c.g = colToByte(rgb.g);
969 c.b = colToByte(rgb.b);
974 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
976 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
977 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
980 this->transformXY(state, x0,y0, &x0,&y0);
981 this->transformXY(state, x1,y1, &x1,&y1);
982 this->transformXY(state, x2,y2, &x2,&y2);
987 shading->getColor(0.0, &color0);
988 shading->getColor(0.5, &color1);
989 shading->getColor(1.0, &color2);
991 GfxColorSpace* colspace = shading->getColorSpace();
993 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
994 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
995 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
996 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
997 infofeature("radial shaded fills");
999 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1003 g[0].color = col2col(colspace, &color0);
1004 g[1].color = col2col(colspace, &color1);
1005 g[2].color = col2col(colspace, &color2);
1010 gfxbbox_t b = states[statepos].clipbbox;
1011 gfxline_t p1,p2,p3,p4,p5;
1012 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1013 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1014 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1015 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1016 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1019 //m.m00 = (x3-x0); m.m10 = (x1-x0);
1020 //m.m01 = (y3-y0); m.m11 = (y1-y0);
1021 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1022 m.m00 = (x1-x0); m.m10 = (x2-x0);
1023 m.m01 = (y1-y0); m.m11 = (y2-y0);
1027 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1031 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1034 shading->getCoords(&x0,&y0,&x1,&y1);
1035 this->transformXY(state, x0,y0,&x0,&y0);
1036 this->transformXY(state, x1,y1,&x1,&y1);
1041 shading->getColor(0.0, &color0);
1042 shading->getColor(0.5, &color1);
1043 shading->getColor(1.0, &color2);
1045 GfxColorSpace* colspace = shading->getColorSpace();
1047 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1048 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1049 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1050 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1052 infofeature("axial shaded fills");
1054 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1058 g[0].color = col2col(colspace, &color0);
1059 g[1].color = col2col(colspace, &color1);
1060 g[2].color = col2col(colspace, &color2);
1065 double xMin,yMin,xMax,yMax;
1066 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1067 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1068 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1071 xMin = 1024; yMin = 1024;
1073 gfxbbox_t b = states[statepos].clipbbox;
1074 gfxline_t p1,p2,p3,p4,p5;
1075 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1076 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1077 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1078 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1079 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1081 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1082 the middle of the two control points */
1084 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1085 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1086 m.tx = (x0 + x1)/2 - 0.5;
1087 m.ty = (y0 + y1)/2 - 0.5;
1089 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1095 GBool GFXOutputDev::useDrawForm()
1097 infofeature("forms");
1100 void GFXOutputDev::drawForm(Ref id)
1102 msg("<error> drawForm not implemented");
1104 GBool GFXOutputDev::needNonText()
1108 void GFXOutputDev::endPage()
1110 msg("<verbose> endPage (GfxOutputDev)");
1111 if(outer_clip_box) {
1112 device->endclip(device);
1115 /* notice: we're not fully done yet with this page- there might still be
1116 a few calls to drawLink() yet to come */
1119 static inline double sqr(double x) {return x*x;}
1121 #define STROKE_FILL 1
1122 #define STROKE_CLIP 2
1123 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1125 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1126 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1127 double miterLimit = state->getMiterLimit();
1128 double width = state->getTransformedLineWidth();
1131 double opaq = state->getStrokeOpacity();
1133 state->getFillRGB(&rgb);
1135 state->getStrokeRGB(&rgb);
1137 col.r = colToByte(rgb.r);
1138 col.g = colToByte(rgb.g);
1139 col.b = colToByte(rgb.b);
1140 col.a = (unsigned char)(opaq*255);
1142 gfx_capType capType = gfx_capRound;
1143 if(lineCap == 0) capType = gfx_capButt;
1144 else if(lineCap == 1) capType = gfx_capRound;
1145 else if(lineCap == 2) capType = gfx_capSquare;
1146 else msg("<error> Invalid line cap type");
1148 gfx_joinType joinType = gfx_joinRound;
1149 if(lineJoin == 0) joinType = gfx_joinMiter;
1150 else if(lineJoin == 1) joinType = gfx_joinRound;
1151 else if(lineJoin == 2) joinType = gfx_joinBevel;
1152 else msg("<error> Invalid line join type");
1154 gfxline_t*line2 = 0;
1156 int dashLength = states[statepos].dashLength;
1157 double*dashPattern = states[statepos].dashPattern;
1158 double dashStart = states[statepos].dashStart;
1159 if(dashLength && dashPattern) {
1160 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1163 /* try to find out how much the transformation matrix would
1164 stretch the dashes, and factor that into the dash lengths.
1165 This is not the entirely correct approach- it would be
1166 better to first convert the path to an unscaled version,
1167 then apply dashing, and then transform the path using
1168 the current transformation matrix. However there are few
1169 PDFs which actually stretch a dashed path in a non-orthonormal
1171 double tx1, ty1, tx2, ty2, tx3, ty3;
1172 this->transformXY(state, 0, 0, &tx1, &ty1);
1173 this->transformXY(state, 0, 1, &tx2, &ty2);
1174 this->transformXY(state, 1, 0, &tx3, &ty3);
1175 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1176 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1178 warnfeature("non-ortogonally dashed strokes", 0);
1179 double f = (d1+d2)/2;
1181 msg("<trace> %d dashes", dashLength);
1182 msg("<trace> | phase: %f", dashStart);
1183 for(t=0;t<dashLength;t++) {
1184 dash[t] = (float)dashPattern[t] * f;
1188 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1190 dash[dashLength] = -1;
1191 if(getLogLevel() >= LOGLEVEL_TRACE) {
1195 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1199 msg("<trace> After dashing:");
1202 if(getLogLevel() >= LOGLEVEL_TRACE) {
1203 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1205 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1206 lineCap==0?"butt": (lineCap==1?"round":"square"),
1208 col.r,col.g,col.b,col.a
1213 if(flags&STROKE_FILL) {
1214 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1215 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1216 if(getLogLevel() >= LOGLEVEL_TRACE) {
1217 dump_outline(gfxline);
1220 msg("<warning> Empty polygon (resulting from stroked line)");
1222 if(flags&STROKE_CLIP) {
1223 device->startclip(device, gfxline);
1224 states[statepos].clipping++;
1226 device->fill(device, gfxline, &col);
1228 gfxline_free(gfxline);
1229 gfxpoly_destroy(poly);
1231 if(flags&STROKE_CLIP)
1232 msg("<error> Stroke&clip not supported at the same time");
1233 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1237 gfxline_free(line2);
1240 gfxcolor_t getFillColor(GfxState * state)
1243 double opaq = state->getFillOpacity();
1244 state->getFillRGB(&rgb);
1246 col.r = colToByte(rgb.r);
1247 col.g = colToByte(rgb.g);
1248 col.b = colToByte(rgb.b);
1249 col.a = (unsigned char)(opaq*255);
1253 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1255 gfxcolor_t col = getFillColor(state);
1257 if(getLogLevel() >= LOGLEVEL_TRACE) {
1258 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1261 device->fill(device, line, &col);
1264 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1266 if(getLogLevel() >= LOGLEVEL_TRACE) {
1267 msg("<trace> %sclip", evenodd?"eo":"");
1270 gfxbbox_t bbox = gfxline_getbbox(line);
1271 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1273 device->startclip(device, line);
1274 states[statepos].clipping++;
1277 void GFXOutputDev::clip(GfxState *state)
1279 GfxPath * path = state->getPath();
1280 msg("<trace> clip");
1281 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1282 if(!config_disable_polygon_conversion) {
1283 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1287 clipToGfxLine(state, line, 0);
1291 void GFXOutputDev::eoClip(GfxState *state)
1293 GfxPath * path = state->getPath();
1294 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1295 clipToGfxLine(state, line, 1);
1298 void GFXOutputDev::clipToStrokePath(GfxState *state)
1300 GfxPath * path = state->getPath();
1301 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1303 if(getLogLevel() >= LOGLEVEL_TRACE) {
1304 double width = state->getTransformedLineWidth();
1305 msg("<trace> cliptostrokepath width=%f", width);
1309 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1313 void GFXOutputDev::finish()
1315 if(outer_clip_box) {
1317 device->endclip(device);
1323 GFXOutputDev::~GFXOutputDev()
1327 GBool GFXOutputDev::upsideDown()
1331 GBool GFXOutputDev::useDrawChar()
1336 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1337 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1339 static char tmp_printstr[4096];
1340 char* makeStringPrintable(char*str)
1342 int len = strlen(str);
1349 for(t=0;t<len;t++) {
1354 tmp_printstr[t] = c;
1357 tmp_printstr[len++] = '.';
1358 tmp_printstr[len++] = '.';
1359 tmp_printstr[len++] = '.';
1361 tmp_printstr[len] = 0;
1362 return tmp_printstr;
1364 void GFXOutputDev::updateFontMatrix(GfxState*state)
1366 double* ctm = state->getCTM();
1367 double fontSize = state->getFontSize();
1368 double*textMat = state->getTextMat();
1370 /* taking the absolute value of horizScaling seems to be required for
1371 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1372 double hscale = fabs(state->getHorizScaling());
1374 // from xpdf-3.02/SplashOutputDev:updateFont
1375 double mm11 = textMat[0] * fontSize * hscale;
1376 double mm12 = textMat[1] * fontSize * hscale;
1377 double mm21 = textMat[2] * fontSize;
1378 double mm22 = textMat[3] * fontSize;
1380 // multiply with ctm, like state->getFontTransMat() does
1381 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1382 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1383 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1384 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1385 this->current_font_matrix.tx = 0;
1386 this->current_font_matrix.ty = 0;
1389 void GFXOutputDev::beginString(GfxState *state, GString *s)
1391 int render = state->getRender();
1392 if(current_text_stroke) {
1393 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1395 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1398 static gfxline_t* mkEmptyGfxShape(double x, double y)
1400 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1401 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1405 static char isValidUnicode(int c)
1407 if(c>=32 && c<0x2fffe)
1412 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1413 double dx, double dy,
1414 double originX, double originY,
1415 CharCode charid, int nBytes, Unicode *_u, int uLen)
1417 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1418 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1422 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1424 int render = state->getRender();
1425 gfxcolor_t col = getFillColor(state);
1427 // check for invisible text -- this is used by Acrobat Capture
1428 if (render == RENDER_INVISIBLE) {
1430 if(!config_extrafontdata)
1434 GfxFont*font = state->getFont();
1436 if(font->getType() == fontType3) {
1437 /* type 3 chars are passed as graphics */
1438 msg("<debug> type3 char at %f/%f", x, y);
1442 Unicode u = uLen?(_u[0]):0;
1444 gfxmatrix_t m = this->current_font_matrix;
1445 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1446 //m.tx += originX; m.ty += originY;
1448 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d font=%p",m.tx,m.ty,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
1450 if((render == RENDER_FILL && !config_drawonlyshapes) ||
1451 (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
1452 (render == RENDER_INVISIBLE)) {
1454 int space = this->current_fontinfo->space_char;
1455 if(config_extrafontdata && space>=0 && m.m00 && !m.m01) {
1456 /* space char detection */
1457 if(last_char_gfxfont == current_gfxfont &&
1458 last_char_y == m.ty &&
1459 !last_char_was_space) {
1460 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1461 int space = this->current_fontinfo->space_char;
1462 float width = this->current_fontinfo->average_advance;
1463 if(m.tx - expected_x >= m.m00*width*4/10) {
1464 msg("<debug> There's a %f pixel gap between char %d and char %d (expected no more than %f), I'm inserting a space here",
1467 last_char, glyphid);
1469 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1470 if(m2.tx < expected_x) m2.tx = expected_x;
1471 device->drawchar(device, current_gfxfont, space, &col, &m2);
1474 last_char_gfxfont = current_gfxfont;
1475 last_char = glyphid;
1478 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1480 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1482 msg("<debug> Drawing glyph %d as shape", charid);
1483 if(!gfxglobals->textmodeinfo) {
1484 msg("<notice> Some texts will be rendered as shape");
1485 gfxglobals->textmodeinfo = 1;
1488 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1489 gfxline_t*tglyph = gfxline_clone(glyph);
1490 gfxline_transform(tglyph, &m);
1491 if((render&3) != RENDER_INVISIBLE) {
1492 gfxline_t*add = gfxline_clone(tglyph);
1493 current_text_stroke = gfxline_append(current_text_stroke, add);
1495 if(render&RENDER_CLIP) {
1496 gfxline_t*add = gfxline_clone(tglyph);
1497 current_text_clip = gfxline_append(current_text_clip, add);
1498 if(!current_text_clip) {
1499 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1502 gfxline_free(tglyph);
1506 void GFXOutputDev::endString(GfxState *state)
1508 int render = state->getRender();
1509 msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
1511 if(current_text_stroke) {
1512 /* fillstroke and stroke text rendering objects we can process right
1513 now (as there may be texts of other rendering modes in this
1514 text object)- clipping objects have to wait until endTextObject,
1516 device->setparameter(device, "mark","TXT");
1517 if((render&3) == RENDER_FILL) {
1518 fillGfxLine(state, current_text_stroke, 0);
1519 gfxline_free(current_text_stroke);
1520 current_text_stroke = 0;
1521 } else if((render&3) == RENDER_FILLSTROKE) {
1522 fillGfxLine(state, current_text_stroke, 0);
1523 strokeGfxline(state, current_text_stroke,0);
1524 gfxline_free(current_text_stroke);
1525 current_text_stroke = 0;
1526 } else if((render&3) == RENDER_STROKE) {
1527 strokeGfxline(state, current_text_stroke,0);
1528 gfxline_free(current_text_stroke);
1529 current_text_stroke = 0;
1531 device->setparameter(device, "mark","");
1535 void GFXOutputDev::endTextObject(GfxState *state)
1537 int render = state->getRender();
1538 msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
1540 if(current_text_clip) {
1541 device->setparameter(device, "mark","TXT");
1542 clipToGfxLine(state, current_text_clip, 0);
1543 device->setparameter(device, "mark","");
1544 gfxline_free(current_text_clip);
1545 current_text_clip = 0;
1549 /* the logic seems to be as following:
1550 first, beginType3Char is called, with the charcode and the coordinates.
1551 if this function returns true, it already knew about the char and has now drawn it.
1552 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1553 called with some parameters.
1554 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1555 at the position first passed to beginType3Char). the char ends with endType3Char.
1557 The drawing operations between beginType3Char and endType3Char are somewhat different to
1558 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1559 color determines the color of a font)
1562 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1564 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1567 if(config_extrafontdata && current_fontinfo) {
1569 gfxmatrix_t m = this->current_font_matrix;
1570 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1572 /*m.m00*=INTERNAL_FONT_SIZE;
1573 m.m01*=INTERNAL_FONT_SIZE;
1574 m.m10*=INTERNAL_FONT_SIZE;
1575 m.m11*=INTERNAL_FONT_SIZE;*/
1577 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1578 msg("<error> Invalid charid %d for font", charid);
1581 gfxcolor_t col={0,0,0,0};
1582 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1583 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1587 /* the character itself is going to be passed using the draw functions */
1588 return gFalse; /* gTrue= is_in_cache? */
1591 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1593 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1596 void GFXOutputDev::endType3Char(GfxState *state)
1599 msg("<debug> endType3Char");
1602 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1604 this->currentpage = pageNum;
1606 int rot = doc->getPageRotate(1);
1607 gfxcolor_t white = {255,255,255,255};
1608 gfxcolor_t black = {255,0,0,0};
1610 gfxline_t clippath[5];
1612 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1613 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1614 Use CropBox, not MediaBox, as page size
1621 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1622 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1624 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1625 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1627 this->clipmovex = -(int)x1;
1628 this->clipmovey = -(int)y1;
1630 /* apply user clip box */
1631 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1632 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1633 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1634 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1635 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1636 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1638 x1 += this->clipmovex;
1639 y1 += this->clipmovey;
1640 x2 += this->clipmovex;
1641 y2 += this->clipmovey;
1644 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1646 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);
1648 msg("<verbose> page is rotated %d degrees", rot);
1650 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1651 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1652 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1653 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1654 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1655 device->startclip(device, clippath); outer_clip_box = 1;
1656 if(!config_transparent) {
1657 device->fill(device, clippath, &white);
1659 states[statepos].clipbbox.xmin = x1;
1660 states[statepos].clipbbox.ymin = x1;
1661 states[statepos].clipbbox.xmax = x2;
1662 states[statepos].clipbbox.ymax = y2;
1664 states[statepos].dashPattern = 0;
1665 states[statepos].dashLength = 0;
1666 states[statepos].dashStart = 0;
1668 this->last_char_gfxfont = 0;
1672 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1674 double x1, y1, x2, y2;
1675 gfxline_t points[5];
1678 msg("<debug> drawlink");
1680 link->getRect(&x1, &y1, &x2, &y2);
1681 cvtUserToDev(x1, y1, &x, &y);
1682 points[0].type = gfx_moveTo;
1683 points[0].x = points[4].x = x + user_movex + clipmovex;
1684 points[0].y = points[4].y = y + user_movey + clipmovey;
1685 points[0].next = &points[1];
1686 cvtUserToDev(x2, y1, &x, &y);
1687 points[1].type = gfx_lineTo;
1688 points[1].x = x + user_movex + clipmovex;
1689 points[1].y = y + user_movey + clipmovey;
1690 points[1].next = &points[2];
1691 cvtUserToDev(x2, y2, &x, &y);
1692 points[2].type = gfx_lineTo;
1693 points[2].x = x + user_movex + clipmovex;
1694 points[2].y = y + user_movey + clipmovey;
1695 points[2].next = &points[3];
1696 cvtUserToDev(x1, y2, &x, &y);
1697 points[3].type = gfx_lineTo;
1698 points[3].x = x + user_movex + clipmovex;
1699 points[3].y = y + user_movey + clipmovey;
1700 points[3].next = &points[4];
1701 cvtUserToDev(x1, y1, &x, &y);
1702 points[4].type = gfx_lineTo;
1703 points[4].x = x + user_movex + clipmovex;
1704 points[4].y = y + user_movey + clipmovey;
1707 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1708 points[0].x, points[0].y,
1709 points[1].x, points[1].y,
1710 points[2].x, points[2].y,
1711 points[3].x, points[3].y);
1713 if(getLogLevel() >= LOGLEVEL_TRACE) {
1714 dump_outline(points);
1717 LinkAction*action=link->getAction();
1720 const char*type = "-?-";
1723 msg("<trace> drawlink action=%d", action->getKind());
1724 switch(action->getKind())
1728 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1729 LinkDest *dest=NULL;
1730 if (ha->getDest()==NULL)
1731 dest=catalog->findDest(ha->getNamedDest());
1733 dest=ha->getDest()->copy();
1735 if (dest->isPageRef()){
1736 Ref pageref=dest->getPageRef();
1737 page=catalog->findPage(pageref.num,pageref.gen);
1739 else page=dest->getPageNum();
1740 sprintf(buf, "%d", page);
1748 LinkGoToR*l = (LinkGoToR*)action;
1749 GString*g = l->getFileName();
1751 s = strdup(g->getCString());
1753 /* if the GoToR link has no filename, then
1754 try to find a refernce in the *local*
1756 GString*g = l->getNamedDest();
1758 s = strdup(g->getCString());
1764 LinkNamed*l = (LinkNamed*)action;
1765 GString*name = l->getName();
1767 s = strdup(name->lowerCase()->getCString());
1768 named = name->getCString();
1771 if(strstr(s, "next") || strstr(s, "forward"))
1773 page = currentpage + 1;
1775 else if(strstr(s, "prev") || strstr(s, "back"))
1777 page = currentpage - 1;
1779 else if(strstr(s, "last") || strstr(s, "end"))
1781 if(this->page2page && this->num_pages) {
1782 page = this->page2page[this->num_pages-1];
1785 else if(strstr(s, "first") || strstr(s, "top"))
1793 case actionLaunch: {
1795 LinkLaunch*l = (LinkLaunch*)action;
1796 GString * str = new GString(l->getFileName());
1797 GString * params = l->getParams();
1799 str->append(params);
1800 s = strdup(str->getCString());
1807 LinkURI*l = (LinkURI*)action;
1808 GString*g = l->getURI();
1810 url = g->getCString();
1815 case actionUnknown: {
1817 LinkUnknown*l = (LinkUnknown*)action;
1822 msg("<error> Unknown link type!");
1827 if(!s) s = strdup("-?-");
1829 msg("<trace> drawlink s=%s", s);
1831 if(!gfxglobals->linkinfo && (page || s))
1833 msg("<notice> File contains links");
1834 gfxglobals->linkinfo = 1;
1840 for(t=1;t<=this->num_pages;t++) {
1841 if(this->page2page[t]==page) {
1851 sprintf(buf, "page%d", lpage);
1852 device->drawlink(device, points, buf);
1856 device->drawlink(device, points, s);
1857 if(this->config_linkdatafile) {
1858 FILE*fi = fopen(config_linkdatafile, "ab+");
1859 fprintf(fi, "%s\n", s);
1864 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1868 void GFXOutputDev::saveState(GfxState *state) {
1869 dbg("saveState %p", state); dbgindent+=2;
1871 msg("<trace> saveState %p", state);
1874 msg("<fatal> Too many nested states in pdf.");
1878 states[statepos].state = state;
1879 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1880 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1881 states[statepos].clipping = 0;
1882 states[statepos].olddevice = 0;
1883 states[statepos].clipbbox = states[statepos-1].clipbbox;
1885 states[statepos].dashPattern = states[statepos-1].dashPattern;
1886 states[statepos].dashStart = states[statepos-1].dashStart;
1887 states[statepos].dashLength = states[statepos-1].dashLength;
1890 void GFXOutputDev::restoreState(GfxState *state) {
1891 dbgindent-=2; dbg("restoreState %p", state);
1894 msg("<fatal> Invalid restoreState");
1897 msg("<trace> restoreState %p%s%s", state,
1898 states[statepos].softmask?" (end softmask)":"",
1899 states[statepos].clipping?" (end clipping)":"");
1900 if(states[statepos].softmask) {
1901 clearSoftMask(state);
1904 if(states[statepos].dashPattern) {
1905 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1906 free(states[statepos].dashPattern);
1907 states[statepos].dashPattern = 0;
1913 while(states[statepos].clipping) {
1914 device->endclip(device);
1915 states[statepos].clipping--;
1917 if(states[statepos].state!=state) {
1918 msg("<fatal> bad state nesting");
1921 for(t=0;t<=statepos;t++) {
1922 printf("%p ", states[t].state);
1928 states[statepos].state=0;
1932 void GFXOutputDev::updateLineDash(GfxState *state)
1934 if(states[statepos].dashPattern &&
1935 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1936 free(states[statepos].dashPattern);
1937 states[statepos].dashPattern = 0;
1939 double *pattern = 0;
1942 state->getLineDash(&pattern, &dashLength, &dashStart);
1943 msg("<debug> updateLineDash, %d dashes", dashLength);
1945 states[statepos].dashPattern = 0;
1946 states[statepos].dashLength = 0;
1948 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1949 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1950 states[statepos].dashPattern = p;
1951 states[statepos].dashLength = dashLength;
1952 states[statepos].dashStart = dashStart;
1956 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1958 this->page2page = page2page;
1959 this->num_pages = num_pages;
1962 void GFXOutputDev::updateLineWidth(GfxState *state)
1964 double width = state->getTransformedLineWidth();
1967 void GFXOutputDev::updateLineCap(GfxState *state)
1969 int c = state->getLineCap();
1972 void GFXOutputDev::updateLineJoin(GfxState *state)
1974 int j = state->getLineJoin();
1977 void GFXOutputDev::updateFillColor(GfxState *state)
1980 double opaq = state->getFillOpacity();
1981 state->getFillRGB(&rgb);
1983 void GFXOutputDev::updateFillOpacity(GfxState *state)
1986 double opaq = state->getFillOpacity();
1987 state->getFillRGB(&rgb);
1988 dbg("update fillopaq %f", opaq);
1990 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1992 double opaq = state->getFillOpacity();
1993 dbg("update strokeopaq %f", opaq);
1995 void GFXOutputDev::updateFillOverprint(GfxState *state)
1997 double opaq = state->getFillOverprint();
1998 dbg("update filloverprint %f", opaq);
2000 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2002 double opaq = state->getStrokeOverprint();
2003 dbg("update strokeoverprint %f", opaq);
2005 void GFXOutputDev::updateTransfer(GfxState *state)
2007 dbg("update transfer");
2011 void GFXOutputDev::updateStrokeColor(GfxState *state)
2014 double opaq = state->getStrokeOpacity();
2015 state->getStrokeRGB(&rgb);
2018 void GFXOutputDev::updateFont(GfxState *state)
2020 GfxFont* gfxFont = state->getFont();
2024 char*id = getFontID(gfxFont);
2025 msg("<verbose> Updating font to %s", id);
2026 if(gfxFont->getType() == fontType3) {
2027 infofeature("Type3 fonts");
2028 if(!config_extrafontdata) {
2033 msg("<error> Internal Error: FontID is null");
2037 this->current_fontinfo = this->info->getFont(id);
2039 if(!this->current_fontinfo) {
2040 msg("<error> Internal Error: no fontinfo for font %s", id);
2043 if(!this->current_fontinfo->seen) {
2044 dumpFontInfo("<verbose>", gfxFont);
2047 current_gfxfont = this->current_fontinfo->getGfxFont();
2048 device->addfont(device, current_gfxfont);
2051 updateFontMatrix(state);
2054 #define SQR(x) ((x)*(x))
2056 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2058 if((newwidth<1 || newheight<1) ||
2059 (width<=newwidth || height<=newheight))
2061 unsigned char*newdata;
2063 newdata= (unsigned char*)malloc(newwidth*newheight);
2064 double fx = ((double)width)/newwidth;
2065 double fy = ((double)height)/newheight;
2067 int blocksize = (int)(8192/(fx*fy));
2068 int r = 8192*256/palettesize;
2069 for(x=0;x<newwidth;x++) {
2070 double ex = px + fx;
2071 int fromx = (int)px;
2073 int xweight1 = (int)((1-(px-fromx))*256);
2074 int xweight2 = (int)((ex-tox)*256);
2076 for(y=0;y<newheight;y++) {
2077 double ey = py + fy;
2078 int fromy = (int)py;
2080 int yweight1 = (int)((1-(py-fromy))*256);
2081 int yweight2 = (int)((ey-toy)*256);
2088 for(xx=fromx;xx<=tox;xx++)
2089 for(yy=fromy;yy<=toy;yy++) {
2090 int b = 1-data[width*yy+xx];
2092 if(xx==fromx) weight = (weight*xweight1)/256;
2093 if(xx==tox) weight = (weight*xweight2)/256;
2094 if(yy==fromy) weight = (weight*yweight1)/256;
2095 if(yy==toy) weight = (weight*yweight2)/256;
2098 //if(a) a=(palettesize-1)*r/blocksize;
2099 newdata[y*newwidth+x] = (a*blocksize)/r;
2107 #define IMAGE_TYPE_JPEG 0
2108 #define IMAGE_TYPE_LOSSLESS 1
2110 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2111 double x1,double y1,
2112 double x2,double y2,
2113 double x3,double y3,
2114 double x4,double y4, int type, int multiply)
2116 gfxcolor_t*newpic=0;
2118 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2119 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2121 gfxline_t p1,p2,p3,p4,p5;
2122 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2123 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2124 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2125 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2126 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2128 {p1.x = (int)(p1.x*20)/20.0;
2129 p1.y = (int)(p1.y*20)/20.0;
2130 p2.x = (int)(p2.x*20)/20.0;
2131 p2.y = (int)(p2.y*20)/20.0;
2132 p3.x = (int)(p3.x*20)/20.0;
2133 p3.y = (int)(p3.y*20)/20.0;
2134 p4.x = (int)(p4.x*20)/20.0;
2135 p4.y = (int)(p4.y*20)/20.0;
2136 p5.x = (int)(p5.x*20)/20.0;
2137 p5.y = (int)(p5.y*20)/20.0;
2141 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2142 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2144 m.tx = p1.x - 0.5*multiply;
2145 m.ty = p1.y - 0.5*multiply;
2148 img.data = (gfxcolor_t*)data;
2152 if(type == IMAGE_TYPE_JPEG)
2153 /* TODO: pass image_dpi to device instead */
2154 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2157 dev->fillbitmap(dev, &p1, &img, &m, 0);
2160 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2161 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2163 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2166 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2167 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2169 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2173 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2174 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2175 GBool inlineImg, int mask, int*maskColors,
2176 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2178 /* the code in this function is *old*. It's not pretty, but it works. */
2180 double x1,y1,x2,y2,x3,y3,x4,y4;
2181 ImageStream *imgStr;
2186 unsigned char* maskbitmap = 0;
2189 ncomps = colorMap->getNumPixelComps();
2190 bits = colorMap->getBits();
2195 unsigned char buf[8];
2196 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2198 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2199 imgMaskStr->reset();
2200 unsigned char pal[256];
2201 int n = 1 << colorMap->getBits();
2206 maskColorMap->getGray(pixBuf, &gray);
2207 pal[t] = colToByte(gray);
2209 for (y = 0; y < maskHeight; y++) {
2210 for (x = 0; x < maskWidth; x++) {
2211 imgMaskStr->getPixel(buf);
2212 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2217 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2218 imgMaskStr->reset();
2219 for (y = 0; y < maskHeight; y++) {
2220 for (x = 0; x < maskWidth; x++) {
2221 imgMaskStr->getPixel(buf);
2223 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2231 imgStr = new ImageStream(str, width, ncomps,bits);
2234 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2236 msg("<verbose> Ignoring %d by %d image", width, height);
2237 unsigned char buf[8];
2239 for (y = 0; y < height; ++y)
2240 for (x = 0; x < width; ++x) {
2241 imgStr->getPixel(buf);
2249 this->transformXY(state, 0, 1, &x1, &y1);
2250 this->transformXY(state, 0, 0, &x2, &y2);
2251 this->transformXY(state, 1, 0, &x3, &y3);
2252 this->transformXY(state, 1, 1, &x4, &y4);
2255 /* as type 3 bitmaps are antialized, we need to place them
2256 at integer coordinates, otherwise flash player's antializing
2257 will kick in and make everything blurry */
2258 x1 = (int)(x1);y1 = (int)(y1);
2259 x2 = (int)(x2);y2 = (int)(y2);
2260 x3 = (int)(x3);y3 = (int)(y3);
2261 x4 = (int)(x4);y4 = (int)(y4);
2264 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2266 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2267 gfxglobals->pbminfo = 1;
2270 msg("<verbose> drawing %d by %d masked picture", width, height);
2272 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2273 msg("<notice> File contains jpeg pictures");
2274 gfxglobals->jpeginfo = 1;
2278 unsigned char buf[8];
2280 unsigned char*pic = new unsigned char[width*height];
2281 gfxcolor_t pal[256];
2283 state->getFillRGB(&rgb);
2285 memset(pal,255,sizeof(pal));
2286 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2287 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2288 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2289 pal[0].a = 255; pal[1].a = 0;
2292 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2293 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2294 for (y = 0; y < height; ++y)
2295 for (x = 0; x < width; ++x)
2297 imgStr->getPixel(buf);
2300 pic[width*y+x] = buf[0];
2304 unsigned char*pic2 = 0;
2307 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2316 height = realheight;
2320 /* make a black/white palette */
2322 float r = 255./(float)(numpalette-1);
2324 for(t=0;t<numpalette;t++) {
2325 pal[t].r = colToByte(rgb.r);
2326 pal[t].g = colToByte(rgb.g);
2327 pal[t].b = colToByte(rgb.b);
2328 pal[t].a = (unsigned char)(t*r);
2333 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2334 for (y = 0; y < height; ++y) {
2335 for (x = 0; x < width; ++x) {
2336 pic2[width*y+x] = pal[pic[y*width+x]];
2339 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2343 if(maskbitmap) free(maskbitmap);
2349 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2350 gfxcolor_t*pic=new gfxcolor_t[width*height];
2351 for (y = 0; y < height; ++y) {
2352 for (x = 0; x < width; ++x) {
2353 imgStr->getPixel(pixBuf);
2354 colorMap->getRGB(pixBuf, &rgb);
2355 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2356 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2357 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2358 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2360 int x1 = x*maskWidth/width;
2361 int y1 = y*maskHeight/height;
2362 int x2 = (x+1)*maskWidth/width;
2363 int y2 = (y+1)*maskHeight/height;
2365 unsigned int alpha=0;
2366 unsigned int count=0;
2367 for(xx=x1;xx<x2;xx++)
2368 for(yy=y1;yy<y2;yy++) {
2369 alpha += maskbitmap[yy*maskWidth+xx];
2373 pic[width*y+x].a = alpha / count;
2375 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2380 if(str->getKind()==strDCT)
2381 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2383 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2386 if(maskbitmap) free(maskbitmap);
2389 gfxcolor_t*pic=new gfxcolor_t[width*height];
2390 gfxcolor_t pal[256];
2391 int n = 1 << colorMap->getBits();
2393 for(t=0;t<256;t++) {
2395 colorMap->getRGB(pixBuf, &rgb);
2396 pal[t].r = (unsigned char)(colToByte(rgb.r));
2397 pal[t].g = (unsigned char)(colToByte(rgb.g));
2398 pal[t].b = (unsigned char)(colToByte(rgb.b));
2399 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2401 for (y = 0; y < height; ++y) {
2402 for (x = 0; x < width; ++x) {
2403 imgStr->getPixel(pixBuf);
2404 pic[width*y+x] = pal[pixBuf[0]];
2405 if(maskColors && *maskColors==pixBuf[0]) {
2406 pic[width*y+x].a = 0;
2411 if(maskWidth < width && maskHeight < height) {
2412 for(y = 0; y < height; y++) {
2413 for (x = 0; x < width; x++) {
2414 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2418 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2419 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2420 double dx = width / (double)maskWidth;
2421 double dy = height / (double)maskHeight;
2423 for(y = 0; y < maskHeight; y++) {
2425 for (x = 0; x < maskWidth; x++) {
2426 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2427 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2435 height = maskHeight;
2438 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2442 if(maskbitmap) free(maskbitmap);
2447 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2448 int width, int height, GBool invert,
2451 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2452 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2453 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2456 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2457 int width, int height, GfxImageColorMap *colorMap,
2458 int *maskColors, GBool inlineImg)
2460 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2461 colorMap?"colorMap":"no colorMap",
2462 maskColors?"maskColors":"no maskColors",
2464 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2465 colorMap?"colorMap":"no colorMap",
2466 maskColors?"maskColors":"no maskColors",
2469 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2470 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2471 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2474 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2475 int width, int height,
2476 GfxImageColorMap *colorMap,
2477 Stream *maskStr, int maskWidth, int maskHeight,
2480 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2481 colorMap?"colorMap":"no colorMap",
2482 maskWidth, maskHeight);
2483 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2484 colorMap?"colorMap":"no colorMap",
2485 maskWidth, maskHeight);
2487 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2488 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2489 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2492 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2493 int width, int height,
2494 GfxImageColorMap *colorMap,
2496 int maskWidth, int maskHeight,
2497 GfxImageColorMap *maskColorMap)
2499 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2500 colorMap?"colorMap":"no colorMap",
2501 maskWidth, maskHeight);
2502 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2503 colorMap?"colorMap":"no colorMap",
2504 maskWidth, maskHeight);
2506 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2507 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2508 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2511 void GFXOutputDev::stroke(GfxState *state)
2515 GfxPath * path = state->getPath();
2516 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2517 strokeGfxline(state, line, 0);
2521 void GFXOutputDev::fill(GfxState *state)
2523 gfxcolor_t col = getFillColor(state);
2524 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2526 GfxPath * path = state->getPath();
2527 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2528 if(!config_disable_polygon_conversion) {
2529 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2533 fillGfxLine(state, line, 0);
2537 void GFXOutputDev::eoFill(GfxState *state)
2539 gfxcolor_t col = getFillColor(state);
2540 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2542 GfxPath * path = state->getPath();
2543 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2544 fillGfxLine(state, line, 1);
2549 static const char* dirseparator()
2558 void addGlobalFont(const char*filename)
2560 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2561 memset(f, 0, sizeof(fontfile_t));
2562 f->filename = filename;
2563 int len = strlen(filename);
2564 char*r1 = strrchr((char*)filename, '/');
2565 char*r2 = strrchr((char*)filename, '\\');
2573 msg("<verbose> Adding font \"%s\".", filename);
2574 if(global_fonts_next) {
2575 global_fonts_next->next = f;
2576 global_fonts_next = global_fonts_next->next;
2578 global_fonts_next = global_fonts = f;
2582 void addGlobalLanguageDir(const char*dir)
2584 msg("<notice> Adding %s to language pack directories", dir);
2587 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2588 strcpy(config_file, dir);
2589 strcat(config_file, dirseparator());
2590 strcat(config_file, "add-to-xpdfrc");
2592 fi = fopen(config_file, "rb");
2594 msg("<error> Could not open %s", config_file);
2597 globalParams->parseFile(new GString(config_file), fi);
2601 void addGlobalFontDir(const char*dirname)
2603 #ifdef HAVE_DIRENT_H
2604 DIR*dir = opendir(dirname);
2606 msg("<warning> Couldn't open directory %s", dirname);
2612 ent = readdir (dir);
2616 char*name = ent->d_name;
2622 if(!strncasecmp(&name[l-4], ".pfa", 4))
2624 if(!strncasecmp(&name[l-4], ".pfb", 4))
2626 if(!strncasecmp(&name[l-4], ".ttf", 4))
2629 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2630 strcpy(fontname, dirname);
2631 strcat(fontname, dirseparator());
2632 strcat(fontname, name);
2633 addGlobalFont(fontname);
2637 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2640 msg("<warning> No dirent.h");
2644 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2645 GfxColorSpace *blendingColorSpace,
2646 GBool isolated, GBool knockout,
2649 const char*colormodename = "";
2651 if(blendingColorSpace) {
2652 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2654 dbg("beginTransparencyGroup device=%p %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2655 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);
2657 //states[statepos].createsoftmask |= forSoftMask;
2658 states[statepos].createsoftmask = forSoftMask;
2659 states[statepos].transparencygroup = !forSoftMask;
2660 states[statepos].isolated = isolated;
2662 states[statepos].olddevice = this->device;
2663 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2664 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2666 gfxdevice_record_init(this->device);
2668 /*if(!forSoftMask) { ////???
2669 state->setFillOpacity(0.0);
2674 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2677 gfxdevice_t*r = this->device;
2679 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2681 this->device = states[statepos].olddevice;
2683 msg("<error> Invalid state nesting");
2685 states[statepos].olddevice = 0;
2687 gfxresult_t*recording = r->finish(r);
2689 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2690 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2692 if(states[statepos].createsoftmask) {
2693 states[statepos-1].softmaskrecording = recording;
2695 states[statepos-1].grouprecording = recording;
2698 states[statepos].createsoftmask = 0;
2699 states[statepos].transparencygroup = 0;
2703 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2705 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2706 "colordodge","colorburn","hardlight","softlight","difference",
2707 "exclusion","hue","saturation","color","luminosity"};
2709 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2710 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2712 if(state->getBlendMode() == gfxBlendNormal)
2713 infofeature("transparency groups");
2716 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2717 warnfeature(buffer, 0);
2720 gfxresult_t*grouprecording = states[statepos].grouprecording;
2722 int blendmode = state->getBlendMode();
2723 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2724 int alpha = (int)(state->getFillOpacity()*255);
2725 if(blendmode == gfxBlendMultiply && alpha>200)
2728 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2729 gfxdevice_ops_init(&ops, this->device, alpha);
2730 gfxresult_record_replay(grouprecording, &ops);
2733 grouprecording->destroy(grouprecording);
2735 states[statepos].grouprecording = 0;
2738 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2740 if(states[statepos].softmask) {
2741 /* shouldn't happen, but *does* happen */
2742 clearSoftMask(state);
2745 /* alpha = 1: retrieve mask values from alpha layer
2746 alpha = 0: retrieve mask values from luminance */
2748 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2749 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2750 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2751 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2753 infofeature("soft masks");
2755 warnfeature("soft masks from alpha channel",0);
2757 if(states[statepos].olddevice) {
2758 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2761 states[statepos].olddevice = this->device;
2762 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2763 gfxdevice_record_init(this->device);
2765 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2767 states[statepos].softmask = 1;
2768 states[statepos].softmask_alpha = alpha;
2771 static inline Guchar div255(int x) {
2772 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2775 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2777 if(c < min) c = min;
2778 if(c > max) c = max;
2782 void GFXOutputDev::clearSoftMask(GfxState *state)
2784 if(!states[statepos].softmask)
2786 states[statepos].softmask = 0;
2787 dbg("clearSoftMask statepos=%d", statepos);
2788 msg("<verbose> clearSoftMask statepos=%d", statepos);
2790 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2791 msg("<error> Error in softmask/tgroup ordering");
2795 gfxresult_t*mask = states[statepos].softmaskrecording;
2796 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2797 this->device = states[statepos].olddevice;
2799 /* get outline of all objects below the soft mask */
2800 gfxdevice_t uniondev;
2801 gfxdevice_union_init(&uniondev, 0);
2802 gfxresult_record_replay(below, &uniondev);
2803 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2804 uniondev.finish(&uniondev);
2805 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2806 gfxline_free(belowoutline);belowoutline=0;
2808 this->device->startclip(this->device, belowoutline);
2809 gfxresult_record_replay(below, this->device);
2810 gfxresult_record_replay(mask, this->device);
2811 this->device->endclip(this->device);
2814 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2815 if(width<=0 || height<=0)
2818 gfxdevice_t belowrender;
2819 gfxdevice_render_init(&belowrender);
2820 if(states[statepos+1].isolated) {
2821 belowrender.setparameter(&belowrender, "fillwhite", "1");
2823 belowrender.setparameter(&belowrender, "antialize", "2");
2824 belowrender.startpage(&belowrender, width, height);
2825 gfxresult_record_replay(below, &belowrender);
2826 belowrender.endpage(&belowrender);
2827 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2828 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2829 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2831 gfxdevice_t maskrender;
2832 gfxdevice_render_init(&maskrender);
2833 maskrender.startpage(&maskrender, width, height);
2834 gfxresult_record_replay(mask, &maskrender);
2835 maskrender.endpage(&maskrender);
2836 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2837 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2839 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2840 msg("<fatal> Internal error in mask drawing");
2845 for(y=0;y<height;y++) {
2846 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2847 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2848 for(x=0;x<width;x++) {
2850 if(states[statepos].softmask_alpha) {
2853 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2856 l2->a = div255(alpha*l2->a);
2858 /* DON'T premultiply alpha- this is done by fillbitmap,
2859 depending on the output device */
2860 //l2->r = div255(alpha*l2->r);
2861 //l2->g = div255(alpha*l2->g);
2862 //l2->b = div255(alpha*l2->b);
2868 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2871 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2872 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2874 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2876 mask->destroy(mask);
2877 below->destroy(below);
2878 maskresult->destroy(maskresult);
2879 belowresult->destroy(belowresult);
2880 states[statepos].softmaskrecording = 0;
2885 // public: ~MemCheck()
2887 // delete globalParams;globalParams=0;
2888 // Object::memCheck(stderr);
2889 // gMemReport(stderr);