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;
108 DisplayFontParam *dfp;
110 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
111 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
112 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
113 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
114 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
115 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
116 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
117 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
118 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
119 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
120 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
121 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
122 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
123 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
126 static int verbose = 0;
127 static int dbgindent = 1;
128 static void dbg(const char*format, ...)
135 va_start(arglist, format);
136 vsnprintf(buf, sizeof(buf)-1, format, arglist);
139 while(l && buf[l-1]=='\n') {
144 int indent = dbgindent;
153 GFXOutputGlobals*gfxglobals=0;
155 GFXOutputGlobals::GFXOutputGlobals()
157 this->featurewarnings = 0;
159 this->textmodeinfo = 0;
163 GFXOutputGlobals::~GFXOutputGlobals()
165 feature_t*f = this->featurewarnings;
167 feature_t*next = f->next;
169 free(f->string);f->string =0;
175 this->featurewarnings = 0;
178 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
180 feature_t*f = gfxglobals->featurewarnings;
182 if(!strcmp(feature, f->string))
186 f = (feature_t*)malloc(sizeof(feature_t));
187 f->string = strdup(feature);
188 f->next = gfxglobals->featurewarnings;
189 gfxglobals->featurewarnings = f;
191 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
192 if(this->config_break_on_warning) {
193 msg("<fatal> Aborting conversion due to unsupported feature");
197 msg("<notice> File contains %s",feature);
200 void GFXOutputDev::warnfeature(const char*feature,char fully)
202 showfeature(feature,fully,1);
204 void GFXOutputDev::infofeature(const char*feature)
206 showfeature(feature,0,0);
209 GFXOutputState::GFXOutputState() {
211 this->createsoftmask = 0;
212 this->transparencygroup = 0;
214 this->grouprecording = 0;
218 GBool GFXOutputDev::interpretType3Chars()
223 typedef struct _drawnchar
241 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
242 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
247 free(chars);chars = 0;
254 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
258 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
261 chars[num_chars].x = x;
262 chars[num_chars].y = y;
263 chars[num_chars].color = color;
264 chars[num_chars].charid = charid;
268 char* writeOutStdFont(fontentry* f)
273 char* tmpFileName = mktmpname(namebuf1);
275 sprintf(namebuf2, "%s.afm", tmpFileName);
276 fi = fopen(namebuf2, "wb");
279 fwrite(f->afm, 1, f->afmlen, fi);
282 sprintf(namebuf2, "%s.pfb", tmpFileName);
283 fi = fopen(namebuf2, "wb");
286 fwrite(f->pfb, 1, f->pfblen, fi);
288 return strdup(namebuf2);
290 void unlinkfont(char* filename)
295 msg("<verbose> Removing temporary font file %s", filename);
298 if(!strncmp(&filename[l-4],".afm",4)) {
299 memcpy(&filename[l-4],".pfb",4); unlink(filename);
300 memcpy(&filename[l-4],".pfa",4); unlink(filename);
301 memcpy(&filename[l-4],".afm",4);
304 if(!strncmp(&filename[l-4],".pfa",4)) {
305 memcpy(&filename[l-4],".afm",4); unlink(filename);
306 memcpy(&filename[l-4],".pfa",4);
309 if(!strncmp(&filename[l-4],".pfb",4)) {
310 memcpy(&filename[l-4],".afm",4); unlink(filename);
311 memcpy(&filename[l-4],".pfb",4);
316 static int config_use_fontconfig = 1;
317 static int fcinitcalled = 0;
319 GFXGlobalParams::GFXGlobalParams()
320 : GlobalParams((char*)"")
322 //setupBaseFonts(char *dir); //not tested yet
324 GFXGlobalParams::~GFXGlobalParams()
326 msg("<verbose> Performing cleanups");
328 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
329 if(pdf2t1map[t].fullfilename) {
330 unlinkfont(pdf2t1map[t].fullfilename);
333 #ifdef HAVE_FONTCONFIG
334 if(config_use_fontconfig && fcinitcalled)
338 #ifdef HAVE_FONTCONFIG
339 static char stralphacmp(const char*s1, const char*s2)
342 /* skip over space, minus, comma etc. */
343 while(*s1>=32 && *s1<=63) s1++;
344 while(*s2>=32 && *s2<=63) s2++;
352 static char fc_ismatch(FcPattern*match, char*family, char*style)
354 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
355 FcBool scalable=FcFalse, outline=FcFalse;
356 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
357 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
358 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
359 FcPatternGetBool(match, "outline", 0, &outline);
360 FcPatternGetBool(match, "scalable", 0, &scalable);
362 if(scalable!=FcTrue || outline!=FcTrue)
365 if (!stralphacmp(fcfamily, family)) {
366 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
369 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
375 static inline char islowercase(char c)
377 return (c>='a' && c<='z');
380 char* fontconfig_searchForFont(char*name)
382 #ifdef HAVE_FONTCONFIG
383 if(!config_use_fontconfig)
386 // call init ony once
390 // check whether we have a config file
391 char* configfile = (char*)FcConfigFilename(0);
392 int configexists = 0;
393 FILE*fi = fopen(configfile, "rb");
395 configexists = 1;fclose(fi);
396 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
398 msg("<debug> Initializing FontConfig (no configfile)");
402 /* A fontconfig instance which didn't find a configfile is unbelievably
403 cranky, so let's just write out a small xml file and make fontconfig
405 FcConfig*c = FcConfigCreate();
407 char* tmpFileName = mktmpname(namebuf);
408 FILE*fi = fopen(tmpFileName, "wb");
409 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
411 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
413 fprintf(fi, "<dir>~/.fonts</dir>\n");
415 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
417 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
418 fprintf(fi, "</fontconfig>\n");
420 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
421 FcConfigBuildFonts(c);
422 FcConfigSetCurrent(c);
426 msg("<debug> FontConfig Initialization failed. Disabling.");
427 config_use_fontconfig = 0;
430 FcConfig * config = FcConfigGetCurrent();
432 msg("<debug> FontConfig Config Initialization failed. Disabling.");
433 config_use_fontconfig = 0;
437 /* add external fonts to fontconfig's config, too. */
438 fontfile_t*fd = global_fonts;
440 FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
441 msg("<debug> Adding font %s to fontconfig", fd->filename);
445 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
446 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
447 if(!set || !set->nfont) {
448 msg("<debug> FontConfig has zero fonts. Disabling.");
449 config_use_fontconfig = 0;
453 if(getLogLevel() >= LOGLEVEL_TRACE) {
458 for(t=0;t<set->nfont;t++) {
459 char*fcfamily=0,*fcstyle=0,*filename=0;
460 FcBool scalable=FcFalse, outline=FcFalse;
461 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
462 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
463 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
464 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
465 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
466 if(scalable && outline) {
467 msg("<trace> %s (%s) -> %s", fcfamily, fcstyle, filename);
471 set = FcConfigGetFonts(config, FcSetApplication);
476 char*family = strdup(name);
477 int len = strlen(family);
479 char*styles[] = {"Medium", "Regular", "Bold", "Italic", "Black", "Narrow"};
482 for(t=0;t<sizeof(styles)/sizeof(styles[0]);t++) {
483 int l = strlen(styles[t]);
484 if(len>l+1 && !strcmp(family+len-l, styles[t]) && islowercase(family[len-l-1])) {
491 char*dash = strchr(family, '-');
492 if(!dash) dash = strchr(family, ',');
498 FcPattern*pattern = 0;
500 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
501 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
503 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
504 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
506 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
509 FcConfigSubstitute(0, pattern, FcMatchPattern);
510 FcDefaultSubstitute(pattern);
512 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
515 for(t=0;t<set->nfont;t++) {
516 FcPattern*match = set->fonts[t];
517 //FcPattern*match = FcFontMatch(0, pattern, &result);
518 if(fc_ismatch(match, family, style)) {
520 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
521 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
524 //FcPatternDestroy(match);
525 msg("<debug> fontconfig: returning filename %s", filename);
527 FcPatternDestroy(pattern);
528 FcFontSetDestroy(set);
529 return filename?strdup(filename):0;
534 FcPatternDestroy(pattern);
535 FcFontSetDestroy(set);
542 static DisplayFontParamKind detectFontType(const char*filename)
544 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
545 return displayFontTT;
546 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
547 return displayFontT1;
548 return displayFontTT;
551 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
553 msg("<verbose> looking for font %s", fontName->getCString());
555 char*name = fontName->getCString();
557 /* see if it is a pdf standard font */
559 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
560 if(!strcmp(name, pdf2t1map[t].pdffont)) {
561 if(!pdf2t1map[t].fullfilename) {
562 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
563 if(!pdf2t1map[t].fullfilename) {
564 msg("<error> Couldn't save default font- is the Temp Directory writable?");
566 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
568 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
569 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
570 pdf2t1map[t].dfp = dfp;
572 return pdf2t1map[t].dfp;
576 int bestlen = 0x7fffffff;
577 const char*bestfilename = 0;
579 #ifndef HAVE_FONTCONFIG
580 /* if we don't have fontconfig, try a simple filename-comparison approach */
581 fontfile_t*f = global_fonts;
583 if(strstr(f->filename, name)) {
584 if(f->len < bestlen) {
586 bestfilename = f->filename;
593 /* if we didn't find anything up to now, try looking for the
594 font via fontconfig */
597 filename = fontconfig_searchForFont(name);
599 filename = strdup(bestfilename);
603 msg("<verbose> Font %s maps to %s\n", name, filename);
604 DisplayFontParamKind kind = detectFontType(filename);
605 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
606 if(kind == displayFontTT) {
607 dfp->tt.fileName = new GString(filename);
609 dfp->t1.fileName = new GString(filename);
614 msg("<verbose> Font %s not found\n", name);
615 return GlobalParams::getDisplayFont(fontName);
619 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
622 gfxglobals = new GFXOutputGlobals();
626 this->xref = doc->getXRef();
628 this->type3active = 0;
631 this->user_movex = 0;
632 this->user_movey = 0;
635 this->user_clipx1 = 0;
636 this->user_clipy1 = 0;
637 this->user_clipx2 = 0;
638 this->user_clipy2 = 0;
639 this->current_gfxfont = 0;
640 this->current_fontinfo = 0;
641 this->current_text_stroke = 0;
642 this->current_text_clip = 0;
643 this->outer_clip_box = 0;
644 this->config_bigchar=0;
645 this->config_convertgradients=1;
646 this->config_break_on_warning=0;
647 this->config_remapunicode=0;
648 this->config_transparent=0;
649 this->config_extrafontdata = 0;
650 this->config_drawonlyshapes = 0;
651 this->config_disable_polygon_conversion = 0;
652 this->config_multiply = 1;
653 this->config_detectspaces = 1;
654 this->config_linkdatafile = 0;
658 memset(states, 0, sizeof(states));
661 void GFXOutputDev::setParameter(const char*key, const char*value)
663 if(!strcmp(key,"breakonwarning")) {
664 this->config_break_on_warning = atoi(value);
665 } else if(!strcmp(key,"remapunicode")) {
666 this->config_remapunicode = atoi(value);
667 } else if(!strcmp(key,"transparent")) {
668 this->config_transparent = atoi(value);
669 } else if(!strcmp(key,"drawonlyshapes")) {
670 this->config_drawonlyshapes = atoi(value);
671 } else if(!strcmp(key,"detectspaces")) {
672 this->config_detectspaces = atoi(value);
673 } else if(!strcmp(key,"extrafontdata")) {
674 this->config_extrafontdata = atoi(value);
675 } else if(!strcmp(key,"linkdatafile")) {
676 this->config_linkdatafile = strdup(value);
677 } else if(!strcmp(key,"convertgradients")) {
678 this->config_convertgradients = atoi(value);
679 } else if(!strcmp(key,"textonly")) {
680 this->config_textonly = atoi(value);
681 } else if(!strcmp(key,"multiply")) {
682 this->config_multiply = atoi(value);
683 if(this->config_multiply<1)
684 this->config_multiply=1;
685 } else if(!strcmp(key,"disable_polygon_conversion")) {
686 this->config_disable_polygon_conversion = atoi(value);
690 void GFXOutputDev::setDevice(gfxdevice_t*dev)
695 void GFXOutputDev::setMove(int x,int y)
697 this->user_movex = x;
698 this->user_movey = y;
701 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
703 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
704 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
706 this->user_clipx1 = x1;
707 this->user_clipy1 = y1;
708 this->user_clipx2 = x2;
709 this->user_clipy2 = y2;
712 static char*getFontName(GfxFont*font)
715 GString*gstr = font->getName();
716 char* fname = gstr==0?0:gstr->getCString();
720 sprintf(buf, "UFONT%d", r->num);
721 fontid = strdup(buf);
723 fontid = strdup(fname);
727 char* plus = strchr(fontid, '+');
728 if(plus && plus < &fontid[strlen(fontid)-1]) {
729 fontname = strdup(plus+1);
731 fontname = strdup(fontid);
737 static void dumpFontInfo(const char*loglevel, GfxFont*font);
738 static int lastdumps[1024];
739 static int lastdumppos = 0;
744 static void showFontError(GfxFont*font, int nr)
748 for(t=0;t<lastdumppos;t++)
749 if(lastdumps[t] == r->num)
753 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
754 lastdumps[lastdumppos++] = r->num;
756 msg("<warning> The following font caused problems:");
758 msg("<warning> The following font caused problems (substituting):");
760 msg("<warning> The following Type 3 Font will be rendered as graphics:");
761 dumpFontInfo("<warning>", font);
764 static void dumpFontInfo(const char*loglevel, GfxFont*font)
766 char* id = getFontID(font);
767 char* name = getFontName(font);
768 Ref* r=font->getID();
769 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
771 GString*gstr = font->getTag();
773 msg("%s| Tag: %s", loglevel, id);
775 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
777 GfxFontType type=font->getType();
779 case fontUnknownType:
780 msg("%s| Type: unknown",loglevel);
783 msg("%s| Type: 1",loglevel);
786 msg("%s| Type: 1C",loglevel);
789 msg("%s| Type: 3",loglevel);
792 msg("%s| Type: TrueType",loglevel);
795 msg("%s| Type: CIDType0",loglevel);
798 msg("%s| Type: CIDType0C",loglevel);
801 msg("%s| Type: CIDType2",loglevel);
806 GBool embedded = font->getEmbeddedFontID(&embRef);
808 if(font->getEmbeddedFontName()) {
809 embeddedName = font->getEmbeddedFontName()->getCString();
812 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
814 gstr = font->getExtFontFile();
816 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
818 // Get font descriptor flags.
819 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
820 if(font->isSerif()) msg("%s| is serif", loglevel);
821 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
822 if(font->isItalic()) msg("%s| is italic", loglevel);
823 if(font->isBold()) msg("%s| is bold", loglevel);
829 //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");}
830 //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");}
832 void dump_outline(gfxline_t*line)
834 /*gfxbbox_t*r = gfxline_isrectangle(line);
836 printf("is not a rectangle\n");
838 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
842 if(line->type == gfx_moveTo) {
843 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
844 } else if(line->type == gfx_lineTo) {
845 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
846 } else if(line->type == gfx_splineTo) {
847 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
853 void gfxPath_dump(GfxPath*path)
855 int num = path->getNumSubpaths();
858 for(t = 0; t < num; t++) {
859 GfxSubpath *subpath = path->getSubpath(t);
860 int subnum = subpath->getNumPoints();
862 for(s=0;s<subnum;s++) {
863 double x=subpath->getX(s);
864 double y=subpath->getY(s);
865 if(s==0 && !subpath->getCurve(s)) {
866 printf("M %f %f\n", x, y);
867 } else if(s==0 && subpath->getCurve(s)) {
868 printf("E %f %f\n", x, y);
869 } else if(subpath->getCurve(s)) {
870 printf("C %f %f\n", x, y);
872 printf("T %f %f\n", x, y);
878 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
880 int num = path->getNumSubpaths();
883 double lastx=0,lasty=0,posx=0,posy=0;
886 msg("<warning> empty path");
890 gfxdrawer_target_gfxline(&draw);
892 for(t = 0; t < num; t++) {
893 GfxSubpath *subpath = path->getSubpath(t);
894 int subnum = subpath->getNumPoints();
895 double bx=0,by=0,cx=0,cy=0;
897 for(s=0;s<subnum;s++) {
900 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
903 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
904 draw.lineTo(&draw, lastx, lasty);
906 draw.moveTo(&draw, x,y);
911 } else if(subpath->getCurve(s) && cpos==0) {
915 } else if(subpath->getCurve(s) && cpos==1) {
923 draw.lineTo(&draw, x,y);
925 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
932 /* fix non-closed lines */
933 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
934 draw.lineTo(&draw, lastx, lasty);
936 gfxline_t*result = (gfxline_t*)draw.result(&draw);
938 gfxline_optimize(result);
943 GBool GFXOutputDev::useTilingPatternFill()
945 infofeature("tiled patterns");
946 // if(config_convertgradients)
950 GBool GFXOutputDev::useShadedFills()
952 infofeature("shaded fills");
953 if(config_convertgradients)
958 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
960 state->transform(x,y,nx,ny);
961 *nx += user_movex + clipmovex;
962 *ny += user_movey + clipmovey;
966 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
967 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
968 int paintType, Dict *resDict,
969 double *mat, double *bbox,
970 int x0, int y0, int x1, int y1,
971 double xStep, double yStep)
973 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
974 int paintType, Dict *resDict,
975 double *mat, double *bbox,
976 int x0, int y0, int x1, int y1,
977 double xStep, double yStep)
980 msg("<debug> tilingPatternFill");
981 infofeature("tiling pattern fills");
984 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
986 msg("<error> functionShadedFill not supported yet");
987 infofeature("function shaded fills");
990 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
994 colspace->getRGB(col, &rgb);
995 c.r = colToByte(rgb.r);
996 c.g = colToByte(rgb.g);
997 c.b = colToByte(rgb.b);
1002 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
1004 if(config_textonly) {return gTrue;}
1006 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
1007 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
1010 this->transformXY(state, x0,y0, &x0,&y0);
1011 this->transformXY(state, x1,y1, &x1,&y1);
1012 this->transformXY(state, x2,y2, &x2,&y2);
1017 shading->getColor(0.0, &color0);
1018 shading->getColor(0.5, &color1);
1019 shading->getColor(1.0, &color2);
1021 GfxColorSpace* colspace = shading->getColorSpace();
1023 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
1024 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1025 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1026 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
1027 infofeature("radial shaded fills");
1029 gfxgradient_t gr[3];
1030 gfxgradient_t*g = &gr[0];
1034 g[0].color = col2col(colspace, &color0);
1035 g[1].color = col2col(colspace, &color1);
1036 g[2].color = col2col(colspace, &color2);
1041 gfxbbox_t b = states[statepos].clipbbox;
1042 gfxline_t p1,p2,p3,p4,p5;
1043 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1044 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1045 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1046 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1047 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1050 //m.m00 = (x3-x0); m.m10 = (x1-x0);
1051 //m.m01 = (y3-y0); m.m11 = (y1-y0);
1052 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1053 m.m00 = (x1-x0); m.m10 = (x2-x0);
1054 m.m01 = (y1-y0); m.m11 = (y2-y0);
1058 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1062 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1064 if(config_textonly) {return gTrue;}
1067 shading->getCoords(&x0,&y0,&x1,&y1);
1068 this->transformXY(state, x0,y0,&x0,&y0);
1069 this->transformXY(state, x1,y1,&x1,&y1);
1074 shading->getColor(0.0, &color0);
1075 shading->getColor(0.5, &color1);
1076 shading->getColor(1.0, &color2);
1078 GfxColorSpace* colspace = shading->getColorSpace();
1080 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1081 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1082 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1083 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1085 infofeature("axial shaded fills");
1087 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1091 g[0].color = col2col(colspace, &color0);
1092 g[1].color = col2col(colspace, &color1);
1093 g[2].color = col2col(colspace, &color2);
1098 double xMin,yMin,xMax,yMax;
1099 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1100 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1101 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1104 xMin = 1024; yMin = 1024;
1106 gfxbbox_t b = states[statepos].clipbbox;
1107 gfxline_t p1,p2,p3,p4,p5;
1108 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1109 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1110 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1111 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1112 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1114 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1115 the middle of the two control points */
1117 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1118 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1119 m.tx = (x0 + x1)/2 - 0.5;
1120 m.ty = (y0 + y1)/2 - 0.5;
1122 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1128 GBool GFXOutputDev::useDrawForm()
1130 infofeature("forms");
1133 void GFXOutputDev::drawForm(Ref id)
1135 msg("<error> drawForm not implemented");
1137 GBool GFXOutputDev::needNonText()
1141 void GFXOutputDev::endPage()
1143 msg("<verbose> endPage (GfxOutputDev)");
1144 if(outer_clip_box) {
1145 device->endclip(device);
1148 /* notice: we're not fully done yet with this page- there might still be
1149 a few calls to drawLink() yet to come */
1152 static inline double sqr(double x) {return x*x;}
1154 #define STROKE_FILL 1
1155 #define STROKE_CLIP 2
1156 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1158 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1159 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1160 double miterLimit = state->getMiterLimit();
1161 double width = state->getTransformedLineWidth();
1164 double opaq = state->getStrokeOpacity();
1166 state->getFillRGB(&rgb);
1168 state->getStrokeRGB(&rgb);
1170 col.r = colToByte(rgb.r);
1171 col.g = colToByte(rgb.g);
1172 col.b = colToByte(rgb.b);
1173 col.a = (unsigned char)(opaq*255);
1175 gfx_capType capType = gfx_capRound;
1176 if(lineCap == 0) capType = gfx_capButt;
1177 else if(lineCap == 1) capType = gfx_capRound;
1178 else if(lineCap == 2) capType = gfx_capSquare;
1179 else msg("<error> Invalid line cap type");
1181 gfx_joinType joinType = gfx_joinRound;
1182 if(lineJoin == 0) joinType = gfx_joinMiter;
1183 else if(lineJoin == 1) joinType = gfx_joinRound;
1184 else if(lineJoin == 2) joinType = gfx_joinBevel;
1185 else msg("<error> Invalid line join type");
1187 gfxline_t*line2 = 0;
1189 int dashLength = states[statepos].dashLength;
1190 double*dashPattern = states[statepos].dashPattern;
1191 double dashStart = states[statepos].dashStart;
1192 if(dashLength && dashPattern) {
1193 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1196 /* try to find out how much the transformation matrix would
1197 stretch the dashes, and factor that into the dash lengths.
1198 This is not the entirely correct approach- it would be
1199 better to first convert the path to an unscaled version,
1200 then apply dashing, and then transform the path using
1201 the current transformation matrix. However there are few
1202 PDFs which actually stretch a dashed path in a non-orthonormal
1204 double tx1, ty1, tx2, ty2, tx3, ty3;
1205 this->transformXY(state, 0, 0, &tx1, &ty1);
1206 this->transformXY(state, 0, 1, &tx2, &ty2);
1207 this->transformXY(state, 1, 0, &tx3, &ty3);
1208 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1209 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1211 warnfeature("non-ortogonally dashed strokes", 0);
1212 double f = (d1+d2)/2;
1214 msg("<trace> %d dashes", dashLength);
1215 msg("<trace> | phase: %f", dashStart);
1216 for(t=0;t<dashLength;t++) {
1217 dash[t] = (float)dashPattern[t] * f;
1221 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1223 dash[dashLength] = -1;
1224 if(getLogLevel() >= LOGLEVEL_TRACE) {
1228 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1232 msg("<trace> After dashing:");
1235 if(getLogLevel() >= LOGLEVEL_TRACE) {
1236 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1238 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1239 lineCap==0?"butt": (lineCap==1?"round":"square"),
1241 col.r,col.g,col.b,col.a
1246 if(flags&STROKE_FILL) {
1247 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1248 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1249 if(getLogLevel() >= LOGLEVEL_TRACE) {
1250 dump_outline(gfxline);
1253 msg("<warning> Empty polygon (resulting from stroked line)");
1255 if(flags&STROKE_CLIP) {
1256 device->startclip(device, gfxline);
1257 states[statepos].clipping++;
1259 device->fill(device, gfxline, &col);
1261 gfxline_free(gfxline);
1262 gfxpoly_destroy(poly);
1264 if(flags&STROKE_CLIP)
1265 msg("<error> Stroke&clip not supported at the same time");
1266 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1270 gfxline_free(line2);
1273 gfxcolor_t getFillColor(GfxState * state)
1276 double opaq = state->getFillOpacity();
1277 state->getFillRGB(&rgb);
1279 col.r = colToByte(rgb.r);
1280 col.g = colToByte(rgb.g);
1281 col.b = colToByte(rgb.b);
1282 col.a = (unsigned char)(opaq*255);
1286 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1288 gfxcolor_t col = getFillColor(state);
1290 if(getLogLevel() >= LOGLEVEL_TRACE) {
1291 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1294 device->fill(device, line, &col);
1297 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1299 if(getLogLevel() >= LOGLEVEL_TRACE) {
1300 msg("<trace> %sclip", evenodd?"eo":"");
1303 gfxbbox_t bbox = gfxline_getbbox(line);
1304 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1306 device->startclip(device, line);
1307 states[statepos].clipping++;
1310 void GFXOutputDev::clip(GfxState *state)
1312 GfxPath * path = state->getPath();
1313 msg("<trace> clip");
1314 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1315 if(!config_disable_polygon_conversion) {
1316 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1320 clipToGfxLine(state, line, 0);
1324 void GFXOutputDev::eoClip(GfxState *state)
1326 GfxPath * path = state->getPath();
1327 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1328 clipToGfxLine(state, line, 1);
1331 void GFXOutputDev::clipToStrokePath(GfxState *state)
1333 GfxPath * path = state->getPath();
1334 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1336 if(getLogLevel() >= LOGLEVEL_TRACE) {
1337 double width = state->getTransformedLineWidth();
1338 msg("<trace> cliptostrokepath width=%f", width);
1342 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1346 void GFXOutputDev::finish()
1348 if(outer_clip_box) {
1350 device->endclip(device);
1356 GFXOutputDev::~GFXOutputDev()
1360 GBool GFXOutputDev::upsideDown()
1364 GBool GFXOutputDev::useDrawChar()
1369 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1370 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1372 static char tmp_printstr[4096];
1373 char* makeStringPrintable(char*str)
1375 int len = strlen(str);
1382 for(t=0;t<len;t++) {
1387 tmp_printstr[t] = c;
1390 tmp_printstr[len++] = '.';
1391 tmp_printstr[len++] = '.';
1392 tmp_printstr[len++] = '.';
1394 tmp_printstr[len] = 0;
1395 return tmp_printstr;
1397 void GFXOutputDev::updateFontMatrix(GfxState*state)
1399 double* ctm = state->getCTM();
1400 double fontSize = state->getFontSize();
1401 double*textMat = state->getTextMat();
1403 /* taking the absolute value of horizScaling seems to be required for
1404 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1405 double hscale = fabs(state->getHorizScaling());
1407 // from xpdf-3.02/SplashOutputDev:updateFont
1408 double mm11 = textMat[0] * fontSize * hscale;
1409 double mm12 = textMat[1] * fontSize * hscale;
1410 double mm21 = textMat[2] * fontSize;
1411 double mm22 = textMat[3] * fontSize;
1413 // multiply with ctm, like state->getFontTransMat() does
1414 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1415 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1416 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1417 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1418 this->current_font_matrix.tx = 0;
1419 this->current_font_matrix.ty = 0;
1422 void GFXOutputDev::beginString(GfxState *state, GString *s)
1424 int render = state->getRender();
1425 if(current_text_stroke) {
1426 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1428 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1431 static gfxline_t* mkEmptyGfxShape(double x, double y)
1433 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1434 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1438 static char isValidUnicode(int c)
1440 if(c>=32 && c<0x2fffe)
1445 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1446 double dx, double dy,
1447 double originX, double originY,
1448 CharCode charid, int nBytes, Unicode *_u, int uLen)
1450 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1451 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1455 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1457 int render = state->getRender();
1458 gfxcolor_t col = getFillColor(state);
1460 // check for invisible text -- this is used by Acrobat Capture
1461 if (render == RENDER_INVISIBLE) {
1463 if(!config_extrafontdata)
1467 GfxFont*font = state->getFont();
1469 if(font->getType() == fontType3) {
1470 /* type 3 chars are passed as graphics */
1471 msg("<debug> type3 char at %f/%f", x, y);
1475 Unicode u = uLen?(_u[0]):0;
1477 gfxmatrix_t m = this->current_font_matrix;
1478 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1479 //m.tx += originX; m.ty += originY;
1481 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);
1483 if((render == RENDER_FILL && !config_drawonlyshapes) ||
1484 (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
1485 (render == RENDER_INVISIBLE)) {
1487 int space = this->current_fontinfo->space_char;
1488 if(config_extrafontdata && config_detectspaces && space>=0 && m.m00 && !m.m01) {
1489 /* space char detection */
1490 if(last_char_gfxfont == current_gfxfont &&
1491 last_char_y == m.ty &&
1492 !last_char_was_space) {
1493 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1494 int space = this->current_fontinfo->space_char;
1495 float width = this->current_fontinfo->average_advance;
1496 if(m.tx - expected_x >= m.m00*width*4/10) {
1497 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",
1500 last_char, glyphid);
1502 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1503 if(m2.tx < expected_x) m2.tx = expected_x;
1504 device->drawchar(device, current_gfxfont, space, &col, &m2);
1507 last_char_gfxfont = current_gfxfont;
1508 last_char = glyphid;
1511 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1513 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1515 msg("<debug> Drawing glyph %d as shape", charid);
1516 if(!gfxglobals->textmodeinfo) {
1517 msg("<notice> Some texts will be rendered as shape");
1518 gfxglobals->textmodeinfo = 1;
1521 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1522 gfxline_t*tglyph = gfxline_clone(glyph);
1523 gfxline_transform(tglyph, &m);
1524 if((render&3) != RENDER_INVISIBLE) {
1525 gfxline_t*add = gfxline_clone(tglyph);
1526 current_text_stroke = gfxline_append(current_text_stroke, add);
1528 if(render&RENDER_CLIP) {
1529 gfxline_t*add = gfxline_clone(tglyph);
1530 current_text_clip = gfxline_append(current_text_clip, add);
1531 if(!current_text_clip) {
1532 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1535 gfxline_free(tglyph);
1539 void GFXOutputDev::endString(GfxState *state)
1541 int render = state->getRender();
1542 msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
1544 if(current_text_stroke) {
1545 /* fillstroke and stroke text rendering objects we can process right
1546 now (as there may be texts of other rendering modes in this
1547 text object)- clipping objects have to wait until endTextObject,
1549 device->setparameter(device, "mark","TXT");
1550 if((render&3) == RENDER_FILL) {
1551 fillGfxLine(state, current_text_stroke, 0);
1552 gfxline_free(current_text_stroke);
1553 current_text_stroke = 0;
1554 } else if((render&3) == RENDER_FILLSTROKE) {
1555 fillGfxLine(state, current_text_stroke, 0);
1556 strokeGfxline(state, current_text_stroke,0);
1557 gfxline_free(current_text_stroke);
1558 current_text_stroke = 0;
1559 } else if((render&3) == RENDER_STROKE) {
1560 strokeGfxline(state, current_text_stroke,0);
1561 gfxline_free(current_text_stroke);
1562 current_text_stroke = 0;
1564 device->setparameter(device, "mark","");
1568 void GFXOutputDev::endTextObject(GfxState *state)
1570 int render = state->getRender();
1571 msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
1573 if(current_text_clip) {
1574 device->setparameter(device, "mark","TXT");
1575 clipToGfxLine(state, current_text_clip, 0);
1576 device->setparameter(device, "mark","");
1577 gfxline_free(current_text_clip);
1578 current_text_clip = 0;
1582 /* the logic seems to be as following:
1583 first, beginType3Char is called, with the charcode and the coordinates.
1584 if this function returns true, it already knew about the char and has now drawn it.
1585 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1586 called with some parameters.
1587 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1588 at the position first passed to beginType3Char). the char ends with endType3Char.
1590 The drawing operations between beginType3Char and endType3Char are somewhat different to
1591 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1592 color determines the color of a font)
1595 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1597 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1600 if(config_extrafontdata && current_fontinfo) {
1602 gfxmatrix_t m = this->current_font_matrix;
1603 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1605 /*m.m00*=INTERNAL_FONT_SIZE;
1606 m.m01*=INTERNAL_FONT_SIZE;
1607 m.m10*=INTERNAL_FONT_SIZE;
1608 m.m11*=INTERNAL_FONT_SIZE;*/
1610 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1611 msg("<error> Invalid charid %d for font", charid);
1614 gfxcolor_t col={0,0,0,0};
1615 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1616 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1620 /* the character itself is going to be passed using the draw functions */
1621 return gFalse; /* gTrue= is_in_cache? */
1624 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1626 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1629 void GFXOutputDev::endType3Char(GfxState *state)
1632 msg("<debug> endType3Char");
1635 void GFXOutputDev::startPage(int pageNum, GfxState *state)
1637 this->currentpage = pageNum;
1639 int rot = doc->getPageRotate(1);
1640 gfxcolor_t white = {255,255,255,255};
1641 gfxcolor_t black = {255,0,0,0};
1643 gfxline_t clippath[5];
1644 PDFRectangle *r = this->page->getCropBox();
1646 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1647 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1648 Use CropBox, not MediaBox, as page size
1655 state->transform(r->x1,r->y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1656 state->transform(r->x2,r->y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1658 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1659 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1661 this->clipmovex = -(int)x1;
1662 this->clipmovey = -(int)y1;
1664 /* apply user clip box */
1665 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1666 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1667 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1668 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1669 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1670 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1672 x1 += this->clipmovex;
1673 y1 += this->clipmovey;
1674 x2 += this->clipmovex;
1675 y2 += this->clipmovey;
1678 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1680 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);
1682 msg("<verbose> page is rotated %d degrees", rot);
1684 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1685 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1686 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1687 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1688 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1689 device->startclip(device, clippath); outer_clip_box = 1;
1690 if(!config_transparent) {
1691 device->fill(device, clippath, &white);
1693 states[statepos].clipbbox.xmin = x1;
1694 states[statepos].clipbbox.ymin = x1;
1695 states[statepos].clipbbox.xmax = x2;
1696 states[statepos].clipbbox.ymax = y2;
1698 states[statepos].dashPattern = 0;
1699 states[statepos].dashLength = 0;
1700 states[statepos].dashStart = 0;
1702 this->last_char_gfxfont = 0;
1706 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1708 double x1, y1, x2, y2;
1709 gfxline_t points[5];
1712 msg("<debug> drawlink");
1714 link->getRect(&x1, &y1, &x2, &y2);
1715 cvtUserToDev(x1, y1, &x, &y);
1716 points[0].type = gfx_moveTo;
1717 points[0].x = points[4].x = x + user_movex + clipmovex;
1718 points[0].y = points[4].y = y + user_movey + clipmovey;
1719 points[0].next = &points[1];
1720 cvtUserToDev(x2, y1, &x, &y);
1721 points[1].type = gfx_lineTo;
1722 points[1].x = x + user_movex + clipmovex;
1723 points[1].y = y + user_movey + clipmovey;
1724 points[1].next = &points[2];
1725 cvtUserToDev(x2, y2, &x, &y);
1726 points[2].type = gfx_lineTo;
1727 points[2].x = x + user_movex + clipmovex;
1728 points[2].y = y + user_movey + clipmovey;
1729 points[2].next = &points[3];
1730 cvtUserToDev(x1, y2, &x, &y);
1731 points[3].type = gfx_lineTo;
1732 points[3].x = x + user_movex + clipmovex;
1733 points[3].y = y + user_movey + clipmovey;
1734 points[3].next = &points[4];
1735 cvtUserToDev(x1, y1, &x, &y);
1736 points[4].type = gfx_lineTo;
1737 points[4].x = x + user_movex + clipmovex;
1738 points[4].y = y + user_movey + clipmovey;
1741 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1742 points[0].x, points[0].y,
1743 points[1].x, points[1].y,
1744 points[2].x, points[2].y,
1745 points[3].x, points[3].y);
1747 if(getLogLevel() >= LOGLEVEL_TRACE) {
1748 dump_outline(points);
1751 LinkAction*action=link->getAction();
1754 const char*type = "-?-";
1757 msg("<trace> drawlink action=%d", action->getKind());
1758 switch(action->getKind())
1762 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1763 LinkDest *dest=NULL;
1764 if (ha->getDest()==NULL)
1765 dest=catalog->findDest(ha->getNamedDest());
1767 dest=ha->getDest()->copy();
1769 if (dest->isPageRef()){
1770 Ref pageref=dest->getPageRef();
1771 page=catalog->findPage(pageref.num,pageref.gen);
1773 else page=dest->getPageNum();
1774 sprintf(buf, "%d", page);
1782 LinkGoToR*l = (LinkGoToR*)action;
1783 GString*g = l->getFileName();
1785 s = strdup(g->getCString());
1787 /* if the GoToR link has no filename, then
1788 try to find a refernce in the *local*
1790 GString*g = l->getNamedDest();
1792 s = strdup(g->getCString());
1798 LinkNamed*l = (LinkNamed*)action;
1799 GString*name = l->getName();
1801 s = strdup(name->lowerCase()->getCString());
1802 named = name->getCString();
1805 if(strstr(s, "next") || strstr(s, "forward"))
1807 page = currentpage + 1;
1809 else if(strstr(s, "prev") || strstr(s, "back"))
1811 page = currentpage - 1;
1813 else if(strstr(s, "last") || strstr(s, "end"))
1815 if(this->page2page && this->num_pages) {
1816 page = this->page2page[this->num_pages-1];
1819 else if(strstr(s, "first") || strstr(s, "top"))
1827 case actionLaunch: {
1829 LinkLaunch*l = (LinkLaunch*)action;
1830 GString * str = new GString(l->getFileName());
1831 GString * params = l->getParams();
1833 str->append(params);
1834 s = strdup(str->getCString());
1841 LinkURI*l = (LinkURI*)action;
1842 GString*g = l->getURI();
1844 url = g->getCString();
1849 case actionUnknown: {
1851 LinkUnknown*l = (LinkUnknown*)action;
1856 msg("<error> Unknown link type!");
1861 if(!s) s = strdup("-?-");
1863 msg("<trace> drawlink s=%s", s);
1865 if(!gfxglobals->linkinfo && (page || s))
1867 msg("<notice> File contains links");
1868 gfxglobals->linkinfo = 1;
1874 for(t=1;t<=this->num_pages;t++) {
1875 if(this->page2page[t]==page) {
1885 sprintf(buf, "page%d", lpage);
1886 device->drawlink(device, points, buf);
1890 device->drawlink(device, points, s);
1891 if(this->config_linkdatafile) {
1892 FILE*fi = fopen(config_linkdatafile, "ab+");
1893 fprintf(fi, "%s\n", s);
1898 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1902 void GFXOutputDev::saveState(GfxState *state) {
1903 dbg("saveState %p", state); dbgindent+=2;
1905 msg("<trace> saveState %p", state);
1908 msg("<fatal> Too many nested states in pdf.");
1912 states[statepos].state = state;
1913 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1914 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1915 states[statepos].clipping = 0;
1916 states[statepos].olddevice = 0;
1917 states[statepos].clipbbox = states[statepos-1].clipbbox;
1919 states[statepos].dashPattern = states[statepos-1].dashPattern;
1920 states[statepos].dashStart = states[statepos-1].dashStart;
1921 states[statepos].dashLength = states[statepos-1].dashLength;
1924 void GFXOutputDev::restoreState(GfxState *state) {
1925 dbgindent-=2; dbg("restoreState %p", state);
1928 msg("<fatal> Invalid restoreState");
1931 msg("<trace> restoreState %p%s%s", state,
1932 states[statepos].softmask?" (end softmask)":"",
1933 states[statepos].clipping?" (end clipping)":"");
1934 if(states[statepos].softmask) {
1935 clearSoftMask(state);
1938 if(states[statepos].dashPattern) {
1939 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1940 free(states[statepos].dashPattern);
1941 states[statepos].dashPattern = 0;
1947 while(states[statepos].clipping) {
1948 device->endclip(device);
1949 states[statepos].clipping--;
1951 if(states[statepos].state!=state) {
1952 msg("<fatal> bad state nesting");
1955 for(t=0;t<=statepos;t++) {
1956 printf("%p ", states[t].state);
1962 states[statepos].state=0;
1966 void GFXOutputDev::updateLineDash(GfxState *state)
1968 if(states[statepos].dashPattern &&
1969 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1970 free(states[statepos].dashPattern);
1971 states[statepos].dashPattern = 0;
1973 double *pattern = 0;
1976 state->getLineDash(&pattern, &dashLength, &dashStart);
1977 msg("<debug> updateLineDash, %d dashes", dashLength);
1979 states[statepos].dashPattern = 0;
1980 states[statepos].dashLength = 0;
1982 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1983 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1984 states[statepos].dashPattern = p;
1985 states[statepos].dashLength = dashLength;
1986 states[statepos].dashStart = dashStart;
1990 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1992 this->page2page = page2page;
1993 this->num_pages = num_pages;
1996 void GFXOutputDev::updateLineWidth(GfxState *state)
1998 double width = state->getTransformedLineWidth();
2001 void GFXOutputDev::updateLineCap(GfxState *state)
2003 int c = state->getLineCap();
2006 void GFXOutputDev::updateLineJoin(GfxState *state)
2008 int j = state->getLineJoin();
2011 void GFXOutputDev::updateFillColor(GfxState *state)
2014 double opaq = state->getFillOpacity();
2015 state->getFillRGB(&rgb);
2017 void GFXOutputDev::updateFillOpacity(GfxState *state)
2020 double opaq = state->getFillOpacity();
2021 state->getFillRGB(&rgb);
2022 dbg("update fillopaq %f", opaq);
2024 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
2026 double opaq = state->getFillOpacity();
2027 dbg("update strokeopaq %f", opaq);
2029 void GFXOutputDev::updateFillOverprint(GfxState *state)
2031 double opaq = state->getFillOverprint();
2032 dbg("update filloverprint %f", opaq);
2034 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2036 double opaq = state->getStrokeOverprint();
2037 dbg("update strokeoverprint %f", opaq);
2039 void GFXOutputDev::updateTransfer(GfxState *state)
2041 dbg("update transfer");
2045 void GFXOutputDev::updateStrokeColor(GfxState *state)
2048 double opaq = state->getStrokeOpacity();
2049 state->getStrokeRGB(&rgb);
2052 void GFXOutputDev::updateFont(GfxState *state)
2054 GfxFont* gfxFont = state->getFont();
2058 char*id = getFontID(gfxFont);
2059 msg("<verbose> Updating font to %s", id);
2060 if(gfxFont->getType() == fontType3) {
2061 infofeature("Type3 fonts");
2062 if(!config_extrafontdata) {
2067 msg("<error> Internal Error: FontID is null");
2071 this->current_fontinfo = this->info->getFont(id);
2073 if(!this->current_fontinfo) {
2074 msg("<error> Internal Error: no fontinfo for font %s", id);
2077 if(!this->current_fontinfo->seen) {
2078 dumpFontInfo("<verbose>", gfxFont);
2081 current_gfxfont = this->current_fontinfo->getGfxFont();
2082 device->addfont(device, current_gfxfont);
2085 updateFontMatrix(state);
2088 #define SQR(x) ((x)*(x))
2090 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2092 if((newwidth<1 || newheight<1) ||
2093 (width<=newwidth || height<=newheight))
2095 unsigned char*newdata;
2097 newdata= (unsigned char*)malloc(newwidth*newheight);
2098 double fx = ((double)width)/newwidth;
2099 double fy = ((double)height)/newheight;
2101 int blocksize = (int)(8192/(fx*fy));
2102 int r = 8192*256/palettesize;
2103 for(x=0;x<newwidth;x++) {
2104 double ex = px + fx;
2105 int fromx = (int)px;
2107 int xweight1 = (int)((1-(px-fromx))*256);
2108 int xweight2 = (int)((ex-tox)*256);
2110 for(y=0;y<newheight;y++) {
2111 double ey = py + fy;
2112 int fromy = (int)py;
2114 int yweight1 = (int)((1-(py-fromy))*256);
2115 int yweight2 = (int)((ey-toy)*256);
2122 for(xx=fromx;xx<=tox;xx++)
2123 for(yy=fromy;yy<=toy;yy++) {
2124 int b = 1-data[width*yy+xx];
2126 if(xx==fromx) weight = (weight*xweight1)/256;
2127 if(xx==tox) weight = (weight*xweight2)/256;
2128 if(yy==fromy) weight = (weight*yweight1)/256;
2129 if(yy==toy) weight = (weight*yweight2)/256;
2132 //if(a) a=(palettesize-1)*r/blocksize;
2133 newdata[y*newwidth+x] = (a*blocksize)/r;
2141 #define IMAGE_TYPE_JPEG 0
2142 #define IMAGE_TYPE_LOSSLESS 1
2144 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2145 double x1,double y1,
2146 double x2,double y2,
2147 double x3,double y3,
2148 double x4,double y4, int type, int multiply)
2150 gfxcolor_t*newpic=0;
2152 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2153 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2155 gfxline_t p1,p2,p3,p4,p5;
2156 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2157 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2158 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2159 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2160 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2162 {p1.x = (int)(p1.x*20)/20.0;
2163 p1.y = (int)(p1.y*20)/20.0;
2164 p2.x = (int)(p2.x*20)/20.0;
2165 p2.y = (int)(p2.y*20)/20.0;
2166 p3.x = (int)(p3.x*20)/20.0;
2167 p3.y = (int)(p3.y*20)/20.0;
2168 p4.x = (int)(p4.x*20)/20.0;
2169 p4.y = (int)(p4.y*20)/20.0;
2170 p5.x = (int)(p5.x*20)/20.0;
2171 p5.y = (int)(p5.y*20)/20.0;
2175 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2176 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2178 m.tx = p1.x - 0.5*multiply;
2179 m.ty = p1.y - 0.5*multiply;
2182 img.data = (gfxcolor_t*)data;
2186 if(type == IMAGE_TYPE_JPEG)
2187 /* TODO: pass image_dpi to device instead */
2188 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2191 dev->fillbitmap(dev, &p1, &img, &m, 0);
2194 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2195 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2197 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2200 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2201 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2203 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2207 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2208 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2209 GBool inlineImg, int mask, int*maskColors,
2210 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2212 /* the code in this function is *old*. It's not pretty, but it works. */
2214 double x1,y1,x2,y2,x3,y3,x4,y4;
2215 ImageStream *imgStr;
2220 unsigned char* maskbitmap = 0;
2223 ncomps = colorMap->getNumPixelComps();
2224 bits = colorMap->getBits();
2229 unsigned char buf[8];
2230 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2232 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2233 imgMaskStr->reset();
2234 unsigned char pal[256];
2235 int n = 1 << colorMap->getBits();
2240 maskColorMap->getGray(pixBuf, &gray);
2241 pal[t] = colToByte(gray);
2243 for (y = 0; y < maskHeight; y++) {
2244 for (x = 0; x < maskWidth; x++) {
2245 imgMaskStr->getPixel(buf);
2246 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2251 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2252 imgMaskStr->reset();
2253 for (y = 0; y < maskHeight; y++) {
2254 for (x = 0; x < maskWidth; x++) {
2255 imgMaskStr->getPixel(buf);
2257 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2265 imgStr = new ImageStream(str, width, ncomps,bits);
2268 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2270 msg("<verbose> Ignoring %d by %d image", width, height);
2271 unsigned char buf[8];
2273 for (y = 0; y < height; ++y)
2274 for (x = 0; x < width; ++x) {
2275 imgStr->getPixel(buf);
2283 this->transformXY(state, 0, 1, &x1, &y1);
2284 this->transformXY(state, 0, 0, &x2, &y2);
2285 this->transformXY(state, 1, 0, &x3, &y3);
2286 this->transformXY(state, 1, 1, &x4, &y4);
2289 /* as type 3 bitmaps are antialized, we need to place them
2290 at integer coordinates, otherwise flash player's antializing
2291 will kick in and make everything blurry */
2292 x1 = (int)(x1);y1 = (int)(y1);
2293 x2 = (int)(x2);y2 = (int)(y2);
2294 x3 = (int)(x3);y3 = (int)(y3);
2295 x4 = (int)(x4);y4 = (int)(y4);
2298 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2300 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2301 gfxglobals->pbminfo = 1;
2304 msg("<verbose> drawing %d by %d masked picture", width, height);
2306 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2307 msg("<notice> File contains jpeg pictures");
2308 gfxglobals->jpeginfo = 1;
2312 unsigned char buf[8];
2314 unsigned char*pic = new unsigned char[width*height];
2315 gfxcolor_t pal[256];
2317 state->getFillRGB(&rgb);
2319 memset(pal,255,sizeof(pal));
2320 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2321 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2322 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2323 pal[0].a = 255; pal[1].a = 0;
2326 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2327 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2328 for (y = 0; y < height; ++y)
2329 for (x = 0; x < width; ++x)
2331 imgStr->getPixel(buf);
2334 pic[width*y+x] = buf[0];
2338 unsigned char*pic2 = 0;
2341 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2350 height = realheight;
2354 /* make a black/white palette */
2356 float r = 255./(float)(numpalette-1);
2358 for(t=0;t<numpalette;t++) {
2359 pal[t].r = colToByte(rgb.r);
2360 pal[t].g = colToByte(rgb.g);
2361 pal[t].b = colToByte(rgb.b);
2362 pal[t].a = (unsigned char)(t*r);
2367 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2368 for (y = 0; y < height; ++y) {
2369 for (x = 0; x < width; ++x) {
2370 pic2[width*y+x] = pal[pic[y*width+x]];
2373 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2377 if(maskbitmap) free(maskbitmap);
2383 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2384 gfxcolor_t*pic=new gfxcolor_t[width*height];
2385 for (y = 0; y < height; ++y) {
2386 for (x = 0; x < width; ++x) {
2387 imgStr->getPixel(pixBuf);
2388 colorMap->getRGB(pixBuf, &rgb);
2389 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2390 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2391 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2392 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2394 int x1 = x*maskWidth/width;
2395 int y1 = y*maskHeight/height;
2396 int x2 = (x+1)*maskWidth/width;
2397 int y2 = (y+1)*maskHeight/height;
2399 unsigned int alpha=0;
2400 unsigned int count=0;
2401 for(xx=x1;xx<x2;xx++)
2402 for(yy=y1;yy<y2;yy++) {
2403 alpha += maskbitmap[yy*maskWidth+xx];
2407 pic[width*y+x].a = alpha / count;
2409 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2414 if(str->getKind()==strDCT)
2415 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2417 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2420 if(maskbitmap) free(maskbitmap);
2423 gfxcolor_t*pic=new gfxcolor_t[width*height];
2424 gfxcolor_t pal[256];
2425 int n = 1 << colorMap->getBits();
2427 for(t=0;t<256;t++) {
2429 colorMap->getRGB(pixBuf, &rgb);
2430 pal[t].r = (unsigned char)(colToByte(rgb.r));
2431 pal[t].g = (unsigned char)(colToByte(rgb.g));
2432 pal[t].b = (unsigned char)(colToByte(rgb.b));
2433 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2435 for (y = 0; y < height; ++y) {
2436 for (x = 0; x < width; ++x) {
2437 imgStr->getPixel(pixBuf);
2438 pic[width*y+x] = pal[pixBuf[0]];
2439 if(maskColors && *maskColors==pixBuf[0]) {
2440 pic[width*y+x].a = 0;
2445 if(maskWidth < width && maskHeight < height) {
2446 for(y = 0; y < height; y++) {
2447 for (x = 0; x < width; x++) {
2448 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2452 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2453 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2454 double dx = width / (double)maskWidth;
2455 double dy = height / (double)maskHeight;
2457 for(y = 0; y < maskHeight; y++) {
2459 for (x = 0; x < maskWidth; x++) {
2460 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2461 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2469 height = maskHeight;
2472 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2476 if(maskbitmap) free(maskbitmap);
2481 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2482 int width, int height, GBool invert,
2485 if(config_textonly) {
2486 OutputDev::drawImageMask(state,ref,str,width,height,invert,inlineImg);
2489 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2490 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2491 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2494 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2495 int width, int height, GfxImageColorMap *colorMap,
2496 int *maskColors, GBool inlineImg)
2498 if(config_textonly) {
2499 OutputDev::drawImage(state,ref,str,width,height,colorMap,maskColors,inlineImg);
2502 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2503 colorMap?"colorMap":"no colorMap",
2504 maskColors?"maskColors":"no maskColors",
2506 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2507 colorMap?"colorMap":"no colorMap",
2508 maskColors?"maskColors":"no maskColors",
2511 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2512 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2513 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2516 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2517 int width, int height,
2518 GfxImageColorMap *colorMap,
2519 Stream *maskStr, int maskWidth, int maskHeight,
2522 if(config_textonly) {
2523 OutputDev::drawMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskInvert);
2526 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2527 colorMap?"colorMap":"no colorMap",
2528 maskWidth, maskHeight);
2529 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2530 colorMap?"colorMap":"no colorMap",
2531 maskWidth, maskHeight);
2533 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2534 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2535 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2538 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2539 int width, int height,
2540 GfxImageColorMap *colorMap,
2542 int maskWidth, int maskHeight,
2543 GfxImageColorMap *maskColorMap)
2545 if(config_textonly) {
2546 OutputDev::drawSoftMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskColorMap);
2549 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2550 colorMap?"colorMap":"no colorMap",
2551 maskWidth, maskHeight);
2552 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2553 colorMap?"colorMap":"no colorMap",
2554 maskWidth, maskHeight);
2556 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2557 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2561 void GFXOutputDev::stroke(GfxState *state)
2563 if(config_textonly) {return;}
2567 GfxPath * path = state->getPath();
2568 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2569 strokeGfxline(state, line, 0);
2573 void GFXOutputDev::fill(GfxState *state)
2575 if(config_textonly) {return;}
2577 gfxcolor_t col = getFillColor(state);
2578 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2580 GfxPath * path = state->getPath();
2581 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2582 if(!config_disable_polygon_conversion) {
2583 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2587 fillGfxLine(state, line, 0);
2591 void GFXOutputDev::eoFill(GfxState *state)
2593 if(config_textonly) {return;}
2595 gfxcolor_t col = getFillColor(state);
2596 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2598 GfxPath * path = state->getPath();
2599 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2600 fillGfxLine(state, line, 1);
2605 static const char* dirseparator()
2614 void addGlobalFont(const char*filename)
2616 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2617 memset(f, 0, sizeof(fontfile_t));
2618 f->filename = filename;
2619 int len = strlen(filename);
2620 char*r1 = strrchr((char*)filename, '/');
2621 char*r2 = strrchr((char*)filename, '\\');
2629 msg("<verbose> Adding font \"%s\".", filename);
2630 if(global_fonts_next) {
2631 global_fonts_next->next = f;
2632 global_fonts_next = global_fonts_next->next;
2634 global_fonts_next = global_fonts = f;
2638 void addGlobalLanguageDir(const char*dir)
2640 msg("<notice> Adding %s to language pack directories", dir);
2643 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2644 strcpy(config_file, dir);
2645 strcat(config_file, dirseparator());
2646 strcat(config_file, "add-to-xpdfrc");
2648 fi = fopen(config_file, "rb");
2650 msg("<error> Could not open %s", config_file);
2653 globalParams->parseFile(new GString(config_file), fi);
2657 void addGlobalFontDir(const char*dirname)
2659 #ifdef HAVE_DIRENT_H
2660 DIR*dir = opendir(dirname);
2662 msg("<warning> Couldn't open directory %s", dirname);
2668 ent = readdir (dir);
2672 char*name = ent->d_name;
2678 if(!strncasecmp(&name[l-4], ".pfa", 4))
2680 if(!strncasecmp(&name[l-4], ".pfb", 4))
2682 if(!strncasecmp(&name[l-4], ".ttf", 4))
2685 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2686 strcpy(fontname, dirname);
2687 strcat(fontname, dirseparator());
2688 strcat(fontname, name);
2689 addGlobalFont(fontname);
2693 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2696 msg("<warning> No dirent.h");
2700 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2701 GfxColorSpace *blendingColorSpace,
2702 GBool isolated, GBool knockout,
2705 const char*colormodename = "";
2707 if(blendingColorSpace) {
2708 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2710 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);
2711 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);
2713 //states[statepos].createsoftmask |= forSoftMask;
2714 states[statepos].createsoftmask = forSoftMask;
2715 states[statepos].transparencygroup = !forSoftMask;
2716 states[statepos].isolated = isolated;
2718 states[statepos].olddevice = this->device;
2719 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2720 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2722 gfxdevice_record_init(this->device, 0);
2724 /*if(!forSoftMask) { ////???
2725 state->setFillOpacity(0.0);
2730 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2733 gfxdevice_t*r = this->device;
2735 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2737 this->device = states[statepos].olddevice;
2739 msg("<error> Invalid state nesting");
2741 states[statepos].olddevice = 0;
2743 gfxresult_t*recording = r->finish(r);
2745 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2746 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2748 if(states[statepos].createsoftmask) {
2749 states[statepos-1].softmaskrecording = recording;
2751 states[statepos-1].grouprecording = recording;
2754 states[statepos].createsoftmask = 0;
2755 states[statepos].transparencygroup = 0;
2759 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2761 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2762 "colordodge","colorburn","hardlight","softlight","difference",
2763 "exclusion","hue","saturation","color","luminosity"};
2765 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2766 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2768 if(state->getBlendMode() == gfxBlendNormal)
2769 infofeature("transparency groups");
2772 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2773 warnfeature(buffer, 0);
2776 gfxresult_t*grouprecording = states[statepos].grouprecording;
2778 int blendmode = state->getBlendMode();
2779 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2780 int alpha = (int)(state->getFillOpacity()*255);
2781 if(blendmode == gfxBlendMultiply && alpha>200)
2784 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2785 gfxdevice_ops_init(&ops, this->device, alpha);
2786 gfxresult_record_replay(grouprecording, &ops, 0);
2789 grouprecording->destroy(grouprecording);
2791 states[statepos].grouprecording = 0;
2794 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2796 if(states[statepos].softmask) {
2797 /* shouldn't happen, but *does* happen */
2798 clearSoftMask(state);
2801 /* alpha = 1: retrieve mask values from alpha layer
2802 alpha = 0: retrieve mask values from luminance */
2804 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2805 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2806 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2807 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2809 infofeature("soft masks");
2811 warnfeature("soft masks from alpha channel",0);
2813 if(states[statepos].olddevice) {
2814 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2817 states[statepos].olddevice = this->device;
2818 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2819 gfxdevice_record_init(this->device, 0);
2821 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2823 states[statepos].softmask = 1;
2824 states[statepos].softmask_alpha = alpha;
2827 static inline Guchar div255(int x) {
2828 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2831 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2833 if(c < min) c = min;
2834 if(c > max) c = max;
2838 void GFXOutputDev::clearSoftMask(GfxState *state)
2840 if(!states[statepos].softmask)
2842 states[statepos].softmask = 0;
2843 dbg("clearSoftMask statepos=%d", statepos);
2844 msg("<verbose> clearSoftMask statepos=%d", statepos);
2846 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2847 msg("<error> Error in softmask/tgroup ordering");
2851 gfxresult_t*mask = states[statepos].softmaskrecording;
2852 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2853 this->device = states[statepos].olddevice;
2855 /* get outline of all objects below the soft mask */
2856 gfxdevice_t uniondev;
2857 gfxdevice_union_init(&uniondev, 0);
2858 gfxresult_record_replay(below, &uniondev, 0);
2859 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2860 uniondev.finish(&uniondev);
2861 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2862 gfxline_free(belowoutline);belowoutline=0;
2864 this->device->startclip(this->device, belowoutline);
2865 gfxresult_record_replay(below, this->device, 0);
2866 gfxresult_record_replay(mask, this->device, 0);
2867 this->device->endclip(this->device);
2870 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2871 if(width<=0 || height<=0)
2874 gfxdevice_t belowrender;
2875 gfxdevice_render_init(&belowrender);
2876 if(states[statepos+1].isolated) {
2877 belowrender.setparameter(&belowrender, "fillwhite", "1");
2879 belowrender.setparameter(&belowrender, "antialize", "2");
2880 belowrender.startpage(&belowrender, width, height);
2881 gfxresult_record_replay(below, &belowrender, 0);
2882 belowrender.endpage(&belowrender);
2883 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2884 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2885 //png_write("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2887 gfxdevice_t maskrender;
2888 gfxdevice_render_init(&maskrender);
2889 maskrender.startpage(&maskrender, width, height);
2890 gfxresult_record_replay(mask, &maskrender, 0);
2891 maskrender.endpage(&maskrender);
2892 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2893 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2895 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2896 msg("<fatal> Internal error in mask drawing");
2901 for(y=0;y<height;y++) {
2902 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2903 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2904 for(x=0;x<width;x++) {
2906 if(states[statepos].softmask_alpha) {
2909 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2912 l2->a = div255(alpha*l2->a);
2914 /* DON'T premultiply alpha- this is done by fillbitmap,
2915 depending on the output device */
2916 //l2->r = div255(alpha*l2->r);
2917 //l2->g = div255(alpha*l2->g);
2918 //l2->b = div255(alpha*l2->b);
2924 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2927 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2928 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2930 if(!config_textonly) {
2931 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2934 mask->destroy(mask);
2935 below->destroy(below);
2936 maskresult->destroy(maskresult);
2937 belowresult->destroy(belowresult);
2938 states[statepos].softmaskrecording = 0;
2943 // public: ~MemCheck()
2945 // delete globalParams;globalParams=0;
2946 // Object::memCheck(stderr);
2947 // gMemReport(stderr);