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, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
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];
1639 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1640 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1641 Use CropBox, not MediaBox, as page size
1648 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1649 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1651 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1652 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1654 this->clipmovex = -(int)x1;
1655 this->clipmovey = -(int)y1;
1657 /* apply user clip box */
1658 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1659 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1660 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1661 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1662 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1663 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1665 x1 += this->clipmovex;
1666 y1 += this->clipmovey;
1667 x2 += this->clipmovex;
1668 y2 += this->clipmovey;
1671 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1673 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);
1675 msg("<verbose> page is rotated %d degrees", rot);
1677 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1678 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1679 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1680 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1681 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1682 device->startclip(device, clippath); outer_clip_box = 1;
1683 if(!config_transparent) {
1684 device->fill(device, clippath, &white);
1686 states[statepos].clipbbox.xmin = x1;
1687 states[statepos].clipbbox.ymin = x1;
1688 states[statepos].clipbbox.xmax = x2;
1689 states[statepos].clipbbox.ymax = y2;
1691 states[statepos].dashPattern = 0;
1692 states[statepos].dashLength = 0;
1693 states[statepos].dashStart = 0;
1695 this->last_char_gfxfont = 0;
1699 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1701 double x1, y1, x2, y2;
1702 gfxline_t points[5];
1705 msg("<debug> drawlink");
1707 link->getRect(&x1, &y1, &x2, &y2);
1708 cvtUserToDev(x1, y1, &x, &y);
1709 points[0].type = gfx_moveTo;
1710 points[0].x = points[4].x = x + user_movex + clipmovex;
1711 points[0].y = points[4].y = y + user_movey + clipmovey;
1712 points[0].next = &points[1];
1713 cvtUserToDev(x2, y1, &x, &y);
1714 points[1].type = gfx_lineTo;
1715 points[1].x = x + user_movex + clipmovex;
1716 points[1].y = y + user_movey + clipmovey;
1717 points[1].next = &points[2];
1718 cvtUserToDev(x2, y2, &x, &y);
1719 points[2].type = gfx_lineTo;
1720 points[2].x = x + user_movex + clipmovex;
1721 points[2].y = y + user_movey + clipmovey;
1722 points[2].next = &points[3];
1723 cvtUserToDev(x1, y2, &x, &y);
1724 points[3].type = gfx_lineTo;
1725 points[3].x = x + user_movex + clipmovex;
1726 points[3].y = y + user_movey + clipmovey;
1727 points[3].next = &points[4];
1728 cvtUserToDev(x1, y1, &x, &y);
1729 points[4].type = gfx_lineTo;
1730 points[4].x = x + user_movex + clipmovex;
1731 points[4].y = y + user_movey + clipmovey;
1734 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1735 points[0].x, points[0].y,
1736 points[1].x, points[1].y,
1737 points[2].x, points[2].y,
1738 points[3].x, points[3].y);
1740 if(getLogLevel() >= LOGLEVEL_TRACE) {
1741 dump_outline(points);
1744 LinkAction*action=link->getAction();
1747 const char*type = "-?-";
1750 msg("<trace> drawlink action=%d", action->getKind());
1751 switch(action->getKind())
1755 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1756 LinkDest *dest=NULL;
1757 if (ha->getDest()==NULL)
1758 dest=catalog->findDest(ha->getNamedDest());
1760 dest=ha->getDest()->copy();
1762 if (dest->isPageRef()){
1763 Ref pageref=dest->getPageRef();
1764 page=catalog->findPage(pageref.num,pageref.gen);
1766 else page=dest->getPageNum();
1767 sprintf(buf, "%d", page);
1775 LinkGoToR*l = (LinkGoToR*)action;
1776 GString*g = l->getFileName();
1778 s = strdup(g->getCString());
1780 /* if the GoToR link has no filename, then
1781 try to find a refernce in the *local*
1783 GString*g = l->getNamedDest();
1785 s = strdup(g->getCString());
1791 LinkNamed*l = (LinkNamed*)action;
1792 GString*name = l->getName();
1794 s = strdup(name->lowerCase()->getCString());
1795 named = name->getCString();
1798 if(strstr(s, "next") || strstr(s, "forward"))
1800 page = currentpage + 1;
1802 else if(strstr(s, "prev") || strstr(s, "back"))
1804 page = currentpage - 1;
1806 else if(strstr(s, "last") || strstr(s, "end"))
1808 if(this->page2page && this->num_pages) {
1809 page = this->page2page[this->num_pages-1];
1812 else if(strstr(s, "first") || strstr(s, "top"))
1820 case actionLaunch: {
1822 LinkLaunch*l = (LinkLaunch*)action;
1823 GString * str = new GString(l->getFileName());
1824 GString * params = l->getParams();
1826 str->append(params);
1827 s = strdup(str->getCString());
1834 LinkURI*l = (LinkURI*)action;
1835 GString*g = l->getURI();
1837 url = g->getCString();
1842 case actionUnknown: {
1844 LinkUnknown*l = (LinkUnknown*)action;
1849 msg("<error> Unknown link type!");
1854 if(!s) s = strdup("-?-");
1856 msg("<trace> drawlink s=%s", s);
1858 if(!gfxglobals->linkinfo && (page || s))
1860 msg("<notice> File contains links");
1861 gfxglobals->linkinfo = 1;
1867 for(t=1;t<=this->num_pages;t++) {
1868 if(this->page2page[t]==page) {
1878 sprintf(buf, "page%d", lpage);
1879 device->drawlink(device, points, buf);
1883 device->drawlink(device, points, s);
1884 if(this->config_linkdatafile) {
1885 FILE*fi = fopen(config_linkdatafile, "ab+");
1886 fprintf(fi, "%s\n", s);
1891 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1895 void GFXOutputDev::saveState(GfxState *state) {
1896 dbg("saveState %p", state); dbgindent+=2;
1898 msg("<trace> saveState %p", state);
1901 msg("<fatal> Too many nested states in pdf.");
1905 states[statepos].state = state;
1906 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1907 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1908 states[statepos].clipping = 0;
1909 states[statepos].olddevice = 0;
1910 states[statepos].clipbbox = states[statepos-1].clipbbox;
1912 states[statepos].dashPattern = states[statepos-1].dashPattern;
1913 states[statepos].dashStart = states[statepos-1].dashStart;
1914 states[statepos].dashLength = states[statepos-1].dashLength;
1917 void GFXOutputDev::restoreState(GfxState *state) {
1918 dbgindent-=2; dbg("restoreState %p", state);
1921 msg("<fatal> Invalid restoreState");
1924 msg("<trace> restoreState %p%s%s", state,
1925 states[statepos].softmask?" (end softmask)":"",
1926 states[statepos].clipping?" (end clipping)":"");
1927 if(states[statepos].softmask) {
1928 clearSoftMask(state);
1931 if(states[statepos].dashPattern) {
1932 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1933 free(states[statepos].dashPattern);
1934 states[statepos].dashPattern = 0;
1940 while(states[statepos].clipping) {
1941 device->endclip(device);
1942 states[statepos].clipping--;
1944 if(states[statepos].state!=state) {
1945 msg("<fatal> bad state nesting");
1948 for(t=0;t<=statepos;t++) {
1949 printf("%p ", states[t].state);
1955 states[statepos].state=0;
1959 void GFXOutputDev::updateLineDash(GfxState *state)
1961 if(states[statepos].dashPattern &&
1962 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1963 free(states[statepos].dashPattern);
1964 states[statepos].dashPattern = 0;
1966 double *pattern = 0;
1969 state->getLineDash(&pattern, &dashLength, &dashStart);
1970 msg("<debug> updateLineDash, %d dashes", dashLength);
1972 states[statepos].dashPattern = 0;
1973 states[statepos].dashLength = 0;
1975 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1976 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1977 states[statepos].dashPattern = p;
1978 states[statepos].dashLength = dashLength;
1979 states[statepos].dashStart = dashStart;
1983 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1985 this->page2page = page2page;
1986 this->num_pages = num_pages;
1989 void GFXOutputDev::updateLineWidth(GfxState *state)
1991 double width = state->getTransformedLineWidth();
1994 void GFXOutputDev::updateLineCap(GfxState *state)
1996 int c = state->getLineCap();
1999 void GFXOutputDev::updateLineJoin(GfxState *state)
2001 int j = state->getLineJoin();
2004 void GFXOutputDev::updateFillColor(GfxState *state)
2007 double opaq = state->getFillOpacity();
2008 state->getFillRGB(&rgb);
2010 void GFXOutputDev::updateFillOpacity(GfxState *state)
2013 double opaq = state->getFillOpacity();
2014 state->getFillRGB(&rgb);
2015 dbg("update fillopaq %f", opaq);
2017 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
2019 double opaq = state->getFillOpacity();
2020 dbg("update strokeopaq %f", opaq);
2022 void GFXOutputDev::updateFillOverprint(GfxState *state)
2024 double opaq = state->getFillOverprint();
2025 dbg("update filloverprint %f", opaq);
2027 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2029 double opaq = state->getStrokeOverprint();
2030 dbg("update strokeoverprint %f", opaq);
2032 void GFXOutputDev::updateTransfer(GfxState *state)
2034 dbg("update transfer");
2038 void GFXOutputDev::updateStrokeColor(GfxState *state)
2041 double opaq = state->getStrokeOpacity();
2042 state->getStrokeRGB(&rgb);
2045 void GFXOutputDev::updateFont(GfxState *state)
2047 GfxFont* gfxFont = state->getFont();
2051 char*id = getFontID(gfxFont);
2052 msg("<verbose> Updating font to %s", id);
2053 if(gfxFont->getType() == fontType3) {
2054 infofeature("Type3 fonts");
2055 if(!config_extrafontdata) {
2060 msg("<error> Internal Error: FontID is null");
2064 this->current_fontinfo = this->info->getFont(id);
2066 if(!this->current_fontinfo) {
2067 msg("<error> Internal Error: no fontinfo for font %s", id);
2070 if(!this->current_fontinfo->seen) {
2071 dumpFontInfo("<verbose>", gfxFont);
2074 current_gfxfont = this->current_fontinfo->getGfxFont();
2075 device->addfont(device, current_gfxfont);
2078 updateFontMatrix(state);
2081 #define SQR(x) ((x)*(x))
2083 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2085 if((newwidth<1 || newheight<1) ||
2086 (width<=newwidth || height<=newheight))
2088 unsigned char*newdata;
2090 newdata= (unsigned char*)malloc(newwidth*newheight);
2091 double fx = ((double)width)/newwidth;
2092 double fy = ((double)height)/newheight;
2094 int blocksize = (int)(8192/(fx*fy));
2095 int r = 8192*256/palettesize;
2096 for(x=0;x<newwidth;x++) {
2097 double ex = px + fx;
2098 int fromx = (int)px;
2100 int xweight1 = (int)((1-(px-fromx))*256);
2101 int xweight2 = (int)((ex-tox)*256);
2103 for(y=0;y<newheight;y++) {
2104 double ey = py + fy;
2105 int fromy = (int)py;
2107 int yweight1 = (int)((1-(py-fromy))*256);
2108 int yweight2 = (int)((ey-toy)*256);
2115 for(xx=fromx;xx<=tox;xx++)
2116 for(yy=fromy;yy<=toy;yy++) {
2117 int b = 1-data[width*yy+xx];
2119 if(xx==fromx) weight = (weight*xweight1)/256;
2120 if(xx==tox) weight = (weight*xweight2)/256;
2121 if(yy==fromy) weight = (weight*yweight1)/256;
2122 if(yy==toy) weight = (weight*yweight2)/256;
2125 //if(a) a=(palettesize-1)*r/blocksize;
2126 newdata[y*newwidth+x] = (a*blocksize)/r;
2134 #define IMAGE_TYPE_JPEG 0
2135 #define IMAGE_TYPE_LOSSLESS 1
2137 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2138 double x1,double y1,
2139 double x2,double y2,
2140 double x3,double y3,
2141 double x4,double y4, int type, int multiply)
2143 gfxcolor_t*newpic=0;
2145 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2146 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2148 gfxline_t p1,p2,p3,p4,p5;
2149 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2150 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2151 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2152 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2153 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2155 {p1.x = (int)(p1.x*20)/20.0;
2156 p1.y = (int)(p1.y*20)/20.0;
2157 p2.x = (int)(p2.x*20)/20.0;
2158 p2.y = (int)(p2.y*20)/20.0;
2159 p3.x = (int)(p3.x*20)/20.0;
2160 p3.y = (int)(p3.y*20)/20.0;
2161 p4.x = (int)(p4.x*20)/20.0;
2162 p4.y = (int)(p4.y*20)/20.0;
2163 p5.x = (int)(p5.x*20)/20.0;
2164 p5.y = (int)(p5.y*20)/20.0;
2168 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2169 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2171 m.tx = p1.x - 0.5*multiply;
2172 m.ty = p1.y - 0.5*multiply;
2175 img.data = (gfxcolor_t*)data;
2179 if(type == IMAGE_TYPE_JPEG)
2180 /* TODO: pass image_dpi to device instead */
2181 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2184 dev->fillbitmap(dev, &p1, &img, &m, 0);
2187 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2188 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2190 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2193 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2194 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2196 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2200 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2201 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2202 GBool inlineImg, int mask, int*maskColors,
2203 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2205 /* the code in this function is *old*. It's not pretty, but it works. */
2207 double x1,y1,x2,y2,x3,y3,x4,y4;
2208 ImageStream *imgStr;
2213 unsigned char* maskbitmap = 0;
2216 ncomps = colorMap->getNumPixelComps();
2217 bits = colorMap->getBits();
2222 unsigned char buf[8];
2223 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2225 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2226 imgMaskStr->reset();
2227 unsigned char pal[256];
2228 int n = 1 << colorMap->getBits();
2233 maskColorMap->getGray(pixBuf, &gray);
2234 pal[t] = colToByte(gray);
2236 for (y = 0; y < maskHeight; y++) {
2237 for (x = 0; x < maskWidth; x++) {
2238 imgMaskStr->getPixel(buf);
2239 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2244 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2245 imgMaskStr->reset();
2246 for (y = 0; y < maskHeight; y++) {
2247 for (x = 0; x < maskWidth; x++) {
2248 imgMaskStr->getPixel(buf);
2250 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2258 imgStr = new ImageStream(str, width, ncomps,bits);
2261 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2263 msg("<verbose> Ignoring %d by %d image", width, height);
2264 unsigned char buf[8];
2266 for (y = 0; y < height; ++y)
2267 for (x = 0; x < width; ++x) {
2268 imgStr->getPixel(buf);
2276 this->transformXY(state, 0, 1, &x1, &y1);
2277 this->transformXY(state, 0, 0, &x2, &y2);
2278 this->transformXY(state, 1, 0, &x3, &y3);
2279 this->transformXY(state, 1, 1, &x4, &y4);
2282 /* as type 3 bitmaps are antialized, we need to place them
2283 at integer coordinates, otherwise flash player's antializing
2284 will kick in and make everything blurry */
2285 x1 = (int)(x1);y1 = (int)(y1);
2286 x2 = (int)(x2);y2 = (int)(y2);
2287 x3 = (int)(x3);y3 = (int)(y3);
2288 x4 = (int)(x4);y4 = (int)(y4);
2291 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2293 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2294 gfxglobals->pbminfo = 1;
2297 msg("<verbose> drawing %d by %d masked picture", width, height);
2299 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2300 msg("<notice> File contains jpeg pictures");
2301 gfxglobals->jpeginfo = 1;
2305 unsigned char buf[8];
2307 unsigned char*pic = new unsigned char[width*height];
2308 gfxcolor_t pal[256];
2310 state->getFillRGB(&rgb);
2312 memset(pal,255,sizeof(pal));
2313 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2314 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2315 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2316 pal[0].a = 255; pal[1].a = 0;
2319 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2320 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2321 for (y = 0; y < height; ++y)
2322 for (x = 0; x < width; ++x)
2324 imgStr->getPixel(buf);
2327 pic[width*y+x] = buf[0];
2331 unsigned char*pic2 = 0;
2334 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2343 height = realheight;
2347 /* make a black/white palette */
2349 float r = 255./(float)(numpalette-1);
2351 for(t=0;t<numpalette;t++) {
2352 pal[t].r = colToByte(rgb.r);
2353 pal[t].g = colToByte(rgb.g);
2354 pal[t].b = colToByte(rgb.b);
2355 pal[t].a = (unsigned char)(t*r);
2360 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2361 for (y = 0; y < height; ++y) {
2362 for (x = 0; x < width; ++x) {
2363 pic2[width*y+x] = pal[pic[y*width+x]];
2366 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2370 if(maskbitmap) free(maskbitmap);
2376 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2377 gfxcolor_t*pic=new gfxcolor_t[width*height];
2378 for (y = 0; y < height; ++y) {
2379 for (x = 0; x < width; ++x) {
2380 imgStr->getPixel(pixBuf);
2381 colorMap->getRGB(pixBuf, &rgb);
2382 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2383 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2384 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2385 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2387 int x1 = x*maskWidth/width;
2388 int y1 = y*maskHeight/height;
2389 int x2 = (x+1)*maskWidth/width;
2390 int y2 = (y+1)*maskHeight/height;
2392 unsigned int alpha=0;
2393 unsigned int count=0;
2394 for(xx=x1;xx<x2;xx++)
2395 for(yy=y1;yy<y2;yy++) {
2396 alpha += maskbitmap[yy*maskWidth+xx];
2400 pic[width*y+x].a = alpha / count;
2402 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2407 if(str->getKind()==strDCT)
2408 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2410 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2413 if(maskbitmap) free(maskbitmap);
2416 gfxcolor_t*pic=new gfxcolor_t[width*height];
2417 gfxcolor_t pal[256];
2418 int n = 1 << colorMap->getBits();
2420 for(t=0;t<256;t++) {
2422 colorMap->getRGB(pixBuf, &rgb);
2423 pal[t].r = (unsigned char)(colToByte(rgb.r));
2424 pal[t].g = (unsigned char)(colToByte(rgb.g));
2425 pal[t].b = (unsigned char)(colToByte(rgb.b));
2426 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2428 for (y = 0; y < height; ++y) {
2429 for (x = 0; x < width; ++x) {
2430 imgStr->getPixel(pixBuf);
2431 pic[width*y+x] = pal[pixBuf[0]];
2432 if(maskColors && *maskColors==pixBuf[0]) {
2433 pic[width*y+x].a = 0;
2438 if(maskWidth < width && maskHeight < height) {
2439 for(y = 0; y < height; y++) {
2440 for (x = 0; x < width; x++) {
2441 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2445 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2446 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2447 double dx = width / (double)maskWidth;
2448 double dy = height / (double)maskHeight;
2450 for(y = 0; y < maskHeight; y++) {
2452 for (x = 0; x < maskWidth; x++) {
2453 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2454 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2462 height = maskHeight;
2465 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2469 if(maskbitmap) free(maskbitmap);
2474 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2475 int width, int height, GBool invert,
2478 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2479 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2480 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2483 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2484 int width, int height, GfxImageColorMap *colorMap,
2485 int *maskColors, GBool inlineImg)
2487 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2488 colorMap?"colorMap":"no colorMap",
2489 maskColors?"maskColors":"no maskColors",
2491 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2492 colorMap?"colorMap":"no colorMap",
2493 maskColors?"maskColors":"no maskColors",
2496 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2497 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2498 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2501 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2502 int width, int height,
2503 GfxImageColorMap *colorMap,
2504 Stream *maskStr, int maskWidth, int maskHeight,
2507 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2508 colorMap?"colorMap":"no colorMap",
2509 maskWidth, maskHeight);
2510 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2511 colorMap?"colorMap":"no colorMap",
2512 maskWidth, maskHeight);
2514 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2515 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2516 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2519 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2520 int width, int height,
2521 GfxImageColorMap *colorMap,
2523 int maskWidth, int maskHeight,
2524 GfxImageColorMap *maskColorMap)
2526 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2527 colorMap?"colorMap":"no colorMap",
2528 maskWidth, maskHeight);
2529 msg("<verbose> drawSoftMaskedImage %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, 0, maskColorMap);
2538 void GFXOutputDev::stroke(GfxState *state)
2542 GfxPath * path = state->getPath();
2543 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2544 strokeGfxline(state, line, 0);
2548 void GFXOutputDev::fill(GfxState *state)
2550 gfxcolor_t col = getFillColor(state);
2551 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2553 GfxPath * path = state->getPath();
2554 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2555 if(!config_disable_polygon_conversion) {
2556 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2560 fillGfxLine(state, line, 0);
2564 void GFXOutputDev::eoFill(GfxState *state)
2566 gfxcolor_t col = getFillColor(state);
2567 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2569 GfxPath * path = state->getPath();
2570 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2571 fillGfxLine(state, line, 1);
2576 static const char* dirseparator()
2585 void addGlobalFont(const char*filename)
2587 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2588 memset(f, 0, sizeof(fontfile_t));
2589 f->filename = filename;
2590 int len = strlen(filename);
2591 char*r1 = strrchr((char*)filename, '/');
2592 char*r2 = strrchr((char*)filename, '\\');
2600 msg("<verbose> Adding font \"%s\".", filename);
2601 if(global_fonts_next) {
2602 global_fonts_next->next = f;
2603 global_fonts_next = global_fonts_next->next;
2605 global_fonts_next = global_fonts = f;
2609 void addGlobalLanguageDir(const char*dir)
2611 msg("<notice> Adding %s to language pack directories", dir);
2614 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2615 strcpy(config_file, dir);
2616 strcat(config_file, dirseparator());
2617 strcat(config_file, "add-to-xpdfrc");
2619 fi = fopen(config_file, "rb");
2621 msg("<error> Could not open %s", config_file);
2624 globalParams->parseFile(new GString(config_file), fi);
2628 void addGlobalFontDir(const char*dirname)
2630 #ifdef HAVE_DIRENT_H
2631 DIR*dir = opendir(dirname);
2633 msg("<warning> Couldn't open directory %s", dirname);
2639 ent = readdir (dir);
2643 char*name = ent->d_name;
2649 if(!strncasecmp(&name[l-4], ".pfa", 4))
2651 if(!strncasecmp(&name[l-4], ".pfb", 4))
2653 if(!strncasecmp(&name[l-4], ".ttf", 4))
2656 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2657 strcpy(fontname, dirname);
2658 strcat(fontname, dirseparator());
2659 strcat(fontname, name);
2660 addGlobalFont(fontname);
2664 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2667 msg("<warning> No dirent.h");
2671 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2672 GfxColorSpace *blendingColorSpace,
2673 GBool isolated, GBool knockout,
2676 const char*colormodename = "";
2678 if(blendingColorSpace) {
2679 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2681 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);
2682 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);
2684 //states[statepos].createsoftmask |= forSoftMask;
2685 states[statepos].createsoftmask = forSoftMask;
2686 states[statepos].transparencygroup = !forSoftMask;
2687 states[statepos].isolated = isolated;
2689 states[statepos].olddevice = this->device;
2690 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2691 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2693 gfxdevice_record_init(this->device, 0);
2695 /*if(!forSoftMask) { ////???
2696 state->setFillOpacity(0.0);
2701 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2704 gfxdevice_t*r = this->device;
2706 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2708 this->device = states[statepos].olddevice;
2710 msg("<error> Invalid state nesting");
2712 states[statepos].olddevice = 0;
2714 gfxresult_t*recording = r->finish(r);
2716 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2717 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2719 if(states[statepos].createsoftmask) {
2720 states[statepos-1].softmaskrecording = recording;
2722 states[statepos-1].grouprecording = recording;
2725 states[statepos].createsoftmask = 0;
2726 states[statepos].transparencygroup = 0;
2730 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2732 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2733 "colordodge","colorburn","hardlight","softlight","difference",
2734 "exclusion","hue","saturation","color","luminosity"};
2736 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2737 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2739 if(state->getBlendMode() == gfxBlendNormal)
2740 infofeature("transparency groups");
2743 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2744 warnfeature(buffer, 0);
2747 gfxresult_t*grouprecording = states[statepos].grouprecording;
2749 int blendmode = state->getBlendMode();
2750 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2751 int alpha = (int)(state->getFillOpacity()*255);
2752 if(blendmode == gfxBlendMultiply && alpha>200)
2755 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2756 gfxdevice_ops_init(&ops, this->device, alpha);
2757 gfxresult_record_replay(grouprecording, &ops, 0);
2760 grouprecording->destroy(grouprecording);
2762 states[statepos].grouprecording = 0;
2765 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2767 if(states[statepos].softmask) {
2768 /* shouldn't happen, but *does* happen */
2769 clearSoftMask(state);
2772 /* alpha = 1: retrieve mask values from alpha layer
2773 alpha = 0: retrieve mask values from luminance */
2775 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2776 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2777 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2778 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2780 infofeature("soft masks");
2782 warnfeature("soft masks from alpha channel",0);
2784 if(states[statepos].olddevice) {
2785 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2788 states[statepos].olddevice = this->device;
2789 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2790 gfxdevice_record_init(this->device, 0);
2792 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2794 states[statepos].softmask = 1;
2795 states[statepos].softmask_alpha = alpha;
2798 static inline Guchar div255(int x) {
2799 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2802 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2804 if(c < min) c = min;
2805 if(c > max) c = max;
2809 void GFXOutputDev::clearSoftMask(GfxState *state)
2811 if(!states[statepos].softmask)
2813 states[statepos].softmask = 0;
2814 dbg("clearSoftMask statepos=%d", statepos);
2815 msg("<verbose> clearSoftMask statepos=%d", statepos);
2817 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2818 msg("<error> Error in softmask/tgroup ordering");
2822 gfxresult_t*mask = states[statepos].softmaskrecording;
2823 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2824 this->device = states[statepos].olddevice;
2826 /* get outline of all objects below the soft mask */
2827 gfxdevice_t uniondev;
2828 gfxdevice_union_init(&uniondev, 0);
2829 gfxresult_record_replay(below, &uniondev, 0);
2830 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2831 uniondev.finish(&uniondev);
2832 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2833 gfxline_free(belowoutline);belowoutline=0;
2835 this->device->startclip(this->device, belowoutline);
2836 gfxresult_record_replay(below, this->device, 0);
2837 gfxresult_record_replay(mask, this->device, 0);
2838 this->device->endclip(this->device);
2841 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2842 if(width<=0 || height<=0)
2845 gfxdevice_t belowrender;
2846 gfxdevice_render_init(&belowrender);
2847 if(states[statepos+1].isolated) {
2848 belowrender.setparameter(&belowrender, "fillwhite", "1");
2850 belowrender.setparameter(&belowrender, "antialize", "2");
2851 belowrender.startpage(&belowrender, width, height);
2852 gfxresult_record_replay(below, &belowrender, 0);
2853 belowrender.endpage(&belowrender);
2854 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2855 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2856 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2858 gfxdevice_t maskrender;
2859 gfxdevice_render_init(&maskrender);
2860 maskrender.startpage(&maskrender, width, height);
2861 gfxresult_record_replay(mask, &maskrender, 0);
2862 maskrender.endpage(&maskrender);
2863 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2864 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2866 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2867 msg("<fatal> Internal error in mask drawing");
2872 for(y=0;y<height;y++) {
2873 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2874 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2875 for(x=0;x<width;x++) {
2877 if(states[statepos].softmask_alpha) {
2880 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2883 l2->a = div255(alpha*l2->a);
2885 /* DON'T premultiply alpha- this is done by fillbitmap,
2886 depending on the output device */
2887 //l2->r = div255(alpha*l2->r);
2888 //l2->g = div255(alpha*l2->g);
2889 //l2->b = div255(alpha*l2->b);
2895 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2898 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2899 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2901 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2903 mask->destroy(mask);
2904 below->destroy(below);
2905 maskresult->destroy(maskresult);
2906 belowresult->destroy(belowresult);
2907 states[statepos].softmaskrecording = 0;
2912 // public: ~MemCheck()
2914 // delete globalParams;globalParams=0;
2915 // Object::memCheck(stderr);
2916 // gMemReport(stderr);