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>
40 #include "../../config.h"
43 #include "popplercompat.h"
45 #include <goo/GooString.h>
46 #include <goo/gfile.h>
48 #include "xpdf/config.h"
63 #include "OutputDev.h"
66 //#include "NameToUnicodeTable.h"
67 #include "GlobalParams.h"
68 #include "GFXOutputDev.h"
70 // swftools header files
72 #include "../gfxdevice.h"
73 #include "../gfxtools.h"
74 #include "../gfxfont.h"
75 #include "../gfxpoly.h"
76 #include "../devices/record.h"
77 #include "../devices/ops.h"
78 #include "../devices/polyops.h"
79 #include "../devices/render.h"
86 #define SQRT2 1.41421356237309504880
88 typedef struct _fontfile
91 int len; // basename length
93 struct _fontfile*next;
98 static fontfile_t* global_fonts = 0;
99 static fontfile_t* global_fonts_next = 0;
101 static int fontnum = 0;
113 DisplayFontParam *dfp;
115 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
116 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
117 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
118 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
119 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
120 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
121 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
122 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
123 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
124 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
125 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
126 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
127 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
128 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
131 static int verbose = 0;
132 static int dbgindent = 1;
133 static void dbg(const char*format, ...)
140 va_start(arglist, format);
141 vsnprintf(buf, sizeof(buf)-1, format, arglist);
144 while(l && buf[l-1]=='\n') {
149 int indent = dbgindent;
158 GFXOutputGlobals*gfxglobals=0;
160 GFXOutputGlobals::GFXOutputGlobals()
162 this->featurewarnings = 0;
164 this->textmodeinfo = 0;
168 GFXOutputGlobals::~GFXOutputGlobals()
170 feature_t*f = this->featurewarnings;
172 feature_t*next = f->next;
174 free(f->string);f->string =0;
180 this->featurewarnings = 0;
183 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
185 feature_t*f = gfxglobals->featurewarnings;
187 if(!strcmp(feature, f->string))
191 f = (feature_t*)malloc(sizeof(feature_t));
192 f->string = strdup(feature);
193 f->next = gfxglobals->featurewarnings;
194 gfxglobals->featurewarnings = f;
196 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
197 if(this->config_break_on_warning) {
198 msg("<fatal> Aborting conversion due to unsupported feature");
202 msg("<notice> File contains %s",feature);
205 void GFXOutputDev::warnfeature(const char*feature,char fully)
207 showfeature(feature,fully,1);
209 void GFXOutputDev::infofeature(const char*feature)
211 showfeature(feature,0,0);
214 GFXOutputState::GFXOutputState() {
216 this->createsoftmask = 0;
217 this->transparencygroup = 0;
219 this->grouprecording = 0;
223 GBool GFXOutputDev::interpretType3Chars()
228 typedef struct _drawnchar
246 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
247 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
252 free(chars);chars = 0;
259 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
263 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
266 chars[num_chars].x = x;
267 chars[num_chars].y = y;
268 chars[num_chars].color = color;
269 chars[num_chars].charid = charid;
273 char* writeOutStdFont(fontentry* f)
278 char* tmpFileName = mktmpname(namebuf1);
280 sprintf(namebuf2, "%s.afm", tmpFileName);
281 fi = fopen(namebuf2, "wb");
284 fwrite(f->afm, 1, f->afmlen, fi);
287 sprintf(namebuf2, "%s.pfb", tmpFileName);
288 fi = fopen(namebuf2, "wb");
291 fwrite(f->pfb, 1, f->pfblen, fi);
293 return strdup(namebuf2);
295 void unlinkfont(char* filename)
300 msg("<verbose> Removing temporary font file %s", filename);
303 if(!strncmp(&filename[l-4],".afm",4)) {
304 memcpy(&filename[l-4],".pfb",4); unlink(filename);
305 memcpy(&filename[l-4],".pfa",4); unlink(filename);
306 memcpy(&filename[l-4],".afm",4);
309 if(!strncmp(&filename[l-4],".pfa",4)) {
310 memcpy(&filename[l-4],".afm",4); unlink(filename);
311 memcpy(&filename[l-4],".pfa",4);
314 if(!strncmp(&filename[l-4],".pfb",4)) {
315 memcpy(&filename[l-4],".afm",4); unlink(filename);
316 memcpy(&filename[l-4],".pfb",4);
321 static int config_use_fontconfig = 1;
322 static int fcinitcalled = 0;
324 GFXGlobalParams::GFXGlobalParams()
325 : GlobalParams((char*)"")
327 //setupBaseFonts(char *dir); //not tested yet
329 GFXGlobalParams::~GFXGlobalParams()
331 msg("<verbose> Performing cleanups");
333 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
334 if(pdf2t1map[t].fullfilename) {
335 unlinkfont(pdf2t1map[t].fullfilename);
338 #ifdef HAVE_FONTCONFIG
339 if(config_use_fontconfig && fcinitcalled)
343 #ifdef HAVE_FONTCONFIG
344 static char stralphacmp(const char*s1, const char*s2)
347 /* skip over space, minus, comma etc. */
348 while(*s1>=32 && *s1<=63) s1++;
349 while(*s2>=32 && *s2<=63) s2++;
357 static char fc_ismatch(FcPattern*match, char*family, char*style)
359 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
360 FcBool scalable=FcFalse, outline=FcFalse;
361 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
362 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
363 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
364 FcPatternGetBool(match, "outline", 0, &outline);
365 FcPatternGetBool(match, "scalable", 0, &scalable);
367 if(scalable!=FcTrue || outline!=FcTrue)
370 if (!stralphacmp(fcfamily, family)) {
371 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
374 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
380 static inline char islowercase(char c)
382 return (c>='a' && c<='z');
385 char* fontconfig_searchForFont(char*name)
387 #ifdef HAVE_FONTCONFIG
388 if(!config_use_fontconfig)
391 // call init ony once
395 // check whether we have a config file
396 char* configfile = (char*)FcConfigFilename(0);
397 int configexists = 0;
398 FILE*fi = fopen(configfile, "rb");
400 configexists = 1;fclose(fi);
401 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
403 msg("<debug> Initializing FontConfig (no configfile)");
407 /* A fontconfig instance which didn't find a configfile is unbelievably
408 cranky, so let's just write out a small xml file and make fontconfig
410 FcConfig*c = FcConfigCreate();
412 char* tmpFileName = mktmpname(namebuf);
413 FILE*fi = fopen(tmpFileName, "wb");
414 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
416 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
418 fprintf(fi, "<dir>~/.fonts</dir>\n");
420 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
422 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
423 fprintf(fi, "</fontconfig>\n");
425 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
426 FcConfigBuildFonts(c);
427 FcConfigSetCurrent(c);
431 msg("<debug> FontConfig Initialization failed. Disabling.");
432 config_use_fontconfig = 0;
435 FcConfig * config = FcConfigGetCurrent();
437 msg("<debug> FontConfig Config Initialization failed. Disabling.");
438 config_use_fontconfig = 0;
442 /* add external fonts to fontconfig's config, too. */
443 fontfile_t*fd = global_fonts;
445 FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
446 msg("<debug> Adding font %s to fontconfig", fd->filename);
450 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
451 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
452 if(!set || !set->nfont) {
453 msg("<debug> FontConfig has zero fonts. Disabling.");
454 config_use_fontconfig = 0;
458 if(getLogLevel() >= LOGLEVEL_TRACE) {
463 for(t=0;t<set->nfont;t++) {
464 char*fcfamily=0,*fcstyle=0,*filename=0;
465 FcBool scalable=FcFalse, outline=FcFalse;
466 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
467 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
468 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
469 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
470 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
471 if(scalable && outline) {
472 msg("<trace> %s (%s) -> %s", fcfamily, fcstyle, filename);
476 set = FcConfigGetFonts(config, FcSetApplication);
481 char*family = strdup(name);
482 int len = strlen(family);
484 char*styles[] = {"Medium", "Regular", "Bold", "Italic", "Black", "Narrow"};
487 for(t=0;t<sizeof(styles)/sizeof(styles[0]);t++) {
488 int l = strlen(styles[t]);
489 if(len>l+1 && !strcmp(family+len-l, styles[t]) && islowercase(family[len-l-1])) {
496 char*dash = strchr(family, '-');
497 if(!dash) dash = strchr(family, ',');
503 FcPattern*pattern = 0;
505 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
506 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
508 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
509 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
511 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
514 FcConfigSubstitute(0, pattern, FcMatchPattern);
515 FcDefaultSubstitute(pattern);
517 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
520 for(t=0;t<set->nfont;t++) {
521 FcPattern*match = set->fonts[t];
522 //FcPattern*match = FcFontMatch(0, pattern, &result);
523 if(fc_ismatch(match, family, style)) {
525 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
526 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
529 //FcPatternDestroy(match);
530 msg("<debug> fontconfig: returning filename %s", filename);
532 FcPatternDestroy(pattern);
533 FcFontSetDestroy(set);
534 return filename?strdup(filename):0;
539 FcPatternDestroy(pattern);
540 FcFontSetDestroy(set);
547 static DisplayFontParamKind detectFontType(const char*filename)
549 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
550 return displayFontTT;
551 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
552 return displayFontT1;
553 return displayFontTT;
556 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
558 msg("<verbose> looking for font %s", fontName->getCString());
560 char*name = fontName->getCString();
562 /* see if it is a pdf standard font */
564 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
565 if(!strcmp(name, pdf2t1map[t].pdffont)) {
566 if(!pdf2t1map[t].fullfilename) {
567 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
568 if(!pdf2t1map[t].fullfilename) {
569 msg("<error> Couldn't save default font- is the Temp Directory writable?");
571 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
573 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
574 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
575 pdf2t1map[t].dfp = dfp;
577 return pdf2t1map[t].dfp;
581 int bestlen = 0x7fffffff;
582 const char*bestfilename = 0;
584 #ifndef HAVE_FONTCONFIG
585 /* if we don't have fontconfig, try a simple filename-comparison approach */
586 fontfile_t*f = global_fonts;
588 if(strstr(f->filename, name)) {
589 if(f->len < bestlen) {
591 bestfilename = f->filename;
598 /* if we didn't find anything up to now, try looking for the
599 font via fontconfig */
602 filename = fontconfig_searchForFont(name);
604 filename = strdup(bestfilename);
608 msg("<verbose> Font %s maps to %s\n", name, filename);
609 DisplayFontParamKind kind = detectFontType(filename);
610 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
611 if(kind == displayFontTT) {
612 dfp->tt.fileName = new GString(filename);
614 dfp->t1.fileName = new GString(filename);
619 msg("<verbose> Font %s not found\n", name);
620 return GlobalParams::getDisplayFont(fontName);
624 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
627 gfxglobals = new GFXOutputGlobals();
631 this->xref = doc->getXRef();
633 this->type3active = 0;
636 this->user_movex = 0;
637 this->user_movey = 0;
640 this->user_clipx1 = 0;
641 this->user_clipy1 = 0;
642 this->user_clipx2 = 0;
643 this->user_clipy2 = 0;
644 this->current_gfxfont = 0;
645 this->current_fontinfo = 0;
646 this->current_text_stroke = 0;
647 this->current_text_clip = 0;
648 this->outer_clip_box = 0;
649 this->config_bigchar=0;
650 this->config_convertgradients=1;
651 this->config_break_on_warning=0;
652 this->config_remapunicode=0;
653 this->config_transparent=0;
654 this->config_extrafontdata = 0;
655 this->config_drawonlyshapes = 0;
656 this->config_disable_polygon_conversion = 0;
657 this->config_multiply = 1;
658 this->config_detectspaces = 1;
659 this->config_linkdatafile = 0;
663 memset(states, 0, sizeof(states));
666 void GFXOutputDev::setParameter(const char*key, const char*value)
668 if(!strcmp(key,"breakonwarning")) {
669 this->config_break_on_warning = atoi(value);
670 } else if(!strcmp(key,"remapunicode")) {
671 this->config_remapunicode = atoi(value);
672 } else if(!strcmp(key,"transparent")) {
673 this->config_transparent = atoi(value);
674 } else if(!strcmp(key,"drawonlyshapes")) {
675 this->config_drawonlyshapes = atoi(value);
676 } else if(!strcmp(key,"detectspaces")) {
677 this->config_detectspaces = atoi(value);
678 } else if(!strcmp(key,"extrafontdata")) {
679 this->config_extrafontdata = atoi(value);
680 } else if(!strcmp(key,"linkdatafile")) {
681 this->config_linkdatafile = strdup(value);
682 } else if(!strcmp(key,"convertgradients")) {
683 this->config_convertgradients = atoi(value);
684 } else if(!strcmp(key,"textonly")) {
685 this->config_textonly = atoi(value);
686 } else if(!strcmp(key,"multiply")) {
687 this->config_multiply = atoi(value);
688 if(this->config_multiply<1)
689 this->config_multiply=1;
690 } else if(!strcmp(key,"disable_polygon_conversion")) {
691 this->config_disable_polygon_conversion = atoi(value);
695 void GFXOutputDev::setDevice(gfxdevice_t*dev)
700 void GFXOutputDev::setMove(int x,int y)
702 this->user_movex = x;
703 this->user_movey = y;
706 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
708 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
709 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
711 this->user_clipx1 = x1;
712 this->user_clipy1 = y1;
713 this->user_clipx2 = x2;
714 this->user_clipy2 = y2;
717 static char*getFontName(GfxFont*font)
720 GString*gstr = font->getName();
721 char* fname = gstr==0?0:gstr->getCString();
725 sprintf(buf, "UFONT%d", r->num);
726 fontid = strdup(buf);
728 fontid = strdup(fname);
732 char* plus = strchr(fontid, '+');
733 if(plus && plus < &fontid[strlen(fontid)-1]) {
734 fontname = strdup(plus+1);
736 fontname = strdup(fontid);
742 static void dumpFontInfo(const char*loglevel, GfxFont*font);
743 static int lastdumps[1024];
744 static int lastdumppos = 0;
749 static void showFontError(GfxFont*font, int nr)
753 for(t=0;t<lastdumppos;t++)
754 if(lastdumps[t] == r->num)
758 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
759 lastdumps[lastdumppos++] = r->num;
761 msg("<warning> The following font caused problems:");
763 msg("<warning> The following font caused problems (substituting):");
765 msg("<warning> The following Type 3 Font will be rendered as graphics:");
766 dumpFontInfo("<warning>", font);
769 static void dumpFontInfo(const char*loglevel, GfxFont*font)
771 char* id = getFontID(font);
772 char* name = getFontName(font);
773 Ref* r=font->getID();
774 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
776 GString*gstr = font->getTag();
778 msg("%s| Tag: %s", loglevel, id);
780 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
782 GfxFontType type=font->getType();
784 case fontUnknownType:
785 msg("%s| Type: unknown",loglevel);
788 msg("%s| Type: 1",loglevel);
791 msg("%s| Type: 1C",loglevel);
794 msg("%s| Type: 3",loglevel);
797 msg("%s| Type: TrueType",loglevel);
800 msg("%s| Type: CIDType0",loglevel);
803 msg("%s| Type: CIDType0C",loglevel);
806 msg("%s| Type: CIDType2",loglevel);
811 GBool embedded = font->getEmbeddedFontID(&embRef);
813 if(font->getEmbeddedFontName()) {
814 embeddedName = font->getEmbeddedFontName()->getCString();
817 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
819 gstr = font->getExtFontFile();
821 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
823 // Get font descriptor flags.
824 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
825 if(font->isSerif()) msg("%s| is serif", loglevel);
826 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
827 if(font->isItalic()) msg("%s| is italic", loglevel);
828 if(font->isBold()) msg("%s| is bold", loglevel);
834 //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");}
835 //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");}
837 void dump_outline(gfxline_t*line)
839 /*gfxbbox_t*r = gfxline_isrectangle(line);
841 printf("is not a rectangle\n");
843 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
847 if(line->type == gfx_moveTo) {
848 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
849 } else if(line->type == gfx_lineTo) {
850 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
851 } else if(line->type == gfx_splineTo) {
852 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
858 void gfxPath_dump(GfxPath*path)
860 int num = path->getNumSubpaths();
863 for(t = 0; t < num; t++) {
864 GfxSubpath *subpath = path->getSubpath(t);
865 int subnum = subpath->getNumPoints();
867 for(s=0;s<subnum;s++) {
868 double x=subpath->getX(s);
869 double y=subpath->getY(s);
870 if(s==0 && !subpath->getCurve(s)) {
871 printf("M %f %f\n", x, y);
872 } else if(s==0 && subpath->getCurve(s)) {
873 printf("E %f %f\n", x, y);
874 } else if(subpath->getCurve(s)) {
875 printf("C %f %f\n", x, y);
877 printf("T %f %f\n", x, y);
883 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
885 int num = path->getNumSubpaths();
888 double lastx=0,lasty=0,posx=0,posy=0;
891 msg("<warning> empty path");
895 gfxdrawer_target_gfxline(&draw);
897 for(t = 0; t < num; t++) {
898 GfxSubpath *subpath = path->getSubpath(t);
899 int subnum = subpath->getNumPoints();
900 double bx=0,by=0,cx=0,cy=0;
902 for(s=0;s<subnum;s++) {
905 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
908 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
909 draw.lineTo(&draw, lastx, lasty);
911 draw.moveTo(&draw, x,y);
916 } else if(subpath->getCurve(s) && cpos==0) {
920 } else if(subpath->getCurve(s) && cpos==1) {
928 draw.lineTo(&draw, x,y);
930 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
937 /* fix non-closed lines */
938 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
939 draw.lineTo(&draw, lastx, lasty);
941 gfxline_t*result = (gfxline_t*)draw.result(&draw);
943 gfxline_optimize(result);
948 GBool GFXOutputDev::useTilingPatternFill()
950 infofeature("tiled patterns");
951 // if(config_convertgradients)
955 GBool GFXOutputDev::useShadedFills()
957 infofeature("shaded fills");
958 if(config_convertgradients)
963 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
965 state->transform(x,y,nx,ny);
966 *nx += user_movex + clipmovex;
967 *ny += user_movey + clipmovey;
970 POPPLER_TILING_PATERN_RETURN GFXOutputDev::tilingPatternFill(GfxState *state,
971 POPPLER_TILING_PATERN_GFX
973 int paintType, Dict *resDict,
974 double *mat, double *bbox,
975 int x0, int y0, int x1, int y1,
976 double xStep, double yStep)
978 msg("<debug> tilingPatternFill");
979 infofeature("tiling pattern fills");
981 // since we don't implement this method yet,
982 // reduce it to a series of other drawing operations.
987 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
989 msg("<error> functionShadedFill not supported yet");
990 infofeature("function shaded fills");
993 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
997 colspace->getRGB(col, &rgb);
998 c.r = colToByte(rgb.r);
999 c.g = colToByte(rgb.g);
1000 c.b = colToByte(rgb.b);
1005 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
1007 if(config_textonly) {return gTrue;}
1009 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
1010 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
1013 this->transformXY(state, x0,y0, &x0,&y0);
1014 this->transformXY(state, x1,y1, &x1,&y1);
1015 this->transformXY(state, x2,y2, &x2,&y2);
1020 shading->getColor(0.0, &color0);
1021 shading->getColor(0.5, &color1);
1022 shading->getColor(1.0, &color2);
1024 GfxColorSpace* colspace = shading->getColorSpace();
1026 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
1027 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1028 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1029 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
1030 infofeature("radial shaded fills");
1032 gfxgradient_t gr[3];
1033 gfxgradient_t*g = &gr[0];
1037 g[0].color = col2col(colspace, &color0);
1038 g[1].color = col2col(colspace, &color1);
1039 g[2].color = col2col(colspace, &color2);
1044 gfxbbox_t b = states[statepos].clipbbox;
1045 gfxline_t p1,p2,p3,p4,p5;
1046 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1047 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1048 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1049 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1050 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1053 //m.m00 = (x3-x0); m.m10 = (x1-x0);
1054 //m.m01 = (y3-y0); m.m11 = (y1-y0);
1055 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1056 m.m00 = (x1-x0); m.m10 = (x2-x0);
1057 m.m01 = (y1-y0); m.m11 = (y2-y0);
1061 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1065 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1067 if(config_textonly) {return gTrue;}
1070 shading->getCoords(&x0,&y0,&x1,&y1);
1071 this->transformXY(state, x0,y0,&x0,&y0);
1072 this->transformXY(state, x1,y1,&x1,&y1);
1077 shading->getColor(0.0, &color0);
1078 shading->getColor(0.5, &color1);
1079 shading->getColor(1.0, &color2);
1081 GfxColorSpace* colspace = shading->getColorSpace();
1083 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1084 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1085 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1086 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1088 infofeature("axial shaded fills");
1090 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1094 g[0].color = col2col(colspace, &color0);
1095 g[1].color = col2col(colspace, &color1);
1096 g[2].color = col2col(colspace, &color2);
1101 double xMin,yMin,xMax,yMax;
1102 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1103 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1104 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1107 xMin = 1024; yMin = 1024;
1109 gfxbbox_t b = states[statepos].clipbbox;
1110 gfxline_t p1,p2,p3,p4,p5;
1111 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1112 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1113 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1114 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1115 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1117 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1118 the middle of the two control points */
1120 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1121 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1122 m.tx = (x0 + x1)/2 - 0.5;
1123 m.ty = (y0 + y1)/2 - 0.5;
1125 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1131 GBool GFXOutputDev::useDrawForm()
1133 infofeature("forms");
1136 void GFXOutputDev::drawForm(Ref id)
1138 msg("<error> drawForm not implemented");
1140 GBool GFXOutputDev::needNonText()
1144 void GFXOutputDev::endPage()
1146 msg("<verbose> endPage (GfxOutputDev)");
1147 if(outer_clip_box) {
1148 device->endclip(device);
1151 /* notice: we're not fully done yet with this page- there might still be
1152 a few calls to drawLink() yet to come */
1155 static inline double sqr(double x) {return x*x;}
1157 #define STROKE_FILL 1
1158 #define STROKE_CLIP 2
1159 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1161 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1162 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1163 double miterLimit = state->getMiterLimit();
1164 double width = state->getTransformedLineWidth();
1167 double opaq = state->getStrokeOpacity();
1169 state->getFillRGB(&rgb);
1171 state->getStrokeRGB(&rgb);
1173 col.r = colToByte(rgb.r);
1174 col.g = colToByte(rgb.g);
1175 col.b = colToByte(rgb.b);
1176 col.a = (unsigned char)(opaq*255);
1178 gfx_capType capType = gfx_capRound;
1179 if(lineCap == 0) capType = gfx_capButt;
1180 else if(lineCap == 1) capType = gfx_capRound;
1181 else if(lineCap == 2) capType = gfx_capSquare;
1182 else msg("<error> Invalid line cap type");
1184 gfx_joinType joinType = gfx_joinRound;
1185 if(lineJoin == 0) joinType = gfx_joinMiter;
1186 else if(lineJoin == 1) joinType = gfx_joinRound;
1187 else if(lineJoin == 2) joinType = gfx_joinBevel;
1188 else msg("<error> Invalid line join type");
1190 gfxline_t*line2 = 0;
1192 int dashLength = states[statepos].dashLength;
1193 double*dashPattern = states[statepos].dashPattern;
1194 double dashStart = states[statepos].dashStart;
1195 if(dashLength && dashPattern) {
1196 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1199 /* try to find out how much the transformation matrix would
1200 stretch the dashes, and factor that into the dash lengths.
1201 This is not the entirely correct approach- it would be
1202 better to first convert the path to an unscaled version,
1203 then apply dashing, and then transform the path using
1204 the current transformation matrix. However there are few
1205 PDFs which actually stretch a dashed path in a non-orthonormal
1207 double tx1, ty1, tx2, ty2, tx3, ty3;
1208 this->transformXY(state, 0, 0, &tx1, &ty1);
1209 this->transformXY(state, 0, 1, &tx2, &ty2);
1210 this->transformXY(state, 1, 0, &tx3, &ty3);
1211 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1212 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1214 warnfeature("non-ortogonally dashed strokes", 0);
1215 double f = (d1+d2)/2;
1217 msg("<trace> %d dashes", dashLength);
1218 msg("<trace> | phase: %f", dashStart);
1219 for(t=0;t<dashLength;t++) {
1220 dash[t] = (float)dashPattern[t] * f;
1224 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1226 dash[dashLength] = -1;
1227 if(getLogLevel() >= LOGLEVEL_TRACE) {
1231 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1235 msg("<trace> After dashing:");
1238 if(getLogLevel() >= LOGLEVEL_TRACE) {
1239 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1241 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1242 lineCap==0?"butt": (lineCap==1?"round":"square"),
1244 col.r,col.g,col.b,col.a
1249 if(flags&STROKE_FILL) {
1250 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1251 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1252 if(getLogLevel() >= LOGLEVEL_TRACE) {
1253 dump_outline(gfxline);
1256 msg("<warning> Empty polygon (resulting from stroked line)");
1258 if(flags&STROKE_CLIP) {
1259 device->startclip(device, gfxline);
1260 states[statepos].clipping++;
1262 device->fill(device, gfxline, &col);
1264 gfxline_free(gfxline);
1265 gfxpoly_destroy(poly);
1267 if(flags&STROKE_CLIP)
1268 msg("<error> Stroke&clip not supported at the same time");
1269 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1273 gfxline_free(line2);
1276 gfxcolor_t getFillColor(GfxState * state)
1279 double opaq = state->getFillOpacity();
1280 state->getFillRGB(&rgb);
1282 col.r = colToByte(rgb.r);
1283 col.g = colToByte(rgb.g);
1284 col.b = colToByte(rgb.b);
1285 col.a = (unsigned char)(opaq*255);
1289 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1291 gfxcolor_t col = getFillColor(state);
1293 if(getLogLevel() >= LOGLEVEL_TRACE) {
1294 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1297 device->fill(device, line, &col);
1300 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1302 if(getLogLevel() >= LOGLEVEL_TRACE) {
1303 msg("<trace> %sclip", evenodd?"eo":"");
1306 gfxbbox_t bbox = gfxline_getbbox(line);
1307 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1309 device->startclip(device, line);
1310 states[statepos].clipping++;
1313 void GFXOutputDev::clip(GfxState *state)
1315 GfxPath * path = state->getPath();
1316 msg("<trace> clip");
1317 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1318 if(!config_disable_polygon_conversion) {
1319 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1323 clipToGfxLine(state, line, 0);
1327 void GFXOutputDev::eoClip(GfxState *state)
1329 GfxPath * path = state->getPath();
1330 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1331 clipToGfxLine(state, line, 1);
1334 void GFXOutputDev::clipToStrokePath(GfxState *state)
1336 GfxPath * path = state->getPath();
1337 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1339 if(getLogLevel() >= LOGLEVEL_TRACE) {
1340 double width = state->getTransformedLineWidth();
1341 msg("<trace> cliptostrokepath width=%f", width);
1345 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1349 void GFXOutputDev::finish()
1351 if(outer_clip_box) {
1353 device->endclip(device);
1359 GFXOutputDev::~GFXOutputDev()
1363 GBool GFXOutputDev::upsideDown()
1367 GBool GFXOutputDev::useDrawChar()
1372 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1373 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1375 static char tmp_printstr[4096];
1376 char* makeStringPrintable(char*str)
1378 int len = strlen(str);
1385 for(t=0;t<len;t++) {
1390 tmp_printstr[t] = c;
1393 tmp_printstr[len++] = '.';
1394 tmp_printstr[len++] = '.';
1395 tmp_printstr[len++] = '.';
1397 tmp_printstr[len] = 0;
1398 return tmp_printstr;
1400 void GFXOutputDev::updateFontMatrix(GfxState*state)
1402 double* ctm = state->getCTM();
1403 double fontSize = state->getFontSize();
1404 double*textMat = state->getTextMat();
1406 /* taking the absolute value of horizScaling seems to be required for
1407 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1408 double hscale = fabs(state->getHorizScaling());
1410 // from xpdf-3.02/SplashOutputDev:updateFont
1411 double mm11 = textMat[0] * fontSize * hscale;
1412 double mm12 = textMat[1] * fontSize * hscale;
1413 double mm21 = textMat[2] * fontSize;
1414 double mm22 = textMat[3] * fontSize;
1416 // multiply with ctm, like state->getFontTransMat() does
1417 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1418 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1419 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1420 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1421 this->current_font_matrix.tx = 0;
1422 this->current_font_matrix.ty = 0;
1425 void GFXOutputDev::beginString(GfxState *state, GString *s)
1427 int render = state->getRender();
1428 if(current_text_stroke) {
1429 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1431 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1434 static gfxline_t* mkEmptyGfxShape(double x, double y)
1436 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1437 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1441 static char isValidUnicode(int c)
1443 if(c>=32 && c<0x2fffe)
1448 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1449 double dx, double dy,
1450 double originX, double originY,
1451 CharCode charid, int nBytes, Unicode *_u, int uLen)
1453 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1454 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1458 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1460 int render = state->getRender();
1461 gfxcolor_t col = getFillColor(state);
1463 // check for invisible text -- this is used by Acrobat Capture
1464 if (render == RENDER_INVISIBLE) {
1466 if(!config_extrafontdata)
1470 GfxFont*font = state->getFont();
1472 if(font->getType() == fontType3) {
1473 /* type 3 chars are passed as graphics */
1474 msg("<debug> type3 char at %f/%f", x, y);
1478 Unicode u = uLen?(_u[0]):0;
1480 gfxmatrix_t m = this->current_font_matrix;
1481 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1482 //m.tx += originX; m.ty += originY;
1484 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);
1486 if((render == RENDER_FILL && !config_drawonlyshapes) ||
1487 (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
1488 (render == RENDER_INVISIBLE)) {
1490 int space = this->current_fontinfo->space_char;
1491 if(config_extrafontdata && config_detectspaces && space>=0 && m.m00 && !m.m01) {
1492 /* space char detection */
1493 if(last_char_gfxfont == current_gfxfont &&
1494 last_char_y == m.ty &&
1495 !last_char_was_space) {
1496 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1497 int space = this->current_fontinfo->space_char;
1498 float width = this->current_fontinfo->average_advance;
1499 if(m.tx - expected_x >= m.m00*width*4/10) {
1500 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",
1503 last_char, glyphid);
1505 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1506 if(m2.tx < expected_x) m2.tx = expected_x;
1507 device->drawchar(device, current_gfxfont, space, &col, &m2);
1510 last_char_gfxfont = current_gfxfont;
1511 last_char = glyphid;
1514 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1516 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1518 msg("<debug> Drawing glyph %d as shape", charid);
1519 if(!gfxglobals->textmodeinfo) {
1520 msg("<notice> Some texts will be rendered as shape");
1521 gfxglobals->textmodeinfo = 1;
1524 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1525 gfxline_t*tglyph = gfxline_clone(glyph);
1526 gfxline_transform(tglyph, &m);
1527 if((render&3) != RENDER_INVISIBLE) {
1528 gfxline_t*add = gfxline_clone(tglyph);
1529 current_text_stroke = gfxline_append(current_text_stroke, add);
1531 if(render&RENDER_CLIP) {
1532 gfxline_t*add = gfxline_clone(tglyph);
1533 current_text_clip = gfxline_append(current_text_clip, add);
1534 if(!current_text_clip) {
1535 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1538 gfxline_free(tglyph);
1542 void GFXOutputDev::endString(GfxState *state)
1544 int render = state->getRender();
1545 msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
1547 if(current_text_stroke) {
1548 /* fillstroke and stroke text rendering objects we can process right
1549 now (as there may be texts of other rendering modes in this
1550 text object)- clipping objects have to wait until endTextObject,
1552 device->setparameter(device, "mark","TXT");
1553 if((render&3) == RENDER_FILL) {
1554 fillGfxLine(state, current_text_stroke, 0);
1555 gfxline_free(current_text_stroke);
1556 current_text_stroke = 0;
1557 } else if((render&3) == RENDER_FILLSTROKE) {
1558 fillGfxLine(state, current_text_stroke, 0);
1559 strokeGfxline(state, current_text_stroke,0);
1560 gfxline_free(current_text_stroke);
1561 current_text_stroke = 0;
1562 } else if((render&3) == RENDER_STROKE) {
1563 strokeGfxline(state, current_text_stroke,0);
1564 gfxline_free(current_text_stroke);
1565 current_text_stroke = 0;
1567 device->setparameter(device, "mark","");
1571 void GFXOutputDev::endTextObject(GfxState *state)
1573 int render = state->getRender();
1574 msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
1576 if(current_text_clip) {
1577 device->setparameter(device, "mark","TXT");
1578 clipToGfxLine(state, current_text_clip, 0);
1579 device->setparameter(device, "mark","");
1580 gfxline_free(current_text_clip);
1581 current_text_clip = 0;
1585 /* the logic seems to be as following:
1586 first, beginType3Char is called, with the charcode and the coordinates.
1587 if this function returns true, it already knew about the char and has now drawn it.
1588 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1589 called with some parameters.
1590 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1591 at the position first passed to beginType3Char). the char ends with endType3Char.
1593 The drawing operations between beginType3Char and endType3Char are somewhat different to
1594 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1595 color determines the color of a font)
1598 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1600 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1603 if(config_extrafontdata && current_fontinfo) {
1605 gfxmatrix_t m = this->current_font_matrix;
1606 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1608 /*m.m00*=INTERNAL_FONT_SIZE;
1609 m.m01*=INTERNAL_FONT_SIZE;
1610 m.m10*=INTERNAL_FONT_SIZE;
1611 m.m11*=INTERNAL_FONT_SIZE;*/
1613 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1614 msg("<error> Invalid charid %d for font", charid);
1617 gfxcolor_t col={0,0,0,0};
1618 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1619 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1623 /* the character itself is going to be passed using the draw functions */
1624 return gFalse; /* gTrue= is_in_cache? */
1627 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1629 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1632 void GFXOutputDev::endType3Char(GfxState *state)
1635 msg("<debug> endType3Char");
1638 void GFXOutputDev::startPage(int pageNum, GfxState *state)
1640 this->currentpage = pageNum;
1642 int rot = doc->getPageRotate(1);
1643 gfxcolor_t white = {255,255,255,255};
1644 gfxcolor_t black = {255,0,0,0};
1646 gfxline_t clippath[5];
1647 PDFRectangle *r = this->page->getCropBox();
1649 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1650 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1651 Use CropBox, not MediaBox, as page size
1658 state->transform(r->x1,r->y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1659 state->transform(r->x2,r->y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1661 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1662 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1664 this->clipmovex = -(int)x1;
1665 this->clipmovey = -(int)y1;
1667 /* apply user clip box */
1668 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1669 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1670 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1671 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1672 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1673 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1675 x1 += this->clipmovex;
1676 y1 += this->clipmovey;
1677 x2 += this->clipmovex;
1678 y2 += this->clipmovey;
1681 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1683 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);
1685 msg("<verbose> page is rotated %d degrees", rot);
1687 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1688 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1689 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1690 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1691 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1692 device->startclip(device, clippath); outer_clip_box = 1;
1693 if(!config_transparent) {
1694 device->fill(device, clippath, &white);
1696 states[statepos].clipbbox.xmin = x1;
1697 states[statepos].clipbbox.ymin = x1;
1698 states[statepos].clipbbox.xmax = x2;
1699 states[statepos].clipbbox.ymax = y2;
1701 states[statepos].dashPattern = 0;
1702 states[statepos].dashLength = 0;
1703 states[statepos].dashStart = 0;
1705 this->last_char_gfxfont = 0;
1709 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1711 double x1, y1, x2, y2;
1712 gfxline_t points[5];
1715 msg("<debug> drawlink");
1717 link->getRect(&x1, &y1, &x2, &y2);
1718 cvtUserToDev(x1, y1, &x, &y);
1719 points[0].type = gfx_moveTo;
1720 points[0].x = points[4].x = x + user_movex + clipmovex;
1721 points[0].y = points[4].y = y + user_movey + clipmovey;
1722 points[0].next = &points[1];
1723 cvtUserToDev(x2, y1, &x, &y);
1724 points[1].type = gfx_lineTo;
1725 points[1].x = x + user_movex + clipmovex;
1726 points[1].y = y + user_movey + clipmovey;
1727 points[1].next = &points[2];
1728 cvtUserToDev(x2, y2, &x, &y);
1729 points[2].type = gfx_lineTo;
1730 points[2].x = x + user_movex + clipmovex;
1731 points[2].y = y + user_movey + clipmovey;
1732 points[2].next = &points[3];
1733 cvtUserToDev(x1, y2, &x, &y);
1734 points[3].type = gfx_lineTo;
1735 points[3].x = x + user_movex + clipmovex;
1736 points[3].y = y + user_movey + clipmovey;
1737 points[3].next = &points[4];
1738 cvtUserToDev(x1, y1, &x, &y);
1739 points[4].type = gfx_lineTo;
1740 points[4].x = x + user_movex + clipmovex;
1741 points[4].y = y + user_movey + clipmovey;
1744 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1745 points[0].x, points[0].y,
1746 points[1].x, points[1].y,
1747 points[2].x, points[2].y,
1748 points[3].x, points[3].y);
1750 if(getLogLevel() >= LOGLEVEL_TRACE) {
1751 dump_outline(points);
1754 LinkAction*action=link->getAction();
1757 const char*type = "-?-";
1760 msg("<trace> drawlink action=%d", action->getKind());
1761 switch(action->getKind())
1765 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1766 LinkDest *dest=NULL;
1767 if (ha->getDest()==NULL)
1768 dest=catalog->findDest(ha->getNamedDest());
1770 dest=ha->getDest()->copy();
1772 if (dest->isPageRef()){
1773 Ref pageref=dest->getPageRef();
1774 page=catalog->findPage(pageref.num,pageref.gen);
1776 else page=dest->getPageNum();
1777 sprintf(buf, "%d", page);
1785 LinkGoToR*l = (LinkGoToR*)action;
1786 GString*g = l->getFileName();
1788 s = strdup(g->getCString());
1790 /* if the GoToR link has no filename, then
1791 try to find a refernce in the *local*
1793 GString*g = l->getNamedDest();
1795 s = strdup(g->getCString());
1801 LinkNamed*l = (LinkNamed*)action;
1802 GString*name = l->getName();
1804 s = strdup(name->lowerCase()->getCString());
1805 named = name->getCString();
1808 if(strstr(s, "next") || strstr(s, "forward"))
1810 page = currentpage + 1;
1812 else if(strstr(s, "prev") || strstr(s, "back"))
1814 page = currentpage - 1;
1816 else if(strstr(s, "last") || strstr(s, "end"))
1818 if(this->page2page && this->num_pages) {
1819 page = this->page2page[this->num_pages-1];
1822 else if(strstr(s, "first") || strstr(s, "top"))
1830 case actionLaunch: {
1832 LinkLaunch*l = (LinkLaunch*)action;
1833 GString * str = new GString(l->getFileName());
1834 GString * params = l->getParams();
1836 str->append(params);
1837 s = strdup(str->getCString());
1844 LinkURI*l = (LinkURI*)action;
1845 GString*g = l->getURI();
1847 url = g->getCString();
1852 case actionUnknown: {
1854 LinkUnknown*l = (LinkUnknown*)action;
1859 msg("<error> Unknown link type!");
1864 if(!s) s = strdup("-?-");
1866 msg("<trace> drawlink s=%s", s);
1868 if(!gfxglobals->linkinfo && (page || s))
1870 msg("<notice> File contains links");
1871 gfxglobals->linkinfo = 1;
1877 for(t=1;t<=this->num_pages;t++) {
1878 if(this->page2page[t]==page) {
1888 sprintf(buf, "page%d", lpage);
1889 device->drawlink(device, points, buf);
1893 device->drawlink(device, points, s);
1894 if(this->config_linkdatafile) {
1895 FILE*fi = fopen(config_linkdatafile, "ab+");
1896 fprintf(fi, "%s\n", s);
1901 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1905 void GFXOutputDev::saveState(GfxState *state) {
1906 dbg("saveState %p", state); dbgindent+=2;
1908 msg("<trace> saveState %p", state);
1911 msg("<fatal> Too many nested states in pdf.");
1915 states[statepos].state = state;
1916 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1917 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1918 states[statepos].clipping = 0;
1919 states[statepos].olddevice = 0;
1920 states[statepos].clipbbox = states[statepos-1].clipbbox;
1922 states[statepos].dashPattern = states[statepos-1].dashPattern;
1923 states[statepos].dashStart = states[statepos-1].dashStart;
1924 states[statepos].dashLength = states[statepos-1].dashLength;
1927 void GFXOutputDev::restoreState(GfxState *state) {
1928 dbgindent-=2; dbg("restoreState %p", state);
1931 msg("<fatal> Invalid restoreState");
1934 msg("<trace> restoreState %p%s%s", state,
1935 states[statepos].softmask?" (end softmask)":"",
1936 states[statepos].clipping?" (end clipping)":"");
1937 if(states[statepos].softmask) {
1938 clearSoftMask(state);
1941 if(states[statepos].dashPattern) {
1942 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1943 free(states[statepos].dashPattern);
1944 states[statepos].dashPattern = 0;
1950 while(states[statepos].clipping) {
1951 device->endclip(device);
1952 states[statepos].clipping--;
1954 if(states[statepos].state!=state) {
1955 msg("<fatal> bad state nesting");
1958 for(t=0;t<=statepos;t++) {
1959 printf("%p ", states[t].state);
1965 states[statepos].state=0;
1969 void GFXOutputDev::updateLineDash(GfxState *state)
1971 if(states[statepos].dashPattern &&
1972 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1973 free(states[statepos].dashPattern);
1974 states[statepos].dashPattern = 0;
1976 double *pattern = 0;
1979 state->getLineDash(&pattern, &dashLength, &dashStart);
1980 msg("<debug> updateLineDash, %d dashes", dashLength);
1982 states[statepos].dashPattern = 0;
1983 states[statepos].dashLength = 0;
1985 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1986 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1987 states[statepos].dashPattern = p;
1988 states[statepos].dashLength = dashLength;
1989 states[statepos].dashStart = dashStart;
1993 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1995 this->page2page = page2page;
1996 this->num_pages = num_pages;
1999 void GFXOutputDev::updateLineWidth(GfxState *state)
2001 double width = state->getTransformedLineWidth();
2004 void GFXOutputDev::updateLineCap(GfxState *state)
2006 int c = state->getLineCap();
2009 void GFXOutputDev::updateLineJoin(GfxState *state)
2011 int j = state->getLineJoin();
2014 void GFXOutputDev::updateFillColor(GfxState *state)
2017 double opaq = state->getFillOpacity();
2018 state->getFillRGB(&rgb);
2020 void GFXOutputDev::updateFillOpacity(GfxState *state)
2023 double opaq = state->getFillOpacity();
2024 state->getFillRGB(&rgb);
2025 dbg("update fillopaq %f", opaq);
2027 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
2029 double opaq = state->getFillOpacity();
2030 dbg("update strokeopaq %f", opaq);
2032 void GFXOutputDev::updateFillOverprint(GfxState *state)
2034 double opaq = state->getFillOverprint();
2035 dbg("update filloverprint %f", opaq);
2037 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2039 double opaq = state->getStrokeOverprint();
2040 dbg("update strokeoverprint %f", opaq);
2042 void GFXOutputDev::updateTransfer(GfxState *state)
2044 dbg("update transfer");
2048 void GFXOutputDev::updateStrokeColor(GfxState *state)
2051 double opaq = state->getStrokeOpacity();
2052 state->getStrokeRGB(&rgb);
2055 void GFXOutputDev::updateFont(GfxState *state)
2057 GfxFont* gfxFont = state->getFont();
2061 char*id = getFontID(gfxFont);
2062 msg("<verbose> Updating font to %s", id);
2063 if(gfxFont->getType() == fontType3) {
2064 infofeature("Type3 fonts");
2065 if(!config_extrafontdata) {
2070 msg("<error> Internal Error: FontID is null");
2074 this->current_fontinfo = this->info->getFont(id);
2076 if(!this->current_fontinfo) {
2077 msg("<error> Internal Error: no fontinfo for font %s", id);
2080 if(!this->current_fontinfo->seen) {
2081 dumpFontInfo("<verbose>", gfxFont);
2084 current_gfxfont = this->current_fontinfo->getGfxFont();
2085 device->addfont(device, current_gfxfont);
2088 updateFontMatrix(state);
2091 #define SQR(x) ((x)*(x))
2093 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2095 if((newwidth<1 || newheight<1) ||
2096 (width<=newwidth || height<=newheight))
2098 unsigned char*newdata;
2100 newdata= (unsigned char*)malloc(newwidth*newheight);
2101 double fx = ((double)width)/newwidth;
2102 double fy = ((double)height)/newheight;
2104 int blocksize = (int)(8192/(fx*fy));
2105 int r = 8192*256/palettesize;
2106 for(x=0;x<newwidth;x++) {
2107 double ex = px + fx;
2108 int fromx = (int)px;
2110 int xweight1 = (int)((1-(px-fromx))*256);
2111 int xweight2 = (int)((ex-tox)*256);
2113 for(y=0;y<newheight;y++) {
2114 double ey = py + fy;
2115 int fromy = (int)py;
2117 int yweight1 = (int)((1-(py-fromy))*256);
2118 int yweight2 = (int)((ey-toy)*256);
2125 for(xx=fromx;xx<=tox;xx++)
2126 for(yy=fromy;yy<=toy;yy++) {
2127 int b = 1-data[width*yy+xx];
2129 if(xx==fromx) weight = (weight*xweight1)/256;
2130 if(xx==tox) weight = (weight*xweight2)/256;
2131 if(yy==fromy) weight = (weight*yweight1)/256;
2132 if(yy==toy) weight = (weight*yweight2)/256;
2135 //if(a) a=(palettesize-1)*r/blocksize;
2136 newdata[y*newwidth+x] = (a*blocksize)/r;
2144 #define IMAGE_TYPE_JPEG 0
2145 #define IMAGE_TYPE_LOSSLESS 1
2147 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2148 double x1,double y1,
2149 double x2,double y2,
2150 double x3,double y3,
2151 double x4,double y4, int type, int multiply)
2153 gfxcolor_t*newpic=0;
2155 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2156 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2158 gfxline_t p1,p2,p3,p4,p5;
2159 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2160 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2161 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2162 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2163 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2165 {p1.x = (int)(p1.x*20)/20.0;
2166 p1.y = (int)(p1.y*20)/20.0;
2167 p2.x = (int)(p2.x*20)/20.0;
2168 p2.y = (int)(p2.y*20)/20.0;
2169 p3.x = (int)(p3.x*20)/20.0;
2170 p3.y = (int)(p3.y*20)/20.0;
2171 p4.x = (int)(p4.x*20)/20.0;
2172 p4.y = (int)(p4.y*20)/20.0;
2173 p5.x = (int)(p5.x*20)/20.0;
2174 p5.y = (int)(p5.y*20)/20.0;
2178 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2179 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2181 m.tx = p1.x - 0.5*multiply;
2182 m.ty = p1.y - 0.5*multiply;
2185 img.data = (gfxcolor_t*)data;
2189 if(type == IMAGE_TYPE_JPEG)
2190 /* TODO: pass image_dpi to device instead */
2191 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2194 dev->fillbitmap(dev, &p1, &img, &m, 0);
2197 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2198 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2200 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2203 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2204 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2206 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2210 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2211 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2212 GBool inlineImg, int mask, int*maskColors,
2213 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2215 /* the code in this function is *old*. It's not pretty, but it works. */
2217 double x1,y1,x2,y2,x3,y3,x4,y4;
2218 ImageStream *imgStr;
2223 unsigned char* maskbitmap = 0;
2226 ncomps = colorMap->getNumPixelComps();
2227 bits = colorMap->getBits();
2232 unsigned char buf[8];
2233 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2235 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2236 imgMaskStr->reset();
2237 unsigned char pal[256];
2238 int n = 1 << colorMap->getBits();
2243 maskColorMap->getGray(pixBuf, &gray);
2244 pal[t] = colToByte(gray);
2246 for (y = 0; y < maskHeight; y++) {
2247 for (x = 0; x < maskWidth; x++) {
2248 imgMaskStr->getPixel(buf);
2249 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2254 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2255 imgMaskStr->reset();
2256 for (y = 0; y < maskHeight; y++) {
2257 for (x = 0; x < maskWidth; x++) {
2258 imgMaskStr->getPixel(buf);
2260 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2268 imgStr = new ImageStream(str, width, ncomps,bits);
2271 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2273 msg("<verbose> Ignoring %d by %d image", width, height);
2274 unsigned char buf[8];
2276 for (y = 0; y < height; ++y)
2277 for (x = 0; x < width; ++x) {
2278 imgStr->getPixel(buf);
2286 this->transformXY(state, 0, 1, &x1, &y1);
2287 this->transformXY(state, 0, 0, &x2, &y2);
2288 this->transformXY(state, 1, 0, &x3, &y3);
2289 this->transformXY(state, 1, 1, &x4, &y4);
2292 /* as type 3 bitmaps are antialized, we need to place them
2293 at integer coordinates, otherwise flash player's antializing
2294 will kick in and make everything blurry */
2295 x1 = (int)(x1);y1 = (int)(y1);
2296 x2 = (int)(x2);y2 = (int)(y2);
2297 x3 = (int)(x3);y3 = (int)(y3);
2298 x4 = (int)(x4);y4 = (int)(y4);
2301 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2303 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2304 gfxglobals->pbminfo = 1;
2307 msg("<verbose> drawing %d by %d masked picture", width, height);
2309 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2310 msg("<notice> File contains jpeg pictures");
2311 gfxglobals->jpeginfo = 1;
2315 unsigned char buf[8];
2317 unsigned char*pic = new unsigned char[width*height];
2318 gfxcolor_t pal[256];
2320 state->getFillRGB(&rgb);
2322 memset(pal,255,sizeof(pal));
2323 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2324 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2325 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2326 pal[0].a = 255; pal[1].a = 0;
2329 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2330 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2331 for (y = 0; y < height; ++y)
2332 for (x = 0; x < width; ++x)
2334 imgStr->getPixel(buf);
2337 pic[width*y+x] = buf[0];
2341 unsigned char*pic2 = 0;
2344 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2353 height = realheight;
2357 /* make a black/white palette */
2359 float r = 255./(float)(numpalette-1);
2361 for(t=0;t<numpalette;t++) {
2362 pal[t].r = colToByte(rgb.r);
2363 pal[t].g = colToByte(rgb.g);
2364 pal[t].b = colToByte(rgb.b);
2365 pal[t].a = (unsigned char)(t*r);
2370 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2371 for (y = 0; y < height; ++y) {
2372 for (x = 0; x < width; ++x) {
2373 pic2[width*y+x] = pal[pic[y*width+x]];
2376 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2380 if(maskbitmap) free(maskbitmap);
2386 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2387 gfxcolor_t*pic=new gfxcolor_t[width*height];
2388 for (y = 0; y < height; ++y) {
2389 for (x = 0; x < width; ++x) {
2390 imgStr->getPixel(pixBuf);
2391 colorMap->getRGB(pixBuf, &rgb);
2392 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2393 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2394 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2395 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2397 int x1 = x*maskWidth/width;
2398 int y1 = y*maskHeight/height;
2399 int x2 = (x+1)*maskWidth/width;
2400 int y2 = (y+1)*maskHeight/height;
2402 unsigned int alpha=0;
2403 unsigned int count=0;
2404 for(xx=x1;xx<x2;xx++)
2405 for(yy=y1;yy<y2;yy++) {
2406 alpha += maskbitmap[yy*maskWidth+xx];
2410 pic[width*y+x].a = alpha / count;
2412 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2417 if(str->getKind()==strDCT)
2418 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2420 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2423 if(maskbitmap) free(maskbitmap);
2426 gfxcolor_t*pic=new gfxcolor_t[width*height];
2427 gfxcolor_t pal[256];
2428 int n = 1 << colorMap->getBits();
2430 for(t=0;t<256;t++) {
2432 colorMap->getRGB(pixBuf, &rgb);
2433 pal[t].r = (unsigned char)(colToByte(rgb.r));
2434 pal[t].g = (unsigned char)(colToByte(rgb.g));
2435 pal[t].b = (unsigned char)(colToByte(rgb.b));
2436 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2438 for (y = 0; y < height; ++y) {
2439 for (x = 0; x < width; ++x) {
2440 imgStr->getPixel(pixBuf);
2441 pic[width*y+x] = pal[pixBuf[0]];
2442 if(maskColors && *maskColors==pixBuf[0]) {
2443 pic[width*y+x].a = 0;
2448 if(maskWidth < width && maskHeight < height) {
2449 for(y = 0; y < height; y++) {
2450 for (x = 0; x < width; x++) {
2451 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2455 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2456 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2457 double dx = width / (double)maskWidth;
2458 double dy = height / (double)maskHeight;
2460 for(y = 0; y < maskHeight; y++) {
2462 for (x = 0; x < maskWidth; x++) {
2463 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2464 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2472 height = maskHeight;
2475 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2479 if(maskbitmap) free(maskbitmap);
2484 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2485 int width, int height, GBool invert,
2488 if(config_textonly) {
2489 OutputDev::drawImageMask(state,ref,str,width,height,invert,inlineImg);
2492 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2493 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2494 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2497 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2498 int width, int height, GfxImageColorMap *colorMap,
2499 int *maskColors, GBool inlineImg)
2501 if(config_textonly) {
2502 OutputDev::drawImage(state,ref,str,width,height,colorMap,maskColors,inlineImg);
2505 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2506 colorMap?"colorMap":"no colorMap",
2507 maskColors?"maskColors":"no maskColors",
2509 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2510 colorMap?"colorMap":"no colorMap",
2511 maskColors?"maskColors":"no maskColors",
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,inlineImg,0,maskColors, 0,0,0,0, 0);
2519 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2520 int width, int height,
2521 GfxImageColorMap *colorMap,
2522 Stream *maskStr, int maskWidth, int maskHeight,
2525 if(config_textonly) {
2526 OutputDev::drawMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskInvert);
2529 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2530 colorMap?"colorMap":"no colorMap",
2531 maskWidth, maskHeight);
2532 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2533 colorMap?"colorMap":"no colorMap",
2534 maskWidth, maskHeight);
2536 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2537 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2538 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2541 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2542 int width, int height,
2543 GfxImageColorMap *colorMap,
2545 int maskWidth, int maskHeight,
2546 GfxImageColorMap *maskColorMap)
2548 if(config_textonly) {
2549 OutputDev::drawSoftMaskedImage(state,ref,str,width,height,colorMap,maskStr,maskWidth,maskHeight,maskColorMap);
2552 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2553 colorMap?"colorMap":"no colorMap",
2554 maskWidth, maskHeight);
2555 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2556 colorMap?"colorMap":"no colorMap",
2557 maskWidth, maskHeight);
2559 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2560 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2564 void GFXOutputDev::stroke(GfxState *state)
2566 if(config_textonly) {return;}
2570 GfxPath * path = state->getPath();
2571 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2572 strokeGfxline(state, line, 0);
2576 void GFXOutputDev::fill(GfxState *state)
2578 if(config_textonly) {return;}
2580 gfxcolor_t col = getFillColor(state);
2581 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2583 GfxPath * path = state->getPath();
2584 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2585 if(!config_disable_polygon_conversion) {
2586 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2590 fillGfxLine(state, line, 0);
2594 void GFXOutputDev::eoFill(GfxState *state)
2596 if(config_textonly) {return;}
2598 gfxcolor_t col = getFillColor(state);
2599 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2601 GfxPath * path = state->getPath();
2602 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2603 fillGfxLine(state, line, 1);
2608 static const char* dirseparator()
2617 void addGlobalFont(const char*filename)
2619 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2620 memset(f, 0, sizeof(fontfile_t));
2621 f->filename = filename;
2622 int len = strlen(filename);
2623 char*r1 = strrchr((char*)filename, '/');
2624 char*r2 = strrchr((char*)filename, '\\');
2632 msg("<verbose> Adding font \"%s\".", filename);
2633 if(global_fonts_next) {
2634 global_fonts_next->next = f;
2635 global_fonts_next = global_fonts_next->next;
2637 global_fonts_next = global_fonts = f;
2641 void addGlobalLanguageDir(const char*dir)
2643 msg("<notice> Adding %s to language pack directories", dir);
2646 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2647 strcpy(config_file, dir);
2648 strcat(config_file, dirseparator());
2649 strcat(config_file, "add-to-xpdfrc");
2651 fi = fopen(config_file, "rb");
2653 msg("<error> Could not open %s", config_file);
2656 globalParams->parseFile(new GString(config_file), fi);
2660 void addGlobalFontDir(const char*dirname)
2662 #ifdef HAVE_DIRENT_H
2663 DIR*dir = opendir(dirname);
2665 msg("<warning> Couldn't open directory %s", dirname);
2671 ent = readdir (dir);
2675 char*name = ent->d_name;
2681 if(!strncasecmp(&name[l-4], ".pfa", 4))
2683 if(!strncasecmp(&name[l-4], ".pfb", 4))
2685 if(!strncasecmp(&name[l-4], ".ttf", 4))
2688 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2689 strcpy(fontname, dirname);
2690 strcat(fontname, dirseparator());
2691 strcat(fontname, name);
2692 addGlobalFont(fontname);
2696 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2699 msg("<warning> No dirent.h");
2703 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2704 GfxColorSpace *blendingColorSpace,
2705 GBool isolated, GBool knockout,
2708 const char*colormodename = "";
2710 if(blendingColorSpace) {
2711 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2713 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);
2714 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);
2716 //states[statepos].createsoftmask |= forSoftMask;
2717 states[statepos].createsoftmask = forSoftMask;
2718 states[statepos].transparencygroup = !forSoftMask;
2719 states[statepos].isolated = isolated;
2721 states[statepos].olddevice = this->device;
2722 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2723 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2725 gfxdevice_record_init(this->device, 0);
2727 /*if(!forSoftMask) { ////???
2728 state->setFillOpacity(0.0);
2733 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2736 gfxdevice_t*r = this->device;
2738 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2740 this->device = states[statepos].olddevice;
2742 msg("<error> Invalid state nesting");
2744 states[statepos].olddevice = 0;
2746 gfxresult_t*recording = r->finish(r);
2748 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2749 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2751 if(states[statepos].createsoftmask) {
2752 states[statepos-1].softmaskrecording = recording;
2754 states[statepos-1].grouprecording = recording;
2757 states[statepos].createsoftmask = 0;
2758 states[statepos].transparencygroup = 0;
2762 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2764 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2765 "colordodge","colorburn","hardlight","softlight","difference",
2766 "exclusion","hue","saturation","color","luminosity"};
2768 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2769 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2771 if(state->getBlendMode() == gfxBlendNormal)
2772 infofeature("transparency groups");
2775 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2776 warnfeature(buffer, 0);
2779 gfxresult_t*grouprecording = states[statepos].grouprecording;
2781 int blendmode = state->getBlendMode();
2782 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2783 int alpha = (int)(state->getFillOpacity()*255);
2784 if(blendmode == gfxBlendMultiply && alpha>200)
2787 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2788 gfxdevice_ops_init(&ops, this->device, alpha);
2789 gfxresult_record_replay(grouprecording, &ops, 0);
2792 grouprecording->destroy(grouprecording);
2794 states[statepos].grouprecording = 0;
2797 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2799 if(states[statepos].softmask) {
2800 /* shouldn't happen, but *does* happen */
2801 clearSoftMask(state);
2804 /* alpha = 1: retrieve mask values from alpha layer
2805 alpha = 0: retrieve mask values from luminance */
2807 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2808 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2809 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2810 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2812 infofeature("soft masks");
2814 warnfeature("soft masks from alpha channel",0);
2816 if(states[statepos].olddevice) {
2817 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2820 states[statepos].olddevice = this->device;
2821 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2822 gfxdevice_record_init(this->device, 0);
2824 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2826 states[statepos].softmask = 1;
2827 states[statepos].softmask_alpha = alpha;
2830 static inline Guchar div255(int x) {
2831 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2834 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2836 if(c < min) c = min;
2837 if(c > max) c = max;
2841 void GFXOutputDev::clearSoftMask(GfxState *state)
2843 if(!states[statepos].softmask)
2845 states[statepos].softmask = 0;
2846 dbg("clearSoftMask statepos=%d", statepos);
2847 msg("<verbose> clearSoftMask statepos=%d", statepos);
2849 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2850 msg("<error> Error in softmask/tgroup ordering");
2854 gfxresult_t*mask = states[statepos].softmaskrecording;
2855 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2856 this->device = states[statepos].olddevice;
2858 /* get outline of all objects below the soft mask */
2859 gfxdevice_t uniondev;
2860 gfxdevice_union_init(&uniondev, 0);
2861 gfxresult_record_replay(below, &uniondev, 0);
2862 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2863 uniondev.finish(&uniondev);
2864 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2865 gfxline_free(belowoutline);belowoutline=0;
2867 this->device->startclip(this->device, belowoutline);
2868 gfxresult_record_replay(below, this->device, 0);
2869 gfxresult_record_replay(mask, this->device, 0);
2870 this->device->endclip(this->device);
2873 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2874 if(width<=0 || height<=0)
2877 gfxdevice_t belowrender;
2878 gfxdevice_render_init(&belowrender);
2879 if(states[statepos+1].isolated) {
2880 belowrender.setparameter(&belowrender, "fillwhite", "1");
2882 belowrender.setparameter(&belowrender, "antialize", "2");
2883 belowrender.startpage(&belowrender, width, height);
2884 gfxresult_record_replay(below, &belowrender, 0);
2885 belowrender.endpage(&belowrender);
2886 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2887 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2888 //png_write("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2890 gfxdevice_t maskrender;
2891 gfxdevice_render_init(&maskrender);
2892 maskrender.startpage(&maskrender, width, height);
2893 gfxresult_record_replay(mask, &maskrender, 0);
2894 maskrender.endpage(&maskrender);
2895 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2896 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2898 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2899 msg("<fatal> Internal error in mask drawing");
2904 for(y=0;y<height;y++) {
2905 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2906 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2907 for(x=0;x<width;x++) {
2909 if(states[statepos].softmask_alpha) {
2912 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2915 l2->a = div255(alpha*l2->a);
2917 /* DON'T premultiply alpha- this is done by fillbitmap,
2918 depending on the output device */
2919 //l2->r = div255(alpha*l2->r);
2920 //l2->g = div255(alpha*l2->g);
2921 //l2->b = div255(alpha*l2->b);
2927 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2930 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2931 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2933 if(!config_textonly) {
2934 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2937 mask->destroy(mask);
2938 below->destroy(below);
2939 maskresult->destroy(maskresult);
2940 belowresult->destroy(belowresult);
2941 states[statepos].softmaskrecording = 0;
2946 // public: ~MemCheck()
2948 // delete globalParams;globalParams=0;
2949 // Object::memCheck(stderr);
2950 // gMemReport(stderr);