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,"multiply")) {
680 this->config_multiply = atoi(value);
681 if(this->config_multiply<1)
682 this->config_multiply=1;
683 } else if(!strcmp(key,"disable_polygon_conversion")) {
684 this->config_disable_polygon_conversion = atoi(value);
688 void GFXOutputDev::setDevice(gfxdevice_t*dev)
693 void GFXOutputDev::setMove(int x,int y)
695 this->user_movex = x;
696 this->user_movey = y;
699 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
701 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
702 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
704 this->user_clipx1 = x1;
705 this->user_clipy1 = y1;
706 this->user_clipx2 = x2;
707 this->user_clipy2 = y2;
710 static char*getFontName(GfxFont*font)
713 GString*gstr = font->getName();
714 char* fname = gstr==0?0:gstr->getCString();
718 sprintf(buf, "UFONT%d", r->num);
719 fontid = strdup(buf);
721 fontid = strdup(fname);
725 char* plus = strchr(fontid, '+');
726 if(plus && plus < &fontid[strlen(fontid)-1]) {
727 fontname = strdup(plus+1);
729 fontname = strdup(fontid);
735 static void dumpFontInfo(const char*loglevel, GfxFont*font);
736 static int lastdumps[1024];
737 static int lastdumppos = 0;
742 static void showFontError(GfxFont*font, int nr)
746 for(t=0;t<lastdumppos;t++)
747 if(lastdumps[t] == r->num)
751 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
752 lastdumps[lastdumppos++] = r->num;
754 msg("<warning> The following font caused problems:");
756 msg("<warning> The following font caused problems (substituting):");
758 msg("<warning> The following Type 3 Font will be rendered as graphics:");
759 dumpFontInfo("<warning>", font);
762 static void dumpFontInfo(const char*loglevel, GfxFont*font)
764 char* id = getFontID(font);
765 char* name = getFontName(font);
766 Ref* r=font->getID();
767 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
769 GString*gstr = font->getTag();
771 msg("%s| Tag: %s", loglevel, id);
773 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
775 GfxFontType type=font->getType();
777 case fontUnknownType:
778 msg("%s| Type: unknown",loglevel);
781 msg("%s| Type: 1",loglevel);
784 msg("%s| Type: 1C",loglevel);
787 msg("%s| Type: 3",loglevel);
790 msg("%s| Type: TrueType",loglevel);
793 msg("%s| Type: CIDType0",loglevel);
796 msg("%s| Type: CIDType0C",loglevel);
799 msg("%s| Type: CIDType2",loglevel);
804 GBool embedded = font->getEmbeddedFontID(&embRef);
806 if(font->getEmbeddedFontName()) {
807 embeddedName = font->getEmbeddedFontName()->getCString();
810 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
812 gstr = font->getExtFontFile();
814 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
816 // Get font descriptor flags.
817 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
818 if(font->isSerif()) msg("%s| is serif", loglevel);
819 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
820 if(font->isItalic()) msg("%s| is italic", loglevel);
821 if(font->isBold()) msg("%s| is bold", loglevel);
827 //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");}
828 //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");}
830 void dump_outline(gfxline_t*line)
832 /*gfxbbox_t*r = gfxline_isrectangle(line);
834 printf("is not a rectangle\n");
836 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
840 if(line->type == gfx_moveTo) {
841 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
842 } else if(line->type == gfx_lineTo) {
843 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
844 } else if(line->type == gfx_splineTo) {
845 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
851 void gfxPath_dump(GfxPath*path)
853 int num = path->getNumSubpaths();
856 for(t = 0; t < num; t++) {
857 GfxSubpath *subpath = path->getSubpath(t);
858 int subnum = subpath->getNumPoints();
860 for(s=0;s<subnum;s++) {
861 double x=subpath->getX(s);
862 double y=subpath->getY(s);
863 if(s==0 && !subpath->getCurve(s)) {
864 printf("M %f %f\n", x, y);
865 } else if(s==0 && subpath->getCurve(s)) {
866 printf("E %f %f\n", x, y);
867 } else if(subpath->getCurve(s)) {
868 printf("C %f %f\n", x, y);
870 printf("T %f %f\n", x, y);
876 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
878 int num = path->getNumSubpaths();
881 double lastx=0,lasty=0,posx=0,posy=0;
884 msg("<warning> empty path");
888 gfxdrawer_target_gfxline(&draw);
890 for(t = 0; t < num; t++) {
891 GfxSubpath *subpath = path->getSubpath(t);
892 int subnum = subpath->getNumPoints();
893 double bx=0,by=0,cx=0,cy=0;
895 for(s=0;s<subnum;s++) {
898 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
901 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
902 draw.lineTo(&draw, lastx, lasty);
904 draw.moveTo(&draw, x,y);
909 } else if(subpath->getCurve(s) && cpos==0) {
913 } else if(subpath->getCurve(s) && cpos==1) {
921 draw.lineTo(&draw, x,y);
923 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
930 /* fix non-closed lines */
931 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
932 draw.lineTo(&draw, lastx, lasty);
934 gfxline_t*result = (gfxline_t*)draw.result(&draw);
936 gfxline_optimize(result);
941 GBool GFXOutputDev::useTilingPatternFill()
943 infofeature("tiled patterns");
944 // if(config_convertgradients)
948 GBool GFXOutputDev::useShadedFills()
950 infofeature("shaded fills");
951 if(config_convertgradients)
956 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
958 state->transform(x,y,nx,ny);
959 *nx += user_movex + clipmovex;
960 *ny += user_movey + clipmovey;
964 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
965 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
966 int paintType, Dict *resDict,
967 double *mat, double *bbox,
968 int x0, int y0, int x1, int y1,
969 double xStep, double yStep)
971 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
972 int paintType, Dict *resDict,
973 double *mat, double *bbox,
974 int x0, int y0, int x1, int y1,
975 double xStep, double yStep)
978 msg("<debug> tilingPatternFill");
979 infofeature("tiling pattern fills");
982 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
984 msg("<error> functionShadedFill not supported yet");
985 infofeature("function shaded fills");
988 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
992 colspace->getRGB(col, &rgb);
993 c.r = colToByte(rgb.r);
994 c.g = colToByte(rgb.g);
995 c.b = colToByte(rgb.b);
1000 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
1002 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
1003 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
1006 this->transformXY(state, x0,y0, &x0,&y0);
1007 this->transformXY(state, x1,y1, &x1,&y1);
1008 this->transformXY(state, x2,y2, &x2,&y2);
1013 shading->getColor(0.0, &color0);
1014 shading->getColor(0.5, &color1);
1015 shading->getColor(1.0, &color2);
1017 GfxColorSpace* colspace = shading->getColorSpace();
1019 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
1020 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1021 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1022 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
1023 infofeature("radial shaded fills");
1025 gfxgradient_t gr[3];
1026 gfxgradient_t*g = &gr[0];
1030 g[0].color = col2col(colspace, &color0);
1031 g[1].color = col2col(colspace, &color1);
1032 g[2].color = col2col(colspace, &color2);
1037 gfxbbox_t b = states[statepos].clipbbox;
1038 gfxline_t p1,p2,p3,p4,p5;
1039 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1040 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1041 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1042 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1043 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1046 //m.m00 = (x3-x0); m.m10 = (x1-x0);
1047 //m.m01 = (y3-y0); m.m11 = (y1-y0);
1048 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1049 m.m00 = (x1-x0); m.m10 = (x2-x0);
1050 m.m01 = (y1-y0); m.m11 = (y2-y0);
1054 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1058 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1061 shading->getCoords(&x0,&y0,&x1,&y1);
1062 this->transformXY(state, x0,y0,&x0,&y0);
1063 this->transformXY(state, x1,y1,&x1,&y1);
1068 shading->getColor(0.0, &color0);
1069 shading->getColor(0.5, &color1);
1070 shading->getColor(1.0, &color2);
1072 GfxColorSpace* colspace = shading->getColorSpace();
1074 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1075 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1076 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1077 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1079 infofeature("axial shaded fills");
1081 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1085 g[0].color = col2col(colspace, &color0);
1086 g[1].color = col2col(colspace, &color1);
1087 g[2].color = col2col(colspace, &color2);
1092 double xMin,yMin,xMax,yMax;
1093 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1094 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1095 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1098 xMin = 1024; yMin = 1024;
1100 gfxbbox_t b = states[statepos].clipbbox;
1101 gfxline_t p1,p2,p3,p4,p5;
1102 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1103 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1104 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1105 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1106 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1108 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1109 the middle of the two control points */
1111 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1112 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1113 m.tx = (x0 + x1)/2 - 0.5;
1114 m.ty = (y0 + y1)/2 - 0.5;
1116 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1122 GBool GFXOutputDev::useDrawForm()
1124 infofeature("forms");
1127 void GFXOutputDev::drawForm(Ref id)
1129 msg("<error> drawForm not implemented");
1131 GBool GFXOutputDev::needNonText()
1135 void GFXOutputDev::endPage()
1137 msg("<verbose> endPage (GfxOutputDev)");
1138 if(outer_clip_box) {
1139 device->endclip(device);
1142 /* notice: we're not fully done yet with this page- there might still be
1143 a few calls to drawLink() yet to come */
1146 static inline double sqr(double x) {return x*x;}
1148 #define STROKE_FILL 1
1149 #define STROKE_CLIP 2
1150 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1152 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1153 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1154 double miterLimit = state->getMiterLimit();
1155 double width = state->getTransformedLineWidth();
1158 double opaq = state->getStrokeOpacity();
1160 state->getFillRGB(&rgb);
1162 state->getStrokeRGB(&rgb);
1164 col.r = colToByte(rgb.r);
1165 col.g = colToByte(rgb.g);
1166 col.b = colToByte(rgb.b);
1167 col.a = (unsigned char)(opaq*255);
1169 gfx_capType capType = gfx_capRound;
1170 if(lineCap == 0) capType = gfx_capButt;
1171 else if(lineCap == 1) capType = gfx_capRound;
1172 else if(lineCap == 2) capType = gfx_capSquare;
1173 else msg("<error> Invalid line cap type");
1175 gfx_joinType joinType = gfx_joinRound;
1176 if(lineJoin == 0) joinType = gfx_joinMiter;
1177 else if(lineJoin == 1) joinType = gfx_joinRound;
1178 else if(lineJoin == 2) joinType = gfx_joinBevel;
1179 else msg("<error> Invalid line join type");
1181 gfxline_t*line2 = 0;
1183 int dashLength = states[statepos].dashLength;
1184 double*dashPattern = states[statepos].dashPattern;
1185 double dashStart = states[statepos].dashStart;
1186 if(dashLength && dashPattern) {
1187 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1190 /* try to find out how much the transformation matrix would
1191 stretch the dashes, and factor that into the dash lengths.
1192 This is not the entirely correct approach- it would be
1193 better to first convert the path to an unscaled version,
1194 then apply dashing, and then transform the path using
1195 the current transformation matrix. However there are few
1196 PDFs which actually stretch a dashed path in a non-orthonormal
1198 double tx1, ty1, tx2, ty2, tx3, ty3;
1199 this->transformXY(state, 0, 0, &tx1, &ty1);
1200 this->transformXY(state, 0, 1, &tx2, &ty2);
1201 this->transformXY(state, 1, 0, &tx3, &ty3);
1202 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1203 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1205 warnfeature("non-ortogonally dashed strokes", 0);
1206 double f = (d1+d2)/2;
1208 msg("<trace> %d dashes", dashLength);
1209 msg("<trace> | phase: %f", dashStart);
1210 for(t=0;t<dashLength;t++) {
1211 dash[t] = (float)dashPattern[t] * f;
1215 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1217 dash[dashLength] = -1;
1218 if(getLogLevel() >= LOGLEVEL_TRACE) {
1222 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1226 msg("<trace> After dashing:");
1229 if(getLogLevel() >= LOGLEVEL_TRACE) {
1230 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1232 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1233 lineCap==0?"butt": (lineCap==1?"round":"square"),
1235 col.r,col.g,col.b,col.a
1240 if(flags&STROKE_FILL) {
1241 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1242 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1243 if(getLogLevel() >= LOGLEVEL_TRACE) {
1244 dump_outline(gfxline);
1247 msg("<warning> Empty polygon (resulting from stroked line)");
1249 if(flags&STROKE_CLIP) {
1250 device->startclip(device, gfxline);
1251 states[statepos].clipping++;
1253 device->fill(device, gfxline, &col);
1255 gfxline_free(gfxline);
1256 gfxpoly_destroy(poly);
1258 if(flags&STROKE_CLIP)
1259 msg("<error> Stroke&clip not supported at the same time");
1260 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1264 gfxline_free(line2);
1267 gfxcolor_t getFillColor(GfxState * state)
1270 double opaq = state->getFillOpacity();
1271 state->getFillRGB(&rgb);
1273 col.r = colToByte(rgb.r);
1274 col.g = colToByte(rgb.g);
1275 col.b = colToByte(rgb.b);
1276 col.a = (unsigned char)(opaq*255);
1280 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1282 gfxcolor_t col = getFillColor(state);
1284 if(getLogLevel() >= LOGLEVEL_TRACE) {
1285 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1288 device->fill(device, line, &col);
1291 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1293 if(getLogLevel() >= LOGLEVEL_TRACE) {
1294 msg("<trace> %sclip", evenodd?"eo":"");
1297 gfxbbox_t bbox = gfxline_getbbox(line);
1298 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1300 device->startclip(device, line);
1301 states[statepos].clipping++;
1304 void GFXOutputDev::clip(GfxState *state)
1306 GfxPath * path = state->getPath();
1307 msg("<trace> clip");
1308 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1309 if(!config_disable_polygon_conversion) {
1310 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1314 clipToGfxLine(state, line, 0);
1318 void GFXOutputDev::eoClip(GfxState *state)
1320 GfxPath * path = state->getPath();
1321 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1322 clipToGfxLine(state, line, 1);
1325 void GFXOutputDev::clipToStrokePath(GfxState *state)
1327 GfxPath * path = state->getPath();
1328 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1330 if(getLogLevel() >= LOGLEVEL_TRACE) {
1331 double width = state->getTransformedLineWidth();
1332 msg("<trace> cliptostrokepath width=%f", width);
1336 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1340 void GFXOutputDev::finish()
1342 if(outer_clip_box) {
1344 device->endclip(device);
1350 GFXOutputDev::~GFXOutputDev()
1354 GBool GFXOutputDev::upsideDown()
1358 GBool GFXOutputDev::useDrawChar()
1363 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1364 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1366 static char tmp_printstr[4096];
1367 char* makeStringPrintable(char*str)
1369 int len = strlen(str);
1376 for(t=0;t<len;t++) {
1381 tmp_printstr[t] = c;
1384 tmp_printstr[len++] = '.';
1385 tmp_printstr[len++] = '.';
1386 tmp_printstr[len++] = '.';
1388 tmp_printstr[len] = 0;
1389 return tmp_printstr;
1391 void GFXOutputDev::updateFontMatrix(GfxState*state)
1393 double* ctm = state->getCTM();
1394 double fontSize = state->getFontSize();
1395 double*textMat = state->getTextMat();
1397 /* taking the absolute value of horizScaling seems to be required for
1398 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1399 double hscale = fabs(state->getHorizScaling());
1401 // from xpdf-3.02/SplashOutputDev:updateFont
1402 double mm11 = textMat[0] * fontSize * hscale;
1403 double mm12 = textMat[1] * fontSize * hscale;
1404 double mm21 = textMat[2] * fontSize;
1405 double mm22 = textMat[3] * fontSize;
1407 // multiply with ctm, like state->getFontTransMat() does
1408 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1409 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1410 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1411 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1412 this->current_font_matrix.tx = 0;
1413 this->current_font_matrix.ty = 0;
1416 void GFXOutputDev::beginString(GfxState *state, GString *s)
1418 int render = state->getRender();
1419 if(current_text_stroke) {
1420 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1422 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1425 static gfxline_t* mkEmptyGfxShape(double x, double y)
1427 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1428 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1432 static char isValidUnicode(int c)
1434 if(c>=32 && c<0x2fffe)
1439 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1440 double dx, double dy,
1441 double originX, double originY,
1442 CharCode charid, int nBytes, Unicode *_u, int uLen)
1444 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1445 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1449 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1451 int render = state->getRender();
1452 gfxcolor_t col = getFillColor(state);
1454 // check for invisible text -- this is used by Acrobat Capture
1455 if (render == RENDER_INVISIBLE) {
1457 if(!config_extrafontdata)
1461 GfxFont*font = state->getFont();
1463 if(font->getType() == fontType3) {
1464 /* type 3 chars are passed as graphics */
1465 msg("<debug> type3 char at %f/%f", x, y);
1469 Unicode u = uLen?(_u[0]):0;
1471 gfxmatrix_t m = this->current_font_matrix;
1472 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1473 //m.tx += originX; m.ty += originY;
1475 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);
1477 if((render == RENDER_FILL && !config_drawonlyshapes) ||
1478 (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
1479 (render == RENDER_INVISIBLE)) {
1481 int space = this->current_fontinfo->space_char;
1482 if(config_extrafontdata && config_detectspaces && space>=0 && m.m00 && !m.m01) {
1483 /* space char detection */
1484 if(last_char_gfxfont == current_gfxfont &&
1485 last_char_y == m.ty &&
1486 !last_char_was_space) {
1487 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1488 int space = this->current_fontinfo->space_char;
1489 float width = this->current_fontinfo->average_advance;
1490 if(m.tx - expected_x >= m.m00*width*4/10) {
1491 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",
1494 last_char, glyphid);
1496 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1497 if(m2.tx < expected_x) m2.tx = expected_x;
1498 device->drawchar(device, current_gfxfont, space, &col, &m2);
1501 last_char_gfxfont = current_gfxfont;
1502 last_char = glyphid;
1505 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1507 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1509 msg("<debug> Drawing glyph %d as shape", charid);
1510 if(!gfxglobals->textmodeinfo) {
1511 msg("<notice> Some texts will be rendered as shape");
1512 gfxglobals->textmodeinfo = 1;
1515 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1516 gfxline_t*tglyph = gfxline_clone(glyph);
1517 gfxline_transform(tglyph, &m);
1518 if((render&3) != RENDER_INVISIBLE) {
1519 gfxline_t*add = gfxline_clone(tglyph);
1520 current_text_stroke = gfxline_append(current_text_stroke, add);
1522 if(render&RENDER_CLIP) {
1523 gfxline_t*add = gfxline_clone(tglyph);
1524 current_text_clip = gfxline_append(current_text_clip, add);
1525 if(!current_text_clip) {
1526 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1529 gfxline_free(tglyph);
1533 void GFXOutputDev::endString(GfxState *state)
1535 int render = state->getRender();
1536 msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
1538 if(current_text_stroke) {
1539 /* fillstroke and stroke text rendering objects we can process right
1540 now (as there may be texts of other rendering modes in this
1541 text object)- clipping objects have to wait until endTextObject,
1543 device->setparameter(device, "mark","TXT");
1544 if((render&3) == RENDER_FILL) {
1545 fillGfxLine(state, current_text_stroke, 0);
1546 gfxline_free(current_text_stroke);
1547 current_text_stroke = 0;
1548 } else if((render&3) == RENDER_FILLSTROKE) {
1549 fillGfxLine(state, current_text_stroke, 0);
1550 strokeGfxline(state, current_text_stroke,0);
1551 gfxline_free(current_text_stroke);
1552 current_text_stroke = 0;
1553 } else if((render&3) == RENDER_STROKE) {
1554 strokeGfxline(state, current_text_stroke,0);
1555 gfxline_free(current_text_stroke);
1556 current_text_stroke = 0;
1558 device->setparameter(device, "mark","");
1562 void GFXOutputDev::endTextObject(GfxState *state)
1564 int render = state->getRender();
1565 msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
1567 if(current_text_clip) {
1568 device->setparameter(device, "mark","TXT");
1569 clipToGfxLine(state, current_text_clip, 0);
1570 device->setparameter(device, "mark","");
1571 gfxline_free(current_text_clip);
1572 current_text_clip = 0;
1576 /* the logic seems to be as following:
1577 first, beginType3Char is called, with the charcode and the coordinates.
1578 if this function returns true, it already knew about the char and has now drawn it.
1579 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1580 called with some parameters.
1581 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1582 at the position first passed to beginType3Char). the char ends with endType3Char.
1584 The drawing operations between beginType3Char and endType3Char are somewhat different to
1585 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1586 color determines the color of a font)
1589 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1591 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1594 if(config_extrafontdata && current_fontinfo) {
1596 gfxmatrix_t m = this->current_font_matrix;
1597 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1599 /*m.m00*=INTERNAL_FONT_SIZE;
1600 m.m01*=INTERNAL_FONT_SIZE;
1601 m.m10*=INTERNAL_FONT_SIZE;
1602 m.m11*=INTERNAL_FONT_SIZE;*/
1604 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1605 msg("<error> Invalid charid %d for font", charid);
1608 gfxcolor_t col={0,0,0,0};
1609 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1610 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1614 /* the character itself is going to be passed using the draw functions */
1615 return gFalse; /* gTrue= is_in_cache? */
1618 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1620 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1623 void GFXOutputDev::endType3Char(GfxState *state)
1626 msg("<debug> endType3Char");
1629 void GFXOutputDev::startPage(int pageNum, GfxState *state)
1631 this->currentpage = pageNum;
1633 int rot = doc->getPageRotate(1);
1634 gfxcolor_t white = {255,255,255,255};
1635 gfxcolor_t black = {255,0,0,0};
1637 gfxline_t clippath[5];
1638 PDFRectangle *r = this->page->getCropBox();
1640 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1641 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1642 Use CropBox, not MediaBox, as page size
1649 state->transform(r->x1,r->y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1650 state->transform(r->x2,r->y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1652 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1653 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1655 this->clipmovex = -(int)x1;
1656 this->clipmovey = -(int)y1;
1658 /* apply user clip box */
1659 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1660 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1661 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1662 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1663 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1664 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1666 x1 += this->clipmovex;
1667 y1 += this->clipmovey;
1668 x2 += this->clipmovex;
1669 y2 += this->clipmovey;
1672 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1674 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);
1676 msg("<verbose> page is rotated %d degrees", rot);
1678 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1679 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1680 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1681 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1682 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1683 device->startclip(device, clippath); outer_clip_box = 1;
1684 if(!config_transparent) {
1685 device->fill(device, clippath, &white);
1687 states[statepos].clipbbox.xmin = x1;
1688 states[statepos].clipbbox.ymin = x1;
1689 states[statepos].clipbbox.xmax = x2;
1690 states[statepos].clipbbox.ymax = y2;
1692 states[statepos].dashPattern = 0;
1693 states[statepos].dashLength = 0;
1694 states[statepos].dashStart = 0;
1696 this->last_char_gfxfont = 0;
1700 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1702 double x1, y1, x2, y2;
1703 gfxline_t points[5];
1706 msg("<debug> drawlink");
1708 link->getRect(&x1, &y1, &x2, &y2);
1709 cvtUserToDev(x1, y1, &x, &y);
1710 points[0].type = gfx_moveTo;
1711 points[0].x = points[4].x = x + user_movex + clipmovex;
1712 points[0].y = points[4].y = y + user_movey + clipmovey;
1713 points[0].next = &points[1];
1714 cvtUserToDev(x2, y1, &x, &y);
1715 points[1].type = gfx_lineTo;
1716 points[1].x = x + user_movex + clipmovex;
1717 points[1].y = y + user_movey + clipmovey;
1718 points[1].next = &points[2];
1719 cvtUserToDev(x2, y2, &x, &y);
1720 points[2].type = gfx_lineTo;
1721 points[2].x = x + user_movex + clipmovex;
1722 points[2].y = y + user_movey + clipmovey;
1723 points[2].next = &points[3];
1724 cvtUserToDev(x1, y2, &x, &y);
1725 points[3].type = gfx_lineTo;
1726 points[3].x = x + user_movex + clipmovex;
1727 points[3].y = y + user_movey + clipmovey;
1728 points[3].next = &points[4];
1729 cvtUserToDev(x1, y1, &x, &y);
1730 points[4].type = gfx_lineTo;
1731 points[4].x = x + user_movex + clipmovex;
1732 points[4].y = y + user_movey + clipmovey;
1735 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1736 points[0].x, points[0].y,
1737 points[1].x, points[1].y,
1738 points[2].x, points[2].y,
1739 points[3].x, points[3].y);
1741 if(getLogLevel() >= LOGLEVEL_TRACE) {
1742 dump_outline(points);
1745 LinkAction*action=link->getAction();
1748 const char*type = "-?-";
1751 msg("<trace> drawlink action=%d", action->getKind());
1752 switch(action->getKind())
1756 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1757 LinkDest *dest=NULL;
1758 if (ha->getDest()==NULL)
1759 dest=catalog->findDest(ha->getNamedDest());
1761 dest=ha->getDest()->copy();
1763 if (dest->isPageRef()){
1764 Ref pageref=dest->getPageRef();
1765 page=catalog->findPage(pageref.num,pageref.gen);
1767 else page=dest->getPageNum();
1768 sprintf(buf, "%d", page);
1776 LinkGoToR*l = (LinkGoToR*)action;
1777 GString*g = l->getFileName();
1779 s = strdup(g->getCString());
1781 /* if the GoToR link has no filename, then
1782 try to find a refernce in the *local*
1784 GString*g = l->getNamedDest();
1786 s = strdup(g->getCString());
1792 LinkNamed*l = (LinkNamed*)action;
1793 GString*name = l->getName();
1795 s = strdup(name->lowerCase()->getCString());
1796 named = name->getCString();
1799 if(strstr(s, "next") || strstr(s, "forward"))
1801 page = currentpage + 1;
1803 else if(strstr(s, "prev") || strstr(s, "back"))
1805 page = currentpage - 1;
1807 else if(strstr(s, "last") || strstr(s, "end"))
1809 if(this->page2page && this->num_pages) {
1810 page = this->page2page[this->num_pages-1];
1813 else if(strstr(s, "first") || strstr(s, "top"))
1821 case actionLaunch: {
1823 LinkLaunch*l = (LinkLaunch*)action;
1824 GString * str = new GString(l->getFileName());
1825 GString * params = l->getParams();
1827 str->append(params);
1828 s = strdup(str->getCString());
1835 LinkURI*l = (LinkURI*)action;
1836 GString*g = l->getURI();
1838 url = g->getCString();
1843 case actionUnknown: {
1845 LinkUnknown*l = (LinkUnknown*)action;
1850 msg("<error> Unknown link type!");
1855 if(!s) s = strdup("-?-");
1857 msg("<trace> drawlink s=%s", s);
1859 if(!gfxglobals->linkinfo && (page || s))
1861 msg("<notice> File contains links");
1862 gfxglobals->linkinfo = 1;
1868 for(t=1;t<=this->num_pages;t++) {
1869 if(this->page2page[t]==page) {
1879 sprintf(buf, "page%d", lpage);
1880 device->drawlink(device, points, buf);
1884 device->drawlink(device, points, s);
1885 if(this->config_linkdatafile) {
1886 FILE*fi = fopen(config_linkdatafile, "ab+");
1887 fprintf(fi, "%s\n", s);
1892 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1896 void GFXOutputDev::saveState(GfxState *state) {
1897 dbg("saveState %p", state); dbgindent+=2;
1899 msg("<trace> saveState %p", state);
1902 msg("<fatal> Too many nested states in pdf.");
1906 states[statepos].state = state;
1907 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1908 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1909 states[statepos].clipping = 0;
1910 states[statepos].olddevice = 0;
1911 states[statepos].clipbbox = states[statepos-1].clipbbox;
1913 states[statepos].dashPattern = states[statepos-1].dashPattern;
1914 states[statepos].dashStart = states[statepos-1].dashStart;
1915 states[statepos].dashLength = states[statepos-1].dashLength;
1918 void GFXOutputDev::restoreState(GfxState *state) {
1919 dbgindent-=2; dbg("restoreState %p", state);
1922 msg("<fatal> Invalid restoreState");
1925 msg("<trace> restoreState %p%s%s", state,
1926 states[statepos].softmask?" (end softmask)":"",
1927 states[statepos].clipping?" (end clipping)":"");
1928 if(states[statepos].softmask) {
1929 clearSoftMask(state);
1932 if(states[statepos].dashPattern) {
1933 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1934 free(states[statepos].dashPattern);
1935 states[statepos].dashPattern = 0;
1941 while(states[statepos].clipping) {
1942 device->endclip(device);
1943 states[statepos].clipping--;
1945 if(states[statepos].state!=state) {
1946 msg("<fatal> bad state nesting");
1949 for(t=0;t<=statepos;t++) {
1950 printf("%p ", states[t].state);
1956 states[statepos].state=0;
1960 void GFXOutputDev::updateLineDash(GfxState *state)
1962 if(states[statepos].dashPattern &&
1963 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1964 free(states[statepos].dashPattern);
1965 states[statepos].dashPattern = 0;
1967 double *pattern = 0;
1970 state->getLineDash(&pattern, &dashLength, &dashStart);
1971 msg("<debug> updateLineDash, %d dashes", dashLength);
1973 states[statepos].dashPattern = 0;
1974 states[statepos].dashLength = 0;
1976 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1977 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1978 states[statepos].dashPattern = p;
1979 states[statepos].dashLength = dashLength;
1980 states[statepos].dashStart = dashStart;
1984 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1986 this->page2page = page2page;
1987 this->num_pages = num_pages;
1990 void GFXOutputDev::updateLineWidth(GfxState *state)
1992 double width = state->getTransformedLineWidth();
1995 void GFXOutputDev::updateLineCap(GfxState *state)
1997 int c = state->getLineCap();
2000 void GFXOutputDev::updateLineJoin(GfxState *state)
2002 int j = state->getLineJoin();
2005 void GFXOutputDev::updateFillColor(GfxState *state)
2008 double opaq = state->getFillOpacity();
2009 state->getFillRGB(&rgb);
2011 void GFXOutputDev::updateFillOpacity(GfxState *state)
2014 double opaq = state->getFillOpacity();
2015 state->getFillRGB(&rgb);
2016 dbg("update fillopaq %f", opaq);
2018 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
2020 double opaq = state->getFillOpacity();
2021 dbg("update strokeopaq %f", opaq);
2023 void GFXOutputDev::updateFillOverprint(GfxState *state)
2025 double opaq = state->getFillOverprint();
2026 dbg("update filloverprint %f", opaq);
2028 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2030 double opaq = state->getStrokeOverprint();
2031 dbg("update strokeoverprint %f", opaq);
2033 void GFXOutputDev::updateTransfer(GfxState *state)
2035 dbg("update transfer");
2039 void GFXOutputDev::updateStrokeColor(GfxState *state)
2042 double opaq = state->getStrokeOpacity();
2043 state->getStrokeRGB(&rgb);
2046 void GFXOutputDev::updateFont(GfxState *state)
2048 GfxFont* gfxFont = state->getFont();
2052 char*id = getFontID(gfxFont);
2053 msg("<verbose> Updating font to %s", id);
2054 if(gfxFont->getType() == fontType3) {
2055 infofeature("Type3 fonts");
2056 if(!config_extrafontdata) {
2061 msg("<error> Internal Error: FontID is null");
2065 this->current_fontinfo = this->info->getFont(id);
2067 if(!this->current_fontinfo) {
2068 msg("<error> Internal Error: no fontinfo for font %s", id);
2071 if(!this->current_fontinfo->seen) {
2072 dumpFontInfo("<verbose>", gfxFont);
2075 current_gfxfont = this->current_fontinfo->getGfxFont();
2076 device->addfont(device, current_gfxfont);
2079 updateFontMatrix(state);
2082 #define SQR(x) ((x)*(x))
2084 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2086 if((newwidth<1 || newheight<1) ||
2087 (width<=newwidth || height<=newheight))
2089 unsigned char*newdata;
2091 newdata= (unsigned char*)malloc(newwidth*newheight);
2092 double fx = ((double)width)/newwidth;
2093 double fy = ((double)height)/newheight;
2095 int blocksize = (int)(8192/(fx*fy));
2096 int r = 8192*256/palettesize;
2097 for(x=0;x<newwidth;x++) {
2098 double ex = px + fx;
2099 int fromx = (int)px;
2101 int xweight1 = (int)((1-(px-fromx))*256);
2102 int xweight2 = (int)((ex-tox)*256);
2104 for(y=0;y<newheight;y++) {
2105 double ey = py + fy;
2106 int fromy = (int)py;
2108 int yweight1 = (int)((1-(py-fromy))*256);
2109 int yweight2 = (int)((ey-toy)*256);
2116 for(xx=fromx;xx<=tox;xx++)
2117 for(yy=fromy;yy<=toy;yy++) {
2118 int b = 1-data[width*yy+xx];
2120 if(xx==fromx) weight = (weight*xweight1)/256;
2121 if(xx==tox) weight = (weight*xweight2)/256;
2122 if(yy==fromy) weight = (weight*yweight1)/256;
2123 if(yy==toy) weight = (weight*yweight2)/256;
2126 //if(a) a=(palettesize-1)*r/blocksize;
2127 newdata[y*newwidth+x] = (a*blocksize)/r;
2135 #define IMAGE_TYPE_JPEG 0
2136 #define IMAGE_TYPE_LOSSLESS 1
2138 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2139 double x1,double y1,
2140 double x2,double y2,
2141 double x3,double y3,
2142 double x4,double y4, int type, int multiply)
2144 gfxcolor_t*newpic=0;
2146 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2147 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2149 gfxline_t p1,p2,p3,p4,p5;
2150 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2151 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2152 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2153 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2154 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2156 {p1.x = (int)(p1.x*20)/20.0;
2157 p1.y = (int)(p1.y*20)/20.0;
2158 p2.x = (int)(p2.x*20)/20.0;
2159 p2.y = (int)(p2.y*20)/20.0;
2160 p3.x = (int)(p3.x*20)/20.0;
2161 p3.y = (int)(p3.y*20)/20.0;
2162 p4.x = (int)(p4.x*20)/20.0;
2163 p4.y = (int)(p4.y*20)/20.0;
2164 p5.x = (int)(p5.x*20)/20.0;
2165 p5.y = (int)(p5.y*20)/20.0;
2169 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2170 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2172 m.tx = p1.x - 0.5*multiply;
2173 m.ty = p1.y - 0.5*multiply;
2176 img.data = (gfxcolor_t*)data;
2180 if(type == IMAGE_TYPE_JPEG)
2181 /* TODO: pass image_dpi to device instead */
2182 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2185 dev->fillbitmap(dev, &p1, &img, &m, 0);
2188 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2189 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2191 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2194 void drawimagelossless(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_LOSSLESS, multiply);
2201 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2202 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2203 GBool inlineImg, int mask, int*maskColors,
2204 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2206 /* the code in this function is *old*. It's not pretty, but it works. */
2208 double x1,y1,x2,y2,x3,y3,x4,y4;
2209 ImageStream *imgStr;
2214 unsigned char* maskbitmap = 0;
2217 ncomps = colorMap->getNumPixelComps();
2218 bits = colorMap->getBits();
2223 unsigned char buf[8];
2224 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2226 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2227 imgMaskStr->reset();
2228 unsigned char pal[256];
2229 int n = 1 << colorMap->getBits();
2234 maskColorMap->getGray(pixBuf, &gray);
2235 pal[t] = colToByte(gray);
2237 for (y = 0; y < maskHeight; y++) {
2238 for (x = 0; x < maskWidth; x++) {
2239 imgMaskStr->getPixel(buf);
2240 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2245 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2246 imgMaskStr->reset();
2247 for (y = 0; y < maskHeight; y++) {
2248 for (x = 0; x < maskWidth; x++) {
2249 imgMaskStr->getPixel(buf);
2251 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2259 imgStr = new ImageStream(str, width, ncomps,bits);
2262 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2264 msg("<verbose> Ignoring %d by %d image", width, height);
2265 unsigned char buf[8];
2267 for (y = 0; y < height; ++y)
2268 for (x = 0; x < width; ++x) {
2269 imgStr->getPixel(buf);
2277 this->transformXY(state, 0, 1, &x1, &y1);
2278 this->transformXY(state, 0, 0, &x2, &y2);
2279 this->transformXY(state, 1, 0, &x3, &y3);
2280 this->transformXY(state, 1, 1, &x4, &y4);
2283 /* as type 3 bitmaps are antialized, we need to place them
2284 at integer coordinates, otherwise flash player's antializing
2285 will kick in and make everything blurry */
2286 x1 = (int)(x1);y1 = (int)(y1);
2287 x2 = (int)(x2);y2 = (int)(y2);
2288 x3 = (int)(x3);y3 = (int)(y3);
2289 x4 = (int)(x4);y4 = (int)(y4);
2292 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2294 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2295 gfxglobals->pbminfo = 1;
2298 msg("<verbose> drawing %d by %d masked picture", width, height);
2300 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2301 msg("<notice> File contains jpeg pictures");
2302 gfxglobals->jpeginfo = 1;
2306 unsigned char buf[8];
2308 unsigned char*pic = new unsigned char[width*height];
2309 gfxcolor_t pal[256];
2311 state->getFillRGB(&rgb);
2313 memset(pal,255,sizeof(pal));
2314 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2315 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2316 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2317 pal[0].a = 255; pal[1].a = 0;
2320 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2321 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2322 for (y = 0; y < height; ++y)
2323 for (x = 0; x < width; ++x)
2325 imgStr->getPixel(buf);
2328 pic[width*y+x] = buf[0];
2332 unsigned char*pic2 = 0;
2335 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2344 height = realheight;
2348 /* make a black/white palette */
2350 float r = 255./(float)(numpalette-1);
2352 for(t=0;t<numpalette;t++) {
2353 pal[t].r = colToByte(rgb.r);
2354 pal[t].g = colToByte(rgb.g);
2355 pal[t].b = colToByte(rgb.b);
2356 pal[t].a = (unsigned char)(t*r);
2361 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2362 for (y = 0; y < height; ++y) {
2363 for (x = 0; x < width; ++x) {
2364 pic2[width*y+x] = pal[pic[y*width+x]];
2367 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2371 if(maskbitmap) free(maskbitmap);
2377 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2378 gfxcolor_t*pic=new gfxcolor_t[width*height];
2379 for (y = 0; y < height; ++y) {
2380 for (x = 0; x < width; ++x) {
2381 imgStr->getPixel(pixBuf);
2382 colorMap->getRGB(pixBuf, &rgb);
2383 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2384 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2385 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2386 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2388 int x1 = x*maskWidth/width;
2389 int y1 = y*maskHeight/height;
2390 int x2 = (x+1)*maskWidth/width;
2391 int y2 = (y+1)*maskHeight/height;
2393 unsigned int alpha=0;
2394 unsigned int count=0;
2395 for(xx=x1;xx<x2;xx++)
2396 for(yy=y1;yy<y2;yy++) {
2397 alpha += maskbitmap[yy*maskWidth+xx];
2401 pic[width*y+x].a = alpha / count;
2403 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2408 if(str->getKind()==strDCT)
2409 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2411 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2414 if(maskbitmap) free(maskbitmap);
2417 gfxcolor_t*pic=new gfxcolor_t[width*height];
2418 gfxcolor_t pal[256];
2419 int n = 1 << colorMap->getBits();
2421 for(t=0;t<256;t++) {
2423 colorMap->getRGB(pixBuf, &rgb);
2424 pal[t].r = (unsigned char)(colToByte(rgb.r));
2425 pal[t].g = (unsigned char)(colToByte(rgb.g));
2426 pal[t].b = (unsigned char)(colToByte(rgb.b));
2427 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2429 for (y = 0; y < height; ++y) {
2430 for (x = 0; x < width; ++x) {
2431 imgStr->getPixel(pixBuf);
2432 pic[width*y+x] = pal[pixBuf[0]];
2433 if(maskColors && *maskColors==pixBuf[0]) {
2434 pic[width*y+x].a = 0;
2439 if(maskWidth < width && maskHeight < height) {
2440 for(y = 0; y < height; y++) {
2441 for (x = 0; x < width; x++) {
2442 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2446 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2447 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2448 double dx = width / (double)maskWidth;
2449 double dy = height / (double)maskHeight;
2451 for(y = 0; y < maskHeight; y++) {
2453 for (x = 0; x < maskWidth; x++) {
2454 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2455 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2463 height = maskHeight;
2466 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2470 if(maskbitmap) free(maskbitmap);
2475 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2476 int width, int height, GBool invert,
2479 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2480 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2481 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2484 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2485 int width, int height, GfxImageColorMap *colorMap,
2486 int *maskColors, GBool inlineImg)
2488 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2489 colorMap?"colorMap":"no colorMap",
2490 maskColors?"maskColors":"no maskColors",
2492 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2493 colorMap?"colorMap":"no colorMap",
2494 maskColors?"maskColors":"no maskColors",
2497 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2498 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2499 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2502 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2503 int width, int height,
2504 GfxImageColorMap *colorMap,
2505 Stream *maskStr, int maskWidth, int maskHeight,
2508 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2509 colorMap?"colorMap":"no colorMap",
2510 maskWidth, maskHeight);
2511 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2512 colorMap?"colorMap":"no colorMap",
2513 maskWidth, maskHeight);
2515 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2516 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2517 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2520 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2521 int width, int height,
2522 GfxImageColorMap *colorMap,
2524 int maskWidth, int maskHeight,
2525 GfxImageColorMap *maskColorMap)
2527 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2528 colorMap?"colorMap":"no colorMap",
2529 maskWidth, maskHeight);
2530 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2531 colorMap?"colorMap":"no colorMap",
2532 maskWidth, maskHeight);
2534 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2535 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2536 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2539 void GFXOutputDev::stroke(GfxState *state)
2543 GfxPath * path = state->getPath();
2544 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2545 strokeGfxline(state, line, 0);
2549 void GFXOutputDev::fill(GfxState *state)
2551 gfxcolor_t col = getFillColor(state);
2552 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2554 GfxPath * path = state->getPath();
2555 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2556 if(!config_disable_polygon_conversion) {
2557 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2561 fillGfxLine(state, line, 0);
2565 void GFXOutputDev::eoFill(GfxState *state)
2567 gfxcolor_t col = getFillColor(state);
2568 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2570 GfxPath * path = state->getPath();
2571 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2572 fillGfxLine(state, line, 1);
2577 static const char* dirseparator()
2586 void addGlobalFont(const char*filename)
2588 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2589 memset(f, 0, sizeof(fontfile_t));
2590 f->filename = filename;
2591 int len = strlen(filename);
2592 char*r1 = strrchr((char*)filename, '/');
2593 char*r2 = strrchr((char*)filename, '\\');
2601 msg("<verbose> Adding font \"%s\".", filename);
2602 if(global_fonts_next) {
2603 global_fonts_next->next = f;
2604 global_fonts_next = global_fonts_next->next;
2606 global_fonts_next = global_fonts = f;
2610 void addGlobalLanguageDir(const char*dir)
2612 msg("<notice> Adding %s to language pack directories", dir);
2615 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2616 strcpy(config_file, dir);
2617 strcat(config_file, dirseparator());
2618 strcat(config_file, "add-to-xpdfrc");
2620 fi = fopen(config_file, "rb");
2622 msg("<error> Could not open %s", config_file);
2625 globalParams->parseFile(new GString(config_file), fi);
2629 void addGlobalFontDir(const char*dirname)
2631 #ifdef HAVE_DIRENT_H
2632 DIR*dir = opendir(dirname);
2634 msg("<warning> Couldn't open directory %s", dirname);
2640 ent = readdir (dir);
2644 char*name = ent->d_name;
2650 if(!strncasecmp(&name[l-4], ".pfa", 4))
2652 if(!strncasecmp(&name[l-4], ".pfb", 4))
2654 if(!strncasecmp(&name[l-4], ".ttf", 4))
2657 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2658 strcpy(fontname, dirname);
2659 strcat(fontname, dirseparator());
2660 strcat(fontname, name);
2661 addGlobalFont(fontname);
2665 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2668 msg("<warning> No dirent.h");
2672 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2673 GfxColorSpace *blendingColorSpace,
2674 GBool isolated, GBool knockout,
2677 const char*colormodename = "";
2679 if(blendingColorSpace) {
2680 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2682 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);
2683 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);
2685 //states[statepos].createsoftmask |= forSoftMask;
2686 states[statepos].createsoftmask = forSoftMask;
2687 states[statepos].transparencygroup = !forSoftMask;
2688 states[statepos].isolated = isolated;
2690 states[statepos].olddevice = this->device;
2691 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2692 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2694 gfxdevice_record_init(this->device, 0);
2696 /*if(!forSoftMask) { ////???
2697 state->setFillOpacity(0.0);
2702 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2705 gfxdevice_t*r = this->device;
2707 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2709 this->device = states[statepos].olddevice;
2711 msg("<error> Invalid state nesting");
2713 states[statepos].olddevice = 0;
2715 gfxresult_t*recording = r->finish(r);
2717 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2718 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2720 if(states[statepos].createsoftmask) {
2721 states[statepos-1].softmaskrecording = recording;
2723 states[statepos-1].grouprecording = recording;
2726 states[statepos].createsoftmask = 0;
2727 states[statepos].transparencygroup = 0;
2731 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2733 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2734 "colordodge","colorburn","hardlight","softlight","difference",
2735 "exclusion","hue","saturation","color","luminosity"};
2737 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2738 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2740 if(state->getBlendMode() == gfxBlendNormal)
2741 infofeature("transparency groups");
2744 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2745 warnfeature(buffer, 0);
2748 gfxresult_t*grouprecording = states[statepos].grouprecording;
2750 int blendmode = state->getBlendMode();
2751 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2752 int alpha = (int)(state->getFillOpacity()*255);
2753 if(blendmode == gfxBlendMultiply && alpha>200)
2756 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2757 gfxdevice_ops_init(&ops, this->device, alpha);
2758 gfxresult_record_replay(grouprecording, &ops, 0);
2761 grouprecording->destroy(grouprecording);
2763 states[statepos].grouprecording = 0;
2766 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2768 if(states[statepos].softmask) {
2769 /* shouldn't happen, but *does* happen */
2770 clearSoftMask(state);
2773 /* alpha = 1: retrieve mask values from alpha layer
2774 alpha = 0: retrieve mask values from luminance */
2776 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2777 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2778 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2779 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2781 infofeature("soft masks");
2783 warnfeature("soft masks from alpha channel",0);
2785 if(states[statepos].olddevice) {
2786 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2789 states[statepos].olddevice = this->device;
2790 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2791 gfxdevice_record_init(this->device, 0);
2793 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2795 states[statepos].softmask = 1;
2796 states[statepos].softmask_alpha = alpha;
2799 static inline Guchar div255(int x) {
2800 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2803 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2805 if(c < min) c = min;
2806 if(c > max) c = max;
2810 void GFXOutputDev::clearSoftMask(GfxState *state)
2812 if(!states[statepos].softmask)
2814 states[statepos].softmask = 0;
2815 dbg("clearSoftMask statepos=%d", statepos);
2816 msg("<verbose> clearSoftMask statepos=%d", statepos);
2818 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2819 msg("<error> Error in softmask/tgroup ordering");
2823 gfxresult_t*mask = states[statepos].softmaskrecording;
2824 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2825 this->device = states[statepos].olddevice;
2827 /* get outline of all objects below the soft mask */
2828 gfxdevice_t uniondev;
2829 gfxdevice_union_init(&uniondev, 0);
2830 gfxresult_record_replay(below, &uniondev, 0);
2831 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2832 uniondev.finish(&uniondev);
2833 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2834 gfxline_free(belowoutline);belowoutline=0;
2836 this->device->startclip(this->device, belowoutline);
2837 gfxresult_record_replay(below, this->device, 0);
2838 gfxresult_record_replay(mask, this->device, 0);
2839 this->device->endclip(this->device);
2842 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2843 if(width<=0 || height<=0)
2846 gfxdevice_t belowrender;
2847 gfxdevice_render_init(&belowrender);
2848 if(states[statepos+1].isolated) {
2849 belowrender.setparameter(&belowrender, "fillwhite", "1");
2851 belowrender.setparameter(&belowrender, "antialize", "2");
2852 belowrender.startpage(&belowrender, width, height);
2853 gfxresult_record_replay(below, &belowrender, 0);
2854 belowrender.endpage(&belowrender);
2855 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2856 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2857 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2859 gfxdevice_t maskrender;
2860 gfxdevice_render_init(&maskrender);
2861 maskrender.startpage(&maskrender, width, height);
2862 gfxresult_record_replay(mask, &maskrender, 0);
2863 maskrender.endpage(&maskrender);
2864 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2865 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2867 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2868 msg("<fatal> Internal error in mask drawing");
2873 for(y=0;y<height;y++) {
2874 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2875 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2876 for(x=0;x<width;x++) {
2878 if(states[statepos].softmask_alpha) {
2881 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2884 l2->a = div255(alpha*l2->a);
2886 /* DON'T premultiply alpha- this is done by fillbitmap,
2887 depending on the output device */
2888 //l2->r = div255(alpha*l2->r);
2889 //l2->g = div255(alpha*l2->g);
2890 //l2->b = div255(alpha*l2->b);
2896 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2899 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2900 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2902 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2904 mask->destroy(mask);
2905 below->destroy(below);
2906 maskresult->destroy(maskresult);
2907 belowresult->destroy(belowresult);
2908 states[statepos].softmaskrecording = 0;
2913 // public: ~MemCheck()
2915 // delete globalParams;globalParams=0;
2916 // Object::memCheck(stderr);
2917 // gMemReport(stderr);