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");
1000 gfxgradient_t*g = &gr[0];
1004 g[0].color = col2col(colspace, &color0);
1005 g[1].color = col2col(colspace, &color1);
1006 g[2].color = col2col(colspace, &color2);
1011 gfxbbox_t b = states[statepos].clipbbox;
1012 gfxline_t p1,p2,p3,p4,p5;
1013 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1014 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1015 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1016 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1017 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1020 //m.m00 = (x3-x0); m.m10 = (x1-x0);
1021 //m.m01 = (y3-y0); m.m11 = (y1-y0);
1022 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1023 m.m00 = (x1-x0); m.m10 = (x2-x0);
1024 m.m01 = (y1-y0); m.m11 = (y2-y0);
1028 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1032 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1035 shading->getCoords(&x0,&y0,&x1,&y1);
1036 this->transformXY(state, x0,y0,&x0,&y0);
1037 this->transformXY(state, x1,y1,&x1,&y1);
1042 shading->getColor(0.0, &color0);
1043 shading->getColor(0.5, &color1);
1044 shading->getColor(1.0, &color2);
1046 GfxColorSpace* colspace = shading->getColorSpace();
1048 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1049 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1050 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1051 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1053 infofeature("axial shaded fills");
1055 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1059 g[0].color = col2col(colspace, &color0);
1060 g[1].color = col2col(colspace, &color1);
1061 g[2].color = col2col(colspace, &color2);
1066 double xMin,yMin,xMax,yMax;
1067 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1068 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1069 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1072 xMin = 1024; yMin = 1024;
1074 gfxbbox_t b = states[statepos].clipbbox;
1075 gfxline_t p1,p2,p3,p4,p5;
1076 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1077 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1078 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1079 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1080 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1082 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1083 the middle of the two control points */
1085 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1086 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1087 m.tx = (x0 + x1)/2 - 0.5;
1088 m.ty = (y0 + y1)/2 - 0.5;
1090 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1096 GBool GFXOutputDev::useDrawForm()
1098 infofeature("forms");
1101 void GFXOutputDev::drawForm(Ref id)
1103 msg("<error> drawForm not implemented");
1105 GBool GFXOutputDev::needNonText()
1109 void GFXOutputDev::endPage()
1111 msg("<verbose> endPage (GfxOutputDev)");
1112 if(outer_clip_box) {
1113 device->endclip(device);
1116 /* notice: we're not fully done yet with this page- there might still be
1117 a few calls to drawLink() yet to come */
1120 static inline double sqr(double x) {return x*x;}
1122 #define STROKE_FILL 1
1123 #define STROKE_CLIP 2
1124 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1126 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1127 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1128 double miterLimit = state->getMiterLimit();
1129 double width = state->getTransformedLineWidth();
1132 double opaq = state->getStrokeOpacity();
1134 state->getFillRGB(&rgb);
1136 state->getStrokeRGB(&rgb);
1138 col.r = colToByte(rgb.r);
1139 col.g = colToByte(rgb.g);
1140 col.b = colToByte(rgb.b);
1141 col.a = (unsigned char)(opaq*255);
1143 gfx_capType capType = gfx_capRound;
1144 if(lineCap == 0) capType = gfx_capButt;
1145 else if(lineCap == 1) capType = gfx_capRound;
1146 else if(lineCap == 2) capType = gfx_capSquare;
1147 else msg("<error> Invalid line cap type");
1149 gfx_joinType joinType = gfx_joinRound;
1150 if(lineJoin == 0) joinType = gfx_joinMiter;
1151 else if(lineJoin == 1) joinType = gfx_joinRound;
1152 else if(lineJoin == 2) joinType = gfx_joinBevel;
1153 else msg("<error> Invalid line join type");
1155 gfxline_t*line2 = 0;
1157 int dashLength = states[statepos].dashLength;
1158 double*dashPattern = states[statepos].dashPattern;
1159 double dashStart = states[statepos].dashStart;
1160 if(dashLength && dashPattern) {
1161 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1164 /* try to find out how much the transformation matrix would
1165 stretch the dashes, and factor that into the dash lengths.
1166 This is not the entirely correct approach- it would be
1167 better to first convert the path to an unscaled version,
1168 then apply dashing, and then transform the path using
1169 the current transformation matrix. However there are few
1170 PDFs which actually stretch a dashed path in a non-orthonormal
1172 double tx1, ty1, tx2, ty2, tx3, ty3;
1173 this->transformXY(state, 0, 0, &tx1, &ty1);
1174 this->transformXY(state, 0, 1, &tx2, &ty2);
1175 this->transformXY(state, 1, 0, &tx3, &ty3);
1176 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1177 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1179 warnfeature("non-ortogonally dashed strokes", 0);
1180 double f = (d1+d2)/2;
1182 msg("<trace> %d dashes", dashLength);
1183 msg("<trace> | phase: %f", dashStart);
1184 for(t=0;t<dashLength;t++) {
1185 dash[t] = (float)dashPattern[t] * f;
1189 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1191 dash[dashLength] = -1;
1192 if(getLogLevel() >= LOGLEVEL_TRACE) {
1196 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1200 msg("<trace> After dashing:");
1203 if(getLogLevel() >= LOGLEVEL_TRACE) {
1204 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1206 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1207 lineCap==0?"butt": (lineCap==1?"round":"square"),
1209 col.r,col.g,col.b,col.a
1214 if(flags&STROKE_FILL) {
1215 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1216 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1217 if(getLogLevel() >= LOGLEVEL_TRACE) {
1218 dump_outline(gfxline);
1221 msg("<warning> Empty polygon (resulting from stroked line)");
1223 if(flags&STROKE_CLIP) {
1224 device->startclip(device, gfxline);
1225 states[statepos].clipping++;
1227 device->fill(device, gfxline, &col);
1229 gfxline_free(gfxline);
1230 gfxpoly_destroy(poly);
1232 if(flags&STROKE_CLIP)
1233 msg("<error> Stroke&clip not supported at the same time");
1234 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1238 gfxline_free(line2);
1241 gfxcolor_t getFillColor(GfxState * state)
1244 double opaq = state->getFillOpacity();
1245 state->getFillRGB(&rgb);
1247 col.r = colToByte(rgb.r);
1248 col.g = colToByte(rgb.g);
1249 col.b = colToByte(rgb.b);
1250 col.a = (unsigned char)(opaq*255);
1254 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1256 gfxcolor_t col = getFillColor(state);
1258 if(getLogLevel() >= LOGLEVEL_TRACE) {
1259 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1262 device->fill(device, line, &col);
1265 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1267 if(getLogLevel() >= LOGLEVEL_TRACE) {
1268 msg("<trace> %sclip", evenodd?"eo":"");
1271 gfxbbox_t bbox = gfxline_getbbox(line);
1272 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1274 device->startclip(device, line);
1275 states[statepos].clipping++;
1278 void GFXOutputDev::clip(GfxState *state)
1280 GfxPath * path = state->getPath();
1281 msg("<trace> clip");
1282 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1283 if(!config_disable_polygon_conversion) {
1284 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1288 clipToGfxLine(state, line, 0);
1292 void GFXOutputDev::eoClip(GfxState *state)
1294 GfxPath * path = state->getPath();
1295 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1296 clipToGfxLine(state, line, 1);
1299 void GFXOutputDev::clipToStrokePath(GfxState *state)
1301 GfxPath * path = state->getPath();
1302 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1304 if(getLogLevel() >= LOGLEVEL_TRACE) {
1305 double width = state->getTransformedLineWidth();
1306 msg("<trace> cliptostrokepath width=%f", width);
1310 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1314 void GFXOutputDev::finish()
1316 if(outer_clip_box) {
1318 device->endclip(device);
1324 GFXOutputDev::~GFXOutputDev()
1328 GBool GFXOutputDev::upsideDown()
1332 GBool GFXOutputDev::useDrawChar()
1337 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1338 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1340 static char tmp_printstr[4096];
1341 char* makeStringPrintable(char*str)
1343 int len = strlen(str);
1350 for(t=0;t<len;t++) {
1355 tmp_printstr[t] = c;
1358 tmp_printstr[len++] = '.';
1359 tmp_printstr[len++] = '.';
1360 tmp_printstr[len++] = '.';
1362 tmp_printstr[len] = 0;
1363 return tmp_printstr;
1365 void GFXOutputDev::updateFontMatrix(GfxState*state)
1367 double* ctm = state->getCTM();
1368 double fontSize = state->getFontSize();
1369 double*textMat = state->getTextMat();
1371 /* taking the absolute value of horizScaling seems to be required for
1372 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1373 double hscale = fabs(state->getHorizScaling());
1375 // from xpdf-3.02/SplashOutputDev:updateFont
1376 double mm11 = textMat[0] * fontSize * hscale;
1377 double mm12 = textMat[1] * fontSize * hscale;
1378 double mm21 = textMat[2] * fontSize;
1379 double mm22 = textMat[3] * fontSize;
1381 // multiply with ctm, like state->getFontTransMat() does
1382 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1383 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1384 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1385 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1386 this->current_font_matrix.tx = 0;
1387 this->current_font_matrix.ty = 0;
1390 void GFXOutputDev::beginString(GfxState *state, GString *s)
1392 int render = state->getRender();
1393 if(current_text_stroke) {
1394 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1396 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1399 static gfxline_t* mkEmptyGfxShape(double x, double y)
1401 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1402 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1406 static char isValidUnicode(int c)
1408 if(c>=32 && c<0x2fffe)
1413 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1414 double dx, double dy,
1415 double originX, double originY,
1416 CharCode charid, int nBytes, Unicode *_u, int uLen)
1418 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1419 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1423 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1425 int render = state->getRender();
1426 gfxcolor_t col = getFillColor(state);
1428 // check for invisible text -- this is used by Acrobat Capture
1429 if (render == RENDER_INVISIBLE) {
1431 if(!config_extrafontdata)
1435 GfxFont*font = state->getFont();
1437 if(font->getType() == fontType3) {
1438 /* type 3 chars are passed as graphics */
1439 msg("<debug> type3 char at %f/%f", x, y);
1443 Unicode u = uLen?(_u[0]):0;
1445 gfxmatrix_t m = this->current_font_matrix;
1446 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1447 //m.tx += originX; m.ty += originY;
1449 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);
1451 if((render == RENDER_FILL && !config_drawonlyshapes) ||
1452 (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
1453 (render == RENDER_INVISIBLE)) {
1455 int space = this->current_fontinfo->space_char;
1456 if(config_extrafontdata && space>=0 && m.m00 && !m.m01) {
1457 /* space char detection */
1458 if(last_char_gfxfont == current_gfxfont &&
1459 last_char_y == m.ty &&
1460 !last_char_was_space) {
1461 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1462 int space = this->current_fontinfo->space_char;
1463 float width = this->current_fontinfo->average_advance;
1464 if(m.tx - expected_x >= m.m00*width*4/10) {
1465 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",
1468 last_char, glyphid);
1470 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1471 if(m2.tx < expected_x) m2.tx = expected_x;
1472 device->drawchar(device, current_gfxfont, space, &col, &m2);
1475 last_char_gfxfont = current_gfxfont;
1476 last_char = glyphid;
1479 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1481 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1483 msg("<debug> Drawing glyph %d as shape", charid);
1484 if(!gfxglobals->textmodeinfo) {
1485 msg("<notice> Some texts will be rendered as shape");
1486 gfxglobals->textmodeinfo = 1;
1489 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1490 gfxline_t*tglyph = gfxline_clone(glyph);
1491 gfxline_transform(tglyph, &m);
1492 if((render&3) != RENDER_INVISIBLE) {
1493 gfxline_t*add = gfxline_clone(tglyph);
1494 current_text_stroke = gfxline_append(current_text_stroke, add);
1496 if(render&RENDER_CLIP) {
1497 gfxline_t*add = gfxline_clone(tglyph);
1498 current_text_clip = gfxline_append(current_text_clip, add);
1499 if(!current_text_clip) {
1500 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1503 gfxline_free(tglyph);
1507 void GFXOutputDev::endString(GfxState *state)
1509 int render = state->getRender();
1510 msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
1512 if(current_text_stroke) {
1513 /* fillstroke and stroke text rendering objects we can process right
1514 now (as there may be texts of other rendering modes in this
1515 text object)- clipping objects have to wait until endTextObject,
1517 device->setparameter(device, "mark","TXT");
1518 if((render&3) == RENDER_FILL) {
1519 fillGfxLine(state, current_text_stroke, 0);
1520 gfxline_free(current_text_stroke);
1521 current_text_stroke = 0;
1522 } else if((render&3) == RENDER_FILLSTROKE) {
1523 fillGfxLine(state, current_text_stroke, 0);
1524 strokeGfxline(state, current_text_stroke,0);
1525 gfxline_free(current_text_stroke);
1526 current_text_stroke = 0;
1527 } else if((render&3) == RENDER_STROKE) {
1528 strokeGfxline(state, current_text_stroke,0);
1529 gfxline_free(current_text_stroke);
1530 current_text_stroke = 0;
1532 device->setparameter(device, "mark","");
1536 void GFXOutputDev::endTextObject(GfxState *state)
1538 int render = state->getRender();
1539 msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
1541 if(current_text_clip) {
1542 device->setparameter(device, "mark","TXT");
1543 clipToGfxLine(state, current_text_clip, 0);
1544 device->setparameter(device, "mark","");
1545 gfxline_free(current_text_clip);
1546 current_text_clip = 0;
1550 /* the logic seems to be as following:
1551 first, beginType3Char is called, with the charcode and the coordinates.
1552 if this function returns true, it already knew about the char and has now drawn it.
1553 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1554 called with some parameters.
1555 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1556 at the position first passed to beginType3Char). the char ends with endType3Char.
1558 The drawing operations between beginType3Char and endType3Char are somewhat different to
1559 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1560 color determines the color of a font)
1563 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1565 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1568 if(config_extrafontdata && current_fontinfo) {
1570 gfxmatrix_t m = this->current_font_matrix;
1571 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1573 /*m.m00*=INTERNAL_FONT_SIZE;
1574 m.m01*=INTERNAL_FONT_SIZE;
1575 m.m10*=INTERNAL_FONT_SIZE;
1576 m.m11*=INTERNAL_FONT_SIZE;*/
1578 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1579 msg("<error> Invalid charid %d for font", charid);
1582 gfxcolor_t col={0,0,0,0};
1583 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1584 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1588 /* the character itself is going to be passed using the draw functions */
1589 return gFalse; /* gTrue= is_in_cache? */
1592 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1594 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1597 void GFXOutputDev::endType3Char(GfxState *state)
1600 msg("<debug> endType3Char");
1603 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1605 this->currentpage = pageNum;
1607 int rot = doc->getPageRotate(1);
1608 gfxcolor_t white = {255,255,255,255};
1609 gfxcolor_t black = {255,0,0,0};
1611 gfxline_t clippath[5];
1613 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1614 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1615 Use CropBox, not MediaBox, as page size
1622 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1623 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1625 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1626 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1628 this->clipmovex = -(int)x1;
1629 this->clipmovey = -(int)y1;
1631 /* apply user clip box */
1632 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1633 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1634 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1635 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1636 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1637 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1639 x1 += this->clipmovex;
1640 y1 += this->clipmovey;
1641 x2 += this->clipmovex;
1642 y2 += this->clipmovey;
1645 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1647 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);
1649 msg("<verbose> page is rotated %d degrees", rot);
1651 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1652 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1653 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1654 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1655 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1656 device->startclip(device, clippath); outer_clip_box = 1;
1657 if(!config_transparent) {
1658 device->fill(device, clippath, &white);
1660 states[statepos].clipbbox.xmin = x1;
1661 states[statepos].clipbbox.ymin = x1;
1662 states[statepos].clipbbox.xmax = x2;
1663 states[statepos].clipbbox.ymax = y2;
1665 states[statepos].dashPattern = 0;
1666 states[statepos].dashLength = 0;
1667 states[statepos].dashStart = 0;
1669 this->last_char_gfxfont = 0;
1673 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1675 double x1, y1, x2, y2;
1676 gfxline_t points[5];
1679 msg("<debug> drawlink");
1681 link->getRect(&x1, &y1, &x2, &y2);
1682 cvtUserToDev(x1, y1, &x, &y);
1683 points[0].type = gfx_moveTo;
1684 points[0].x = points[4].x = x + user_movex + clipmovex;
1685 points[0].y = points[4].y = y + user_movey + clipmovey;
1686 points[0].next = &points[1];
1687 cvtUserToDev(x2, y1, &x, &y);
1688 points[1].type = gfx_lineTo;
1689 points[1].x = x + user_movex + clipmovex;
1690 points[1].y = y + user_movey + clipmovey;
1691 points[1].next = &points[2];
1692 cvtUserToDev(x2, y2, &x, &y);
1693 points[2].type = gfx_lineTo;
1694 points[2].x = x + user_movex + clipmovex;
1695 points[2].y = y + user_movey + clipmovey;
1696 points[2].next = &points[3];
1697 cvtUserToDev(x1, y2, &x, &y);
1698 points[3].type = gfx_lineTo;
1699 points[3].x = x + user_movex + clipmovex;
1700 points[3].y = y + user_movey + clipmovey;
1701 points[3].next = &points[4];
1702 cvtUserToDev(x1, y1, &x, &y);
1703 points[4].type = gfx_lineTo;
1704 points[4].x = x + user_movex + clipmovex;
1705 points[4].y = y + user_movey + clipmovey;
1708 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1709 points[0].x, points[0].y,
1710 points[1].x, points[1].y,
1711 points[2].x, points[2].y,
1712 points[3].x, points[3].y);
1714 if(getLogLevel() >= LOGLEVEL_TRACE) {
1715 dump_outline(points);
1718 LinkAction*action=link->getAction();
1721 const char*type = "-?-";
1724 msg("<trace> drawlink action=%d", action->getKind());
1725 switch(action->getKind())
1729 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1730 LinkDest *dest=NULL;
1731 if (ha->getDest()==NULL)
1732 dest=catalog->findDest(ha->getNamedDest());
1734 dest=ha->getDest()->copy();
1736 if (dest->isPageRef()){
1737 Ref pageref=dest->getPageRef();
1738 page=catalog->findPage(pageref.num,pageref.gen);
1740 else page=dest->getPageNum();
1741 sprintf(buf, "%d", page);
1749 LinkGoToR*l = (LinkGoToR*)action;
1750 GString*g = l->getFileName();
1752 s = strdup(g->getCString());
1754 /* if the GoToR link has no filename, then
1755 try to find a refernce in the *local*
1757 GString*g = l->getNamedDest();
1759 s = strdup(g->getCString());
1765 LinkNamed*l = (LinkNamed*)action;
1766 GString*name = l->getName();
1768 s = strdup(name->lowerCase()->getCString());
1769 named = name->getCString();
1772 if(strstr(s, "next") || strstr(s, "forward"))
1774 page = currentpage + 1;
1776 else if(strstr(s, "prev") || strstr(s, "back"))
1778 page = currentpage - 1;
1780 else if(strstr(s, "last") || strstr(s, "end"))
1782 if(this->page2page && this->num_pages) {
1783 page = this->page2page[this->num_pages-1];
1786 else if(strstr(s, "first") || strstr(s, "top"))
1794 case actionLaunch: {
1796 LinkLaunch*l = (LinkLaunch*)action;
1797 GString * str = new GString(l->getFileName());
1798 GString * params = l->getParams();
1800 str->append(params);
1801 s = strdup(str->getCString());
1808 LinkURI*l = (LinkURI*)action;
1809 GString*g = l->getURI();
1811 url = g->getCString();
1816 case actionUnknown: {
1818 LinkUnknown*l = (LinkUnknown*)action;
1823 msg("<error> Unknown link type!");
1828 if(!s) s = strdup("-?-");
1830 msg("<trace> drawlink s=%s", s);
1832 if(!gfxglobals->linkinfo && (page || s))
1834 msg("<notice> File contains links");
1835 gfxglobals->linkinfo = 1;
1841 for(t=1;t<=this->num_pages;t++) {
1842 if(this->page2page[t]==page) {
1852 sprintf(buf, "page%d", lpage);
1853 device->drawlink(device, points, buf);
1857 device->drawlink(device, points, s);
1858 if(this->config_linkdatafile) {
1859 FILE*fi = fopen(config_linkdatafile, "ab+");
1860 fprintf(fi, "%s\n", s);
1865 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1869 void GFXOutputDev::saveState(GfxState *state) {
1870 dbg("saveState %p", state); dbgindent+=2;
1872 msg("<trace> saveState %p", state);
1875 msg("<fatal> Too many nested states in pdf.");
1879 states[statepos].state = state;
1880 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1881 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1882 states[statepos].clipping = 0;
1883 states[statepos].olddevice = 0;
1884 states[statepos].clipbbox = states[statepos-1].clipbbox;
1886 states[statepos].dashPattern = states[statepos-1].dashPattern;
1887 states[statepos].dashStart = states[statepos-1].dashStart;
1888 states[statepos].dashLength = states[statepos-1].dashLength;
1891 void GFXOutputDev::restoreState(GfxState *state) {
1892 dbgindent-=2; dbg("restoreState %p", state);
1895 msg("<fatal> Invalid restoreState");
1898 msg("<trace> restoreState %p%s%s", state,
1899 states[statepos].softmask?" (end softmask)":"",
1900 states[statepos].clipping?" (end clipping)":"");
1901 if(states[statepos].softmask) {
1902 clearSoftMask(state);
1905 if(states[statepos].dashPattern) {
1906 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1907 free(states[statepos].dashPattern);
1908 states[statepos].dashPattern = 0;
1914 while(states[statepos].clipping) {
1915 device->endclip(device);
1916 states[statepos].clipping--;
1918 if(states[statepos].state!=state) {
1919 msg("<fatal> bad state nesting");
1922 for(t=0;t<=statepos;t++) {
1923 printf("%p ", states[t].state);
1929 states[statepos].state=0;
1933 void GFXOutputDev::updateLineDash(GfxState *state)
1935 if(states[statepos].dashPattern &&
1936 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1937 free(states[statepos].dashPattern);
1938 states[statepos].dashPattern = 0;
1940 double *pattern = 0;
1943 state->getLineDash(&pattern, &dashLength, &dashStart);
1944 msg("<debug> updateLineDash, %d dashes", dashLength);
1946 states[statepos].dashPattern = 0;
1947 states[statepos].dashLength = 0;
1949 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1950 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1951 states[statepos].dashPattern = p;
1952 states[statepos].dashLength = dashLength;
1953 states[statepos].dashStart = dashStart;
1957 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1959 this->page2page = page2page;
1960 this->num_pages = num_pages;
1963 void GFXOutputDev::updateLineWidth(GfxState *state)
1965 double width = state->getTransformedLineWidth();
1968 void GFXOutputDev::updateLineCap(GfxState *state)
1970 int c = state->getLineCap();
1973 void GFXOutputDev::updateLineJoin(GfxState *state)
1975 int j = state->getLineJoin();
1978 void GFXOutputDev::updateFillColor(GfxState *state)
1981 double opaq = state->getFillOpacity();
1982 state->getFillRGB(&rgb);
1984 void GFXOutputDev::updateFillOpacity(GfxState *state)
1987 double opaq = state->getFillOpacity();
1988 state->getFillRGB(&rgb);
1989 dbg("update fillopaq %f", opaq);
1991 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1993 double opaq = state->getFillOpacity();
1994 dbg("update strokeopaq %f", opaq);
1996 void GFXOutputDev::updateFillOverprint(GfxState *state)
1998 double opaq = state->getFillOverprint();
1999 dbg("update filloverprint %f", opaq);
2001 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2003 double opaq = state->getStrokeOverprint();
2004 dbg("update strokeoverprint %f", opaq);
2006 void GFXOutputDev::updateTransfer(GfxState *state)
2008 dbg("update transfer");
2012 void GFXOutputDev::updateStrokeColor(GfxState *state)
2015 double opaq = state->getStrokeOpacity();
2016 state->getStrokeRGB(&rgb);
2019 void GFXOutputDev::updateFont(GfxState *state)
2021 GfxFont* gfxFont = state->getFont();
2025 char*id = getFontID(gfxFont);
2026 msg("<verbose> Updating font to %s", id);
2027 if(gfxFont->getType() == fontType3) {
2028 infofeature("Type3 fonts");
2029 if(!config_extrafontdata) {
2034 msg("<error> Internal Error: FontID is null");
2038 this->current_fontinfo = this->info->getFont(id);
2040 if(!this->current_fontinfo) {
2041 msg("<error> Internal Error: no fontinfo for font %s", id);
2044 if(!this->current_fontinfo->seen) {
2045 dumpFontInfo("<verbose>", gfxFont);
2048 current_gfxfont = this->current_fontinfo->getGfxFont();
2049 device->addfont(device, current_gfxfont);
2052 updateFontMatrix(state);
2055 #define SQR(x) ((x)*(x))
2057 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2059 if((newwidth<1 || newheight<1) ||
2060 (width<=newwidth || height<=newheight))
2062 unsigned char*newdata;
2064 newdata= (unsigned char*)malloc(newwidth*newheight);
2065 double fx = ((double)width)/newwidth;
2066 double fy = ((double)height)/newheight;
2068 int blocksize = (int)(8192/(fx*fy));
2069 int r = 8192*256/palettesize;
2070 for(x=0;x<newwidth;x++) {
2071 double ex = px + fx;
2072 int fromx = (int)px;
2074 int xweight1 = (int)((1-(px-fromx))*256);
2075 int xweight2 = (int)((ex-tox)*256);
2077 for(y=0;y<newheight;y++) {
2078 double ey = py + fy;
2079 int fromy = (int)py;
2081 int yweight1 = (int)((1-(py-fromy))*256);
2082 int yweight2 = (int)((ey-toy)*256);
2089 for(xx=fromx;xx<=tox;xx++)
2090 for(yy=fromy;yy<=toy;yy++) {
2091 int b = 1-data[width*yy+xx];
2093 if(xx==fromx) weight = (weight*xweight1)/256;
2094 if(xx==tox) weight = (weight*xweight2)/256;
2095 if(yy==fromy) weight = (weight*yweight1)/256;
2096 if(yy==toy) weight = (weight*yweight2)/256;
2099 //if(a) a=(palettesize-1)*r/blocksize;
2100 newdata[y*newwidth+x] = (a*blocksize)/r;
2108 #define IMAGE_TYPE_JPEG 0
2109 #define IMAGE_TYPE_LOSSLESS 1
2111 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2112 double x1,double y1,
2113 double x2,double y2,
2114 double x3,double y3,
2115 double x4,double y4, int type, int multiply)
2117 gfxcolor_t*newpic=0;
2119 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2120 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2122 gfxline_t p1,p2,p3,p4,p5;
2123 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2124 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2125 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2126 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2127 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2129 {p1.x = (int)(p1.x*20)/20.0;
2130 p1.y = (int)(p1.y*20)/20.0;
2131 p2.x = (int)(p2.x*20)/20.0;
2132 p2.y = (int)(p2.y*20)/20.0;
2133 p3.x = (int)(p3.x*20)/20.0;
2134 p3.y = (int)(p3.y*20)/20.0;
2135 p4.x = (int)(p4.x*20)/20.0;
2136 p4.y = (int)(p4.y*20)/20.0;
2137 p5.x = (int)(p5.x*20)/20.0;
2138 p5.y = (int)(p5.y*20)/20.0;
2142 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2143 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2145 m.tx = p1.x - 0.5*multiply;
2146 m.ty = p1.y - 0.5*multiply;
2149 img.data = (gfxcolor_t*)data;
2153 if(type == IMAGE_TYPE_JPEG)
2154 /* TODO: pass image_dpi to device instead */
2155 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2158 dev->fillbitmap(dev, &p1, &img, &m, 0);
2161 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2162 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2164 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2167 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2168 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2170 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2174 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2175 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2176 GBool inlineImg, int mask, int*maskColors,
2177 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2179 /* the code in this function is *old*. It's not pretty, but it works. */
2181 double x1,y1,x2,y2,x3,y3,x4,y4;
2182 ImageStream *imgStr;
2187 unsigned char* maskbitmap = 0;
2190 ncomps = colorMap->getNumPixelComps();
2191 bits = colorMap->getBits();
2196 unsigned char buf[8];
2197 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2199 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2200 imgMaskStr->reset();
2201 unsigned char pal[256];
2202 int n = 1 << colorMap->getBits();
2207 maskColorMap->getGray(pixBuf, &gray);
2208 pal[t] = colToByte(gray);
2210 for (y = 0; y < maskHeight; y++) {
2211 for (x = 0; x < maskWidth; x++) {
2212 imgMaskStr->getPixel(buf);
2213 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2218 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2219 imgMaskStr->reset();
2220 for (y = 0; y < maskHeight; y++) {
2221 for (x = 0; x < maskWidth; x++) {
2222 imgMaskStr->getPixel(buf);
2224 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2232 imgStr = new ImageStream(str, width, ncomps,bits);
2235 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2237 msg("<verbose> Ignoring %d by %d image", width, height);
2238 unsigned char buf[8];
2240 for (y = 0; y < height; ++y)
2241 for (x = 0; x < width; ++x) {
2242 imgStr->getPixel(buf);
2250 this->transformXY(state, 0, 1, &x1, &y1);
2251 this->transformXY(state, 0, 0, &x2, &y2);
2252 this->transformXY(state, 1, 0, &x3, &y3);
2253 this->transformXY(state, 1, 1, &x4, &y4);
2256 /* as type 3 bitmaps are antialized, we need to place them
2257 at integer coordinates, otherwise flash player's antializing
2258 will kick in and make everything blurry */
2259 x1 = (int)(x1);y1 = (int)(y1);
2260 x2 = (int)(x2);y2 = (int)(y2);
2261 x3 = (int)(x3);y3 = (int)(y3);
2262 x4 = (int)(x4);y4 = (int)(y4);
2265 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2267 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2268 gfxglobals->pbminfo = 1;
2271 msg("<verbose> drawing %d by %d masked picture", width, height);
2273 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2274 msg("<notice> File contains jpeg pictures");
2275 gfxglobals->jpeginfo = 1;
2279 unsigned char buf[8];
2281 unsigned char*pic = new unsigned char[width*height];
2282 gfxcolor_t pal[256];
2284 state->getFillRGB(&rgb);
2286 memset(pal,255,sizeof(pal));
2287 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2288 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2289 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2290 pal[0].a = 255; pal[1].a = 0;
2293 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2294 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2295 for (y = 0; y < height; ++y)
2296 for (x = 0; x < width; ++x)
2298 imgStr->getPixel(buf);
2301 pic[width*y+x] = buf[0];
2305 unsigned char*pic2 = 0;
2308 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2317 height = realheight;
2321 /* make a black/white palette */
2323 float r = 255./(float)(numpalette-1);
2325 for(t=0;t<numpalette;t++) {
2326 pal[t].r = colToByte(rgb.r);
2327 pal[t].g = colToByte(rgb.g);
2328 pal[t].b = colToByte(rgb.b);
2329 pal[t].a = (unsigned char)(t*r);
2334 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2335 for (y = 0; y < height; ++y) {
2336 for (x = 0; x < width; ++x) {
2337 pic2[width*y+x] = pal[pic[y*width+x]];
2340 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2344 if(maskbitmap) free(maskbitmap);
2350 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2351 gfxcolor_t*pic=new gfxcolor_t[width*height];
2352 for (y = 0; y < height; ++y) {
2353 for (x = 0; x < width; ++x) {
2354 imgStr->getPixel(pixBuf);
2355 colorMap->getRGB(pixBuf, &rgb);
2356 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2357 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2358 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2359 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2361 int x1 = x*maskWidth/width;
2362 int y1 = y*maskHeight/height;
2363 int x2 = (x+1)*maskWidth/width;
2364 int y2 = (y+1)*maskHeight/height;
2366 unsigned int alpha=0;
2367 unsigned int count=0;
2368 for(xx=x1;xx<x2;xx++)
2369 for(yy=y1;yy<y2;yy++) {
2370 alpha += maskbitmap[yy*maskWidth+xx];
2374 pic[width*y+x].a = alpha / count;
2376 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2381 if(str->getKind()==strDCT)
2382 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2384 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2387 if(maskbitmap) free(maskbitmap);
2390 gfxcolor_t*pic=new gfxcolor_t[width*height];
2391 gfxcolor_t pal[256];
2392 int n = 1 << colorMap->getBits();
2394 for(t=0;t<256;t++) {
2396 colorMap->getRGB(pixBuf, &rgb);
2397 pal[t].r = (unsigned char)(colToByte(rgb.r));
2398 pal[t].g = (unsigned char)(colToByte(rgb.g));
2399 pal[t].b = (unsigned char)(colToByte(rgb.b));
2400 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2402 for (y = 0; y < height; ++y) {
2403 for (x = 0; x < width; ++x) {
2404 imgStr->getPixel(pixBuf);
2405 pic[width*y+x] = pal[pixBuf[0]];
2406 if(maskColors && *maskColors==pixBuf[0]) {
2407 pic[width*y+x].a = 0;
2412 if(maskWidth < width && maskHeight < height) {
2413 for(y = 0; y < height; y++) {
2414 for (x = 0; x < width; x++) {
2415 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2419 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2420 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2421 double dx = width / (double)maskWidth;
2422 double dy = height / (double)maskHeight;
2424 for(y = 0; y < maskHeight; y++) {
2426 for (x = 0; x < maskWidth; x++) {
2427 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2428 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2436 height = maskHeight;
2439 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2443 if(maskbitmap) free(maskbitmap);
2448 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2449 int width, int height, GBool invert,
2452 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2453 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2454 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2457 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2458 int width, int height, GfxImageColorMap *colorMap,
2459 int *maskColors, GBool inlineImg)
2461 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2462 colorMap?"colorMap":"no colorMap",
2463 maskColors?"maskColors":"no maskColors",
2465 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2466 colorMap?"colorMap":"no colorMap",
2467 maskColors?"maskColors":"no maskColors",
2470 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2471 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2472 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2475 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2476 int width, int height,
2477 GfxImageColorMap *colorMap,
2478 Stream *maskStr, int maskWidth, int maskHeight,
2481 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2482 colorMap?"colorMap":"no colorMap",
2483 maskWidth, maskHeight);
2484 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2485 colorMap?"colorMap":"no colorMap",
2486 maskWidth, maskHeight);
2488 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2489 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2490 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2493 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2494 int width, int height,
2495 GfxImageColorMap *colorMap,
2497 int maskWidth, int maskHeight,
2498 GfxImageColorMap *maskColorMap)
2500 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2501 colorMap?"colorMap":"no colorMap",
2502 maskWidth, maskHeight);
2503 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2504 colorMap?"colorMap":"no colorMap",
2505 maskWidth, maskHeight);
2507 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2508 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2509 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2512 void GFXOutputDev::stroke(GfxState *state)
2516 GfxPath * path = state->getPath();
2517 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2518 strokeGfxline(state, line, 0);
2522 void GFXOutputDev::fill(GfxState *state)
2524 gfxcolor_t col = getFillColor(state);
2525 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2527 GfxPath * path = state->getPath();
2528 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2529 if(!config_disable_polygon_conversion) {
2530 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2534 fillGfxLine(state, line, 0);
2538 void GFXOutputDev::eoFill(GfxState *state)
2540 gfxcolor_t col = getFillColor(state);
2541 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2543 GfxPath * path = state->getPath();
2544 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2545 fillGfxLine(state, line, 1);
2550 static const char* dirseparator()
2559 void addGlobalFont(const char*filename)
2561 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2562 memset(f, 0, sizeof(fontfile_t));
2563 f->filename = filename;
2564 int len = strlen(filename);
2565 char*r1 = strrchr((char*)filename, '/');
2566 char*r2 = strrchr((char*)filename, '\\');
2574 msg("<verbose> Adding font \"%s\".", filename);
2575 if(global_fonts_next) {
2576 global_fonts_next->next = f;
2577 global_fonts_next = global_fonts_next->next;
2579 global_fonts_next = global_fonts = f;
2583 void addGlobalLanguageDir(const char*dir)
2585 msg("<notice> Adding %s to language pack directories", dir);
2588 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2589 strcpy(config_file, dir);
2590 strcat(config_file, dirseparator());
2591 strcat(config_file, "add-to-xpdfrc");
2593 fi = fopen(config_file, "rb");
2595 msg("<error> Could not open %s", config_file);
2598 globalParams->parseFile(new GString(config_file), fi);
2602 void addGlobalFontDir(const char*dirname)
2604 #ifdef HAVE_DIRENT_H
2605 DIR*dir = opendir(dirname);
2607 msg("<warning> Couldn't open directory %s", dirname);
2613 ent = readdir (dir);
2617 char*name = ent->d_name;
2623 if(!strncasecmp(&name[l-4], ".pfa", 4))
2625 if(!strncasecmp(&name[l-4], ".pfb", 4))
2627 if(!strncasecmp(&name[l-4], ".ttf", 4))
2630 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2631 strcpy(fontname, dirname);
2632 strcat(fontname, dirseparator());
2633 strcat(fontname, name);
2634 addGlobalFont(fontname);
2638 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2641 msg("<warning> No dirent.h");
2645 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2646 GfxColorSpace *blendingColorSpace,
2647 GBool isolated, GBool knockout,
2650 const char*colormodename = "";
2652 if(blendingColorSpace) {
2653 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2655 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);
2656 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);
2658 //states[statepos].createsoftmask |= forSoftMask;
2659 states[statepos].createsoftmask = forSoftMask;
2660 states[statepos].transparencygroup = !forSoftMask;
2661 states[statepos].isolated = isolated;
2663 states[statepos].olddevice = this->device;
2664 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2665 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2667 gfxdevice_record_init(this->device, 0);
2669 /*if(!forSoftMask) { ////???
2670 state->setFillOpacity(0.0);
2675 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2678 gfxdevice_t*r = this->device;
2680 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2682 this->device = states[statepos].olddevice;
2684 msg("<error> Invalid state nesting");
2686 states[statepos].olddevice = 0;
2688 gfxresult_t*recording = r->finish(r);
2690 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2691 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2693 if(states[statepos].createsoftmask) {
2694 states[statepos-1].softmaskrecording = recording;
2696 states[statepos-1].grouprecording = recording;
2699 states[statepos].createsoftmask = 0;
2700 states[statepos].transparencygroup = 0;
2704 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2706 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2707 "colordodge","colorburn","hardlight","softlight","difference",
2708 "exclusion","hue","saturation","color","luminosity"};
2710 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2711 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2713 if(state->getBlendMode() == gfxBlendNormal)
2714 infofeature("transparency groups");
2717 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2718 warnfeature(buffer, 0);
2721 gfxresult_t*grouprecording = states[statepos].grouprecording;
2723 int blendmode = state->getBlendMode();
2724 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2725 int alpha = (int)(state->getFillOpacity()*255);
2726 if(blendmode == gfxBlendMultiply && alpha>200)
2729 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2730 gfxdevice_ops_init(&ops, this->device, alpha);
2731 gfxresult_record_replay(grouprecording, &ops);
2734 grouprecording->destroy(grouprecording);
2736 states[statepos].grouprecording = 0;
2739 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2741 if(states[statepos].softmask) {
2742 /* shouldn't happen, but *does* happen */
2743 clearSoftMask(state);
2746 /* alpha = 1: retrieve mask values from alpha layer
2747 alpha = 0: retrieve mask values from luminance */
2749 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2750 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2751 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2752 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2754 infofeature("soft masks");
2756 warnfeature("soft masks from alpha channel",0);
2758 if(states[statepos].olddevice) {
2759 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2762 states[statepos].olddevice = this->device;
2763 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2764 gfxdevice_record_init(this->device, 0);
2766 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2768 states[statepos].softmask = 1;
2769 states[statepos].softmask_alpha = alpha;
2772 static inline Guchar div255(int x) {
2773 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2776 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2778 if(c < min) c = min;
2779 if(c > max) c = max;
2783 void GFXOutputDev::clearSoftMask(GfxState *state)
2785 if(!states[statepos].softmask)
2787 states[statepos].softmask = 0;
2788 dbg("clearSoftMask statepos=%d", statepos);
2789 msg("<verbose> clearSoftMask statepos=%d", statepos);
2791 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2792 msg("<error> Error in softmask/tgroup ordering");
2796 gfxresult_t*mask = states[statepos].softmaskrecording;
2797 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2798 this->device = states[statepos].olddevice;
2800 /* get outline of all objects below the soft mask */
2801 gfxdevice_t uniondev;
2802 gfxdevice_union_init(&uniondev, 0);
2803 gfxresult_record_replay(below, &uniondev);
2804 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2805 uniondev.finish(&uniondev);
2806 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2807 gfxline_free(belowoutline);belowoutline=0;
2809 this->device->startclip(this->device, belowoutline);
2810 gfxresult_record_replay(below, this->device);
2811 gfxresult_record_replay(mask, this->device);
2812 this->device->endclip(this->device);
2815 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2816 if(width<=0 || height<=0)
2819 gfxdevice_t belowrender;
2820 gfxdevice_render_init(&belowrender);
2821 if(states[statepos+1].isolated) {
2822 belowrender.setparameter(&belowrender, "fillwhite", "1");
2824 belowrender.setparameter(&belowrender, "antialize", "2");
2825 belowrender.startpage(&belowrender, width, height);
2826 gfxresult_record_replay(below, &belowrender);
2827 belowrender.endpage(&belowrender);
2828 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2829 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2830 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2832 gfxdevice_t maskrender;
2833 gfxdevice_render_init(&maskrender);
2834 maskrender.startpage(&maskrender, width, height);
2835 gfxresult_record_replay(mask, &maskrender);
2836 maskrender.endpage(&maskrender);
2837 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2838 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2840 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2841 msg("<fatal> Internal error in mask drawing");
2846 for(y=0;y<height;y++) {
2847 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2848 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2849 for(x=0;x<width;x++) {
2851 if(states[statepos].softmask_alpha) {
2854 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2857 l2->a = div255(alpha*l2->a);
2859 /* DON'T premultiply alpha- this is done by fillbitmap,
2860 depending on the output device */
2861 //l2->r = div255(alpha*l2->r);
2862 //l2->g = div255(alpha*l2->g);
2863 //l2->b = div255(alpha*l2->b);
2869 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2872 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2873 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2875 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2877 mask->destroy(mask);
2878 below->destroy(below);
2879 maskresult->destroy(maskresult);
2880 belowresult->destroy(belowresult);
2881 states[statepos].softmaskrecording = 0;
2886 // public: ~MemCheck()
2888 // delete globalParams;globalParams=0;
2889 // Object::memCheck(stderr);
2890 // gMemReport(stderr);