2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
28 #include "../../config.h"
33 #ifdef HAVE_SYS_STAT_H
36 #ifdef HAVE_FONTCONFIG
37 #include <fontconfig.h>
42 #include <goo/GooString.h>
43 #include <goo/gfile.h>
58 #include "OutputDev.h"
61 //#include "NameToUnicodeTable.h"
62 #include "GlobalParams.h"
63 #include "GFXOutputDev.h"
65 // swftools header files
67 #include "../gfxdevice.h"
68 #include "../gfxtools.h"
69 #include "../gfxfont.h"
70 #include "../gfxpoly.h"
71 #include "../devices/record.h"
72 #include "../devices/ops.h"
73 #include "../devices/polyops.h"
74 #include "../devices/render.h"
81 #define SQRT2 1.41421356237309504880
83 typedef struct _fontfile
86 int len; // basename length
88 struct _fontfile*next;
93 static fontfile_t* global_fonts = 0;
94 static fontfile_t* global_fonts_next = 0;
96 static int fontnum = 0;
109 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
110 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
111 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
112 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
113 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
114 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
115 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
116 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
117 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
118 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
119 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
120 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
121 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
122 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
125 static int verbose = 0;
126 static int dbgindent = 1;
127 static void dbg(const char*format, ...)
134 va_start(arglist, format);
135 vsnprintf(buf, sizeof(buf)-1, format, arglist);
138 while(l && buf[l-1]=='\n') {
143 int indent = dbgindent;
152 GFXOutputGlobals*gfxglobals=0;
154 GFXOutputGlobals::GFXOutputGlobals()
156 this->featurewarnings = 0;
158 this->textmodeinfo = 0;
162 GFXOutputGlobals::~GFXOutputGlobals()
164 feature_t*f = this->featurewarnings;
166 feature_t*next = f->next;
168 free(f->string);f->string =0;
174 this->featurewarnings = 0;
177 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
179 feature_t*f = gfxglobals->featurewarnings;
181 if(!strcmp(feature, f->string))
185 f = (feature_t*)malloc(sizeof(feature_t));
186 f->string = strdup(feature);
187 f->next = gfxglobals->featurewarnings;
188 gfxglobals->featurewarnings = f;
190 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
191 if(this->config_break_on_warning) {
192 msg("<fatal> Aborting conversion due to unsupported feature");
196 msg("<notice> File contains %s",feature);
199 void GFXOutputDev::warnfeature(const char*feature,char fully)
201 showfeature(feature,fully,1);
203 void GFXOutputDev::infofeature(const char*feature)
205 showfeature(feature,0,0);
208 GFXOutputState::GFXOutputState() {
210 this->createsoftmask = 0;
211 this->transparencygroup = 0;
213 this->grouprecording = 0;
217 GBool GFXOutputDev::interpretType3Chars()
222 typedef struct _drawnchar
240 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
241 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
246 free(chars);chars = 0;
253 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
257 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
260 chars[num_chars].x = x;
261 chars[num_chars].y = y;
262 chars[num_chars].color = color;
263 chars[num_chars].charid = charid;
267 char* writeOutStdFont(fontentry* f)
272 char* tmpFileName = mktmpname(namebuf1);
274 sprintf(namebuf2, "%s.afm", tmpFileName);
275 fi = fopen(namebuf2, "wb");
278 fwrite(f->afm, 1, f->afmlen, fi);
281 sprintf(namebuf2, "%s.pfb", tmpFileName);
282 fi = fopen(namebuf2, "wb");
285 fwrite(f->pfb, 1, f->pfblen, fi);
287 return strdup(namebuf2);
289 void unlinkfont(char* filename)
294 msg("<verbose> Removing temporary font file %s", filename);
297 if(!strncmp(&filename[l-4],".afm",4)) {
298 memcpy(&filename[l-4],".pfb",4); unlink(filename);
299 memcpy(&filename[l-4],".pfa",4); unlink(filename);
300 memcpy(&filename[l-4],".afm",4);
303 if(!strncmp(&filename[l-4],".pfa",4)) {
304 memcpy(&filename[l-4],".afm",4); unlink(filename);
305 memcpy(&filename[l-4],".pfa",4);
308 if(!strncmp(&filename[l-4],".pfb",4)) {
309 memcpy(&filename[l-4],".afm",4); unlink(filename);
310 memcpy(&filename[l-4],".pfb",4);
315 static int config_use_fontconfig = 1;
316 static int fcinitcalled = 0;
318 GFXGlobalParams::GFXGlobalParams()
319 : GlobalParams((char*)"")
321 //setupBaseFonts(char *dir); //not tested yet
323 GFXGlobalParams::~GFXGlobalParams()
325 msg("<verbose> Performing cleanups");
327 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
328 if(pdf2t1map[t].fullfilename) {
329 unlinkfont(pdf2t1map[t].fullfilename);
332 #ifdef HAVE_FONTCONFIG
333 if(config_use_fontconfig && fcinitcalled)
337 #ifdef HAVE_FONTCONFIG
338 static char stralphacmp(const char*s1, const char*s2)
341 /* skip over space, minus, comma etc. */
342 while(*s1>=32 && *s1<=63) s1++;
343 while(*s2>=32 && *s2<=63) s2++;
351 static char fc_ismatch(FcPattern*match, char*family, char*style)
353 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
354 FcBool scalable=FcFalse, outline=FcFalse;
355 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
356 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
357 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
358 FcPatternGetBool(match, "outline", 0, &outline);
359 FcPatternGetBool(match, "scalable", 0, &scalable);
361 if(scalable!=FcTrue || outline!=FcTrue)
364 if (!stralphacmp(fcfamily, family)) {
365 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
368 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
374 char* fontconfig_searchForFont(char*name)
376 #ifdef HAVE_FONTCONFIG
377 if(!config_use_fontconfig)
380 // call init ony once
384 // check whether we have a config file
385 char* configfile = (char*)FcConfigFilename(0);
386 int configexists = 0;
387 FILE*fi = fopen(configfile, "rb");
389 configexists = 1;fclose(fi);
390 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
392 msg("<debug> Initializing FontConfig (no configfile)");
396 /* A fontconfig instance which didn't find a configfile is unbelievably
397 cranky, so let's just write out a small xml file and make fontconfig
399 FcConfig*c = FcConfigCreate();
401 char* tmpFileName = mktmpname(namebuf);
402 FILE*fi = fopen(tmpFileName, "wb");
403 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
405 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
407 fprintf(fi, "<dir>~/.fonts</dir>\n");
409 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
411 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
412 fprintf(fi, "</fontconfig>\n");
414 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
415 FcConfigBuildFonts(c);
416 FcConfigSetCurrent(c);
420 msg("<debug> FontConfig Initialization failed. Disabling.");
421 config_use_fontconfig = 0;
424 FcConfig * config = FcConfigGetCurrent();
426 msg("<debug> FontConfig Config Initialization failed. Disabling.");
427 config_use_fontconfig = 0;
431 /* add external fonts to fontconfig's config, too. */
432 fontfile_t*fd = global_fonts;
434 FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
435 msg("<debug> Adding font %s to fontconfig", fd->filename);
439 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
440 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
441 if(!set || !set->nfont) {
442 msg("<debug> FontConfig has zero fonts. Disabling.");
443 config_use_fontconfig = 0;
447 if(getLogLevel() >= LOGLEVEL_TRACE) {
452 for(t=0;t<set->nfont;t++) {
453 char*fcfamily=0,*fcstyle=0,*filename=0;
454 FcBool scalable=FcFalse, outline=FcFalse;
455 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
456 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
457 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
458 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
459 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
460 if(scalable && outline) {
461 msg("<trace> %s (%s) -> %s", fcfamily, fcstyle, filename);
465 set = FcConfigGetFonts(config, FcSetApplication);
470 char*family = strdup(name);
472 char*dash = strchr(family, '-');
473 if(!dash) dash = strchr(family, ',');
475 FcPattern*pattern = 0;
479 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
480 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
482 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
483 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
487 FcConfigSubstitute(0, pattern, FcMatchPattern);
488 FcDefaultSubstitute(pattern);
490 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
493 for(t=0;t<set->nfont;t++) {
494 FcPattern*match = set->fonts[t];
495 //FcPattern*match = FcFontMatch(0, pattern, &result);
496 if(fc_ismatch(match, family, style)) {
498 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
499 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
502 //FcPatternDestroy(match);
503 msg("<debug> fontconfig: returning filename %s", filename);
505 FcPatternDestroy(pattern);
506 FcFontSetDestroy(set);
507 return filename?strdup(filename):0;
512 FcPatternDestroy(pattern);
513 FcFontSetDestroy(set);
520 static DisplayFontParamKind detectFontType(const char*filename)
522 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
523 return displayFontTT;
524 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
525 return displayFontT1;
526 return displayFontTT;
529 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
531 msg("<verbose> looking for font %s", fontName->getCString());
533 char*name = fontName->getCString();
535 /* see if it is a pdf standard font */
537 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
538 if(!strcmp(name, pdf2t1map[t].pdffont)) {
539 if(!pdf2t1map[t].fullfilename) {
540 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
541 if(!pdf2t1map[t].fullfilename) {
542 msg("<error> Couldn't save default font- is the Temp Directory writable?");
544 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
547 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
548 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
553 int bestlen = 0x7fffffff;
554 const char*bestfilename = 0;
556 #ifndef HAVE_FONTCONFIG
557 /* if we don't have fontconfig, try a simple filename-comparison approach */
558 fontfile_t*f = global_fonts;
560 if(strstr(f->filename, name)) {
561 if(f->len < bestlen) {
563 bestfilename = f->filename;
570 /* if we didn't find anything up to now, try looking for the
571 font via fontconfig */
574 filename = fontconfig_searchForFont(name);
576 filename = strdup(bestfilename);
580 msg("<verbose> Font %s maps to %s\n", name, filename);
581 DisplayFontParamKind kind = detectFontType(filename);
582 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
583 if(kind == displayFontTT) {
584 dfp->tt.fileName = new GString(filename);
586 dfp->t1.fileName = new GString(filename);
591 msg("<verbose> Font %s not found\n", name);
592 return GlobalParams::getDisplayFont(fontName);
596 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
599 gfxglobals = new GFXOutputGlobals();
603 this->xref = doc->getXRef();
605 this->type3active = 0;
608 this->user_movex = 0;
609 this->user_movey = 0;
612 this->user_clipx1 = 0;
613 this->user_clipy1 = 0;
614 this->user_clipx2 = 0;
615 this->user_clipy2 = 0;
616 this->current_gfxfont = 0;
617 this->current_fontinfo = 0;
618 this->current_text_stroke = 0;
619 this->current_text_clip = 0;
620 this->outer_clip_box = 0;
621 this->config_bigchar=0;
622 this->config_convertgradients=1;
623 this->config_break_on_warning=0;
624 this->config_remapunicode=0;
625 this->config_transparent=0;
626 this->config_extrafontdata = 0;
627 this->config_drawonlyshapes = 0;
628 this->config_disable_polygon_conversion = 0;
629 this->config_multiply = 1;
630 this->config_detectspaces = 1;
631 this->config_linkdatafile = 0;
635 memset(states, 0, sizeof(states));
638 void GFXOutputDev::setParameter(const char*key, const char*value)
640 if(!strcmp(key,"breakonwarning")) {
641 this->config_break_on_warning = atoi(value);
642 } else if(!strcmp(key,"remapunicode")) {
643 this->config_remapunicode = atoi(value);
644 } else if(!strcmp(key,"transparent")) {
645 this->config_transparent = atoi(value);
646 } else if(!strcmp(key,"drawonlyshapes")) {
647 this->config_drawonlyshapes = atoi(value);
648 } else if(!strcmp(key,"detectspaces")) {
649 this->config_detectspaces = atoi(value);
650 } else if(!strcmp(key,"extrafontdata")) {
651 this->config_extrafontdata = atoi(value);
652 } else if(!strcmp(key,"linkdatafile")) {
653 this->config_linkdatafile = strdup(value);
654 } else if(!strcmp(key,"convertgradients")) {
655 this->config_convertgradients = atoi(value);
656 } else if(!strcmp(key,"multiply")) {
657 this->config_multiply = atoi(value);
658 if(this->config_multiply<1)
659 this->config_multiply=1;
660 } else if(!strcmp(key,"disable_polygon_conversion")) {
661 this->config_disable_polygon_conversion = atoi(value);
665 void GFXOutputDev::setDevice(gfxdevice_t*dev)
670 void GFXOutputDev::setMove(int x,int y)
672 this->user_movex = x;
673 this->user_movey = y;
676 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
678 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
679 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
681 this->user_clipx1 = x1;
682 this->user_clipy1 = y1;
683 this->user_clipx2 = x2;
684 this->user_clipy2 = y2;
687 static char*getFontName(GfxFont*font)
690 GString*gstr = font->getName();
691 char* fname = gstr==0?0:gstr->getCString();
695 sprintf(buf, "UFONT%d", r->num);
696 fontid = strdup(buf);
698 fontid = strdup(fname);
702 char* plus = strchr(fontid, '+');
703 if(plus && plus < &fontid[strlen(fontid)-1]) {
704 fontname = strdup(plus+1);
706 fontname = strdup(fontid);
712 static void dumpFontInfo(const char*loglevel, GfxFont*font);
713 static int lastdumps[1024];
714 static int lastdumppos = 0;
719 static void showFontError(GfxFont*font, int nr)
723 for(t=0;t<lastdumppos;t++)
724 if(lastdumps[t] == r->num)
728 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
729 lastdumps[lastdumppos++] = r->num;
731 msg("<warning> The following font caused problems:");
733 msg("<warning> The following font caused problems (substituting):");
735 msg("<warning> The following Type 3 Font will be rendered as graphics:");
736 dumpFontInfo("<warning>", font);
739 static void dumpFontInfo(const char*loglevel, GfxFont*font)
741 char* id = getFontID(font);
742 char* name = getFontName(font);
743 Ref* r=font->getID();
744 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
746 GString*gstr = font->getTag();
748 msg("%s| Tag: %s", loglevel, id);
750 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
752 GfxFontType type=font->getType();
754 case fontUnknownType:
755 msg("%s| Type: unknown",loglevel);
758 msg("%s| Type: 1",loglevel);
761 msg("%s| Type: 1C",loglevel);
764 msg("%s| Type: 3",loglevel);
767 msg("%s| Type: TrueType",loglevel);
770 msg("%s| Type: CIDType0",loglevel);
773 msg("%s| Type: CIDType0C",loglevel);
776 msg("%s| Type: CIDType2",loglevel);
781 GBool embedded = font->getEmbeddedFontID(&embRef);
783 if(font->getEmbeddedFontName()) {
784 embeddedName = font->getEmbeddedFontName()->getCString();
787 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
789 gstr = font->getExtFontFile();
791 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
793 // Get font descriptor flags.
794 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
795 if(font->isSerif()) msg("%s| is serif", loglevel);
796 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
797 if(font->isItalic()) msg("%s| is italic", loglevel);
798 if(font->isBold()) msg("%s| is bold", loglevel);
804 //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");}
805 //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");}
807 void dump_outline(gfxline_t*line)
809 /*gfxbbox_t*r = gfxline_isrectangle(line);
811 printf("is not a rectangle\n");
813 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
817 if(line->type == gfx_moveTo) {
818 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
819 } else if(line->type == gfx_lineTo) {
820 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
821 } else if(line->type == gfx_splineTo) {
822 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
828 void gfxPath_dump(GfxPath*path)
830 int num = path->getNumSubpaths();
833 for(t = 0; t < num; t++) {
834 GfxSubpath *subpath = path->getSubpath(t);
835 int subnum = subpath->getNumPoints();
837 for(s=0;s<subnum;s++) {
838 double x=subpath->getX(s);
839 double y=subpath->getY(s);
840 if(s==0 && !subpath->getCurve(s)) {
841 printf("M %f %f\n", x, y);
842 } else if(s==0 && subpath->getCurve(s)) {
843 printf("E %f %f\n", x, y);
844 } else if(subpath->getCurve(s)) {
845 printf("C %f %f\n", x, y);
847 printf("T %f %f\n", x, y);
853 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
855 int num = path->getNumSubpaths();
858 double lastx=0,lasty=0,posx=0,posy=0;
861 msg("<warning> empty path");
865 gfxdrawer_target_gfxline(&draw);
867 for(t = 0; t < num; t++) {
868 GfxSubpath *subpath = path->getSubpath(t);
869 int subnum = subpath->getNumPoints();
870 double bx=0,by=0,cx=0,cy=0;
872 for(s=0;s<subnum;s++) {
875 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
878 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
879 draw.lineTo(&draw, lastx, lasty);
881 draw.moveTo(&draw, x,y);
886 } else if(subpath->getCurve(s) && cpos==0) {
890 } else if(subpath->getCurve(s) && cpos==1) {
898 draw.lineTo(&draw, x,y);
900 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
907 /* fix non-closed lines */
908 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
909 draw.lineTo(&draw, lastx, lasty);
911 gfxline_t*result = (gfxline_t*)draw.result(&draw);
913 gfxline_optimize(result);
918 GBool GFXOutputDev::useTilingPatternFill()
920 infofeature("tiled patterns");
921 // if(config_convertgradients)
925 GBool GFXOutputDev::useShadedFills()
927 infofeature("shaded fills");
928 if(config_convertgradients)
933 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
935 state->transform(x,y,nx,ny);
936 *nx += user_movex + clipmovex;
937 *ny += user_movey + clipmovey;
941 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
942 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
943 int paintType, Dict *resDict,
944 double *mat, double *bbox,
945 int x0, int y0, int x1, int y1,
946 double xStep, double yStep)
948 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
949 int paintType, Dict *resDict,
950 double *mat, double *bbox,
951 int x0, int y0, int x1, int y1,
952 double xStep, double yStep)
955 msg("<debug> tilingPatternFill");
956 infofeature("tiling pattern fills");
959 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
961 msg("<error> functionShadedFill not supported yet");
962 infofeature("function shaded fills");
965 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
969 colspace->getRGB(col, &rgb);
970 c.r = colToByte(rgb.r);
971 c.g = colToByte(rgb.g);
972 c.b = colToByte(rgb.b);
977 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
979 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
980 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
983 this->transformXY(state, x0,y0, &x0,&y0);
984 this->transformXY(state, x1,y1, &x1,&y1);
985 this->transformXY(state, x2,y2, &x2,&y2);
990 shading->getColor(0.0, &color0);
991 shading->getColor(0.5, &color1);
992 shading->getColor(1.0, &color2);
994 GfxColorSpace* colspace = shading->getColorSpace();
996 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
997 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
998 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
999 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
1000 infofeature("radial shaded fills");
1002 gfxgradient_t gr[3];
1003 gfxgradient_t*g = &gr[0];
1007 g[0].color = col2col(colspace, &color0);
1008 g[1].color = col2col(colspace, &color1);
1009 g[2].color = col2col(colspace, &color2);
1014 gfxbbox_t b = states[statepos].clipbbox;
1015 gfxline_t p1,p2,p3,p4,p5;
1016 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1017 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1018 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1019 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1020 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1023 //m.m00 = (x3-x0); m.m10 = (x1-x0);
1024 //m.m01 = (y3-y0); m.m11 = (y1-y0);
1025 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1026 m.m00 = (x1-x0); m.m10 = (x2-x0);
1027 m.m01 = (y1-y0); m.m11 = (y2-y0);
1031 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1035 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1038 shading->getCoords(&x0,&y0,&x1,&y1);
1039 this->transformXY(state, x0,y0,&x0,&y0);
1040 this->transformXY(state, x1,y1,&x1,&y1);
1045 shading->getColor(0.0, &color0);
1046 shading->getColor(0.5, &color1);
1047 shading->getColor(1.0, &color2);
1049 GfxColorSpace* colspace = shading->getColorSpace();
1051 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1052 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1053 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1054 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1056 infofeature("axial shaded fills");
1058 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1062 g[0].color = col2col(colspace, &color0);
1063 g[1].color = col2col(colspace, &color1);
1064 g[2].color = col2col(colspace, &color2);
1069 double xMin,yMin,xMax,yMax;
1070 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1071 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1072 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1075 xMin = 1024; yMin = 1024;
1077 gfxbbox_t b = states[statepos].clipbbox;
1078 gfxline_t p1,p2,p3,p4,p5;
1079 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1080 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1081 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1082 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1083 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1085 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1086 the middle of the two control points */
1088 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1089 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1090 m.tx = (x0 + x1)/2 - 0.5;
1091 m.ty = (y0 + y1)/2 - 0.5;
1093 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1099 GBool GFXOutputDev::useDrawForm()
1101 infofeature("forms");
1104 void GFXOutputDev::drawForm(Ref id)
1106 msg("<error> drawForm not implemented");
1108 GBool GFXOutputDev::needNonText()
1112 void GFXOutputDev::endPage()
1114 msg("<verbose> endPage (GfxOutputDev)");
1115 if(outer_clip_box) {
1116 device->endclip(device);
1119 /* notice: we're not fully done yet with this page- there might still be
1120 a few calls to drawLink() yet to come */
1123 static inline double sqr(double x) {return x*x;}
1125 #define STROKE_FILL 1
1126 #define STROKE_CLIP 2
1127 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1129 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1130 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1131 double miterLimit = state->getMiterLimit();
1132 double width = state->getTransformedLineWidth();
1135 double opaq = state->getStrokeOpacity();
1137 state->getFillRGB(&rgb);
1139 state->getStrokeRGB(&rgb);
1141 col.r = colToByte(rgb.r);
1142 col.g = colToByte(rgb.g);
1143 col.b = colToByte(rgb.b);
1144 col.a = (unsigned char)(opaq*255);
1146 gfx_capType capType = gfx_capRound;
1147 if(lineCap == 0) capType = gfx_capButt;
1148 else if(lineCap == 1) capType = gfx_capRound;
1149 else if(lineCap == 2) capType = gfx_capSquare;
1150 else msg("<error> Invalid line cap type");
1152 gfx_joinType joinType = gfx_joinRound;
1153 if(lineJoin == 0) joinType = gfx_joinMiter;
1154 else if(lineJoin == 1) joinType = gfx_joinRound;
1155 else if(lineJoin == 2) joinType = gfx_joinBevel;
1156 else msg("<error> Invalid line join type");
1158 gfxline_t*line2 = 0;
1160 int dashLength = states[statepos].dashLength;
1161 double*dashPattern = states[statepos].dashPattern;
1162 double dashStart = states[statepos].dashStart;
1163 if(dashLength && dashPattern) {
1164 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1167 /* try to find out how much the transformation matrix would
1168 stretch the dashes, and factor that into the dash lengths.
1169 This is not the entirely correct approach- it would be
1170 better to first convert the path to an unscaled version,
1171 then apply dashing, and then transform the path using
1172 the current transformation matrix. However there are few
1173 PDFs which actually stretch a dashed path in a non-orthonormal
1175 double tx1, ty1, tx2, ty2, tx3, ty3;
1176 this->transformXY(state, 0, 0, &tx1, &ty1);
1177 this->transformXY(state, 0, 1, &tx2, &ty2);
1178 this->transformXY(state, 1, 0, &tx3, &ty3);
1179 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1180 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1182 warnfeature("non-ortogonally dashed strokes", 0);
1183 double f = (d1+d2)/2;
1185 msg("<trace> %d dashes", dashLength);
1186 msg("<trace> | phase: %f", dashStart);
1187 for(t=0;t<dashLength;t++) {
1188 dash[t] = (float)dashPattern[t] * f;
1192 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1194 dash[dashLength] = -1;
1195 if(getLogLevel() >= LOGLEVEL_TRACE) {
1199 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1203 msg("<trace> After dashing:");
1206 if(getLogLevel() >= LOGLEVEL_TRACE) {
1207 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1209 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1210 lineCap==0?"butt": (lineCap==1?"round":"square"),
1212 col.r,col.g,col.b,col.a
1217 if(flags&STROKE_FILL) {
1218 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1219 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1220 if(getLogLevel() >= LOGLEVEL_TRACE) {
1221 dump_outline(gfxline);
1224 msg("<warning> Empty polygon (resulting from stroked line)");
1226 if(flags&STROKE_CLIP) {
1227 device->startclip(device, gfxline);
1228 states[statepos].clipping++;
1230 device->fill(device, gfxline, &col);
1232 gfxline_free(gfxline);
1233 gfxpoly_destroy(poly);
1235 if(flags&STROKE_CLIP)
1236 msg("<error> Stroke&clip not supported at the same time");
1237 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1241 gfxline_free(line2);
1244 gfxcolor_t getFillColor(GfxState * state)
1247 double opaq = state->getFillOpacity();
1248 state->getFillRGB(&rgb);
1250 col.r = colToByte(rgb.r);
1251 col.g = colToByte(rgb.g);
1252 col.b = colToByte(rgb.b);
1253 col.a = (unsigned char)(opaq*255);
1257 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1259 gfxcolor_t col = getFillColor(state);
1261 if(getLogLevel() >= LOGLEVEL_TRACE) {
1262 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1265 device->fill(device, line, &col);
1268 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1270 if(getLogLevel() >= LOGLEVEL_TRACE) {
1271 msg("<trace> %sclip", evenodd?"eo":"");
1274 gfxbbox_t bbox = gfxline_getbbox(line);
1275 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1277 device->startclip(device, line);
1278 states[statepos].clipping++;
1281 void GFXOutputDev::clip(GfxState *state)
1283 GfxPath * path = state->getPath();
1284 msg("<trace> clip");
1285 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1286 if(!config_disable_polygon_conversion) {
1287 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1291 clipToGfxLine(state, line, 0);
1295 void GFXOutputDev::eoClip(GfxState *state)
1297 GfxPath * path = state->getPath();
1298 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1299 clipToGfxLine(state, line, 1);
1302 void GFXOutputDev::clipToStrokePath(GfxState *state)
1304 GfxPath * path = state->getPath();
1305 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1307 if(getLogLevel() >= LOGLEVEL_TRACE) {
1308 double width = state->getTransformedLineWidth();
1309 msg("<trace> cliptostrokepath width=%f", width);
1313 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1317 void GFXOutputDev::finish()
1319 if(outer_clip_box) {
1321 device->endclip(device);
1327 GFXOutputDev::~GFXOutputDev()
1331 GBool GFXOutputDev::upsideDown()
1335 GBool GFXOutputDev::useDrawChar()
1340 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1341 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1343 static char tmp_printstr[4096];
1344 char* makeStringPrintable(char*str)
1346 int len = strlen(str);
1353 for(t=0;t<len;t++) {
1358 tmp_printstr[t] = c;
1361 tmp_printstr[len++] = '.';
1362 tmp_printstr[len++] = '.';
1363 tmp_printstr[len++] = '.';
1365 tmp_printstr[len] = 0;
1366 return tmp_printstr;
1368 void GFXOutputDev::updateFontMatrix(GfxState*state)
1370 double* ctm = state->getCTM();
1371 double fontSize = state->getFontSize();
1372 double*textMat = state->getTextMat();
1374 /* taking the absolute value of horizScaling seems to be required for
1375 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1376 double hscale = fabs(state->getHorizScaling());
1378 // from xpdf-3.02/SplashOutputDev:updateFont
1379 double mm11 = textMat[0] * fontSize * hscale;
1380 double mm12 = textMat[1] * fontSize * hscale;
1381 double mm21 = textMat[2] * fontSize;
1382 double mm22 = textMat[3] * fontSize;
1384 // multiply with ctm, like state->getFontTransMat() does
1385 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1386 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1387 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1388 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1389 this->current_font_matrix.tx = 0;
1390 this->current_font_matrix.ty = 0;
1393 void GFXOutputDev::beginString(GfxState *state, GString *s)
1395 int render = state->getRender();
1396 if(current_text_stroke) {
1397 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1399 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1402 static gfxline_t* mkEmptyGfxShape(double x, double y)
1404 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1405 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1409 static char isValidUnicode(int c)
1411 if(c>=32 && c<0x2fffe)
1416 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1417 double dx, double dy,
1418 double originX, double originY,
1419 CharCode charid, int nBytes, Unicode *_u, int uLen)
1421 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1422 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1426 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1428 int render = state->getRender();
1429 gfxcolor_t col = getFillColor(state);
1431 // check for invisible text -- this is used by Acrobat Capture
1432 if (render == RENDER_INVISIBLE) {
1434 if(!config_extrafontdata)
1438 GfxFont*font = state->getFont();
1440 if(font->getType() == fontType3) {
1441 /* type 3 chars are passed as graphics */
1442 msg("<debug> type3 char at %f/%f", x, y);
1446 Unicode u = uLen?(_u[0]):0;
1448 gfxmatrix_t m = this->current_font_matrix;
1449 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1450 //m.tx += originX; m.ty += originY;
1452 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);
1454 if((render == RENDER_FILL && !config_drawonlyshapes) ||
1455 (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
1456 (render == RENDER_INVISIBLE)) {
1458 int space = this->current_fontinfo->space_char;
1459 if(config_extrafontdata && config_detectspaces && space>=0 && m.m00 && !m.m01) {
1460 /* space char detection */
1461 if(last_char_gfxfont == current_gfxfont &&
1462 last_char_y == m.ty &&
1463 !last_char_was_space) {
1464 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1465 int space = this->current_fontinfo->space_char;
1466 float width = this->current_fontinfo->average_advance;
1467 if(m.tx - expected_x >= m.m00*width*4/10) {
1468 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",
1471 last_char, glyphid);
1473 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1474 if(m2.tx < expected_x) m2.tx = expected_x;
1475 device->drawchar(device, current_gfxfont, space, &col, &m2);
1478 last_char_gfxfont = current_gfxfont;
1479 last_char = glyphid;
1482 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1484 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1486 msg("<debug> Drawing glyph %d as shape", charid);
1487 if(!gfxglobals->textmodeinfo) {
1488 msg("<notice> Some texts will be rendered as shape");
1489 gfxglobals->textmodeinfo = 1;
1492 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1493 gfxline_t*tglyph = gfxline_clone(glyph);
1494 gfxline_transform(tglyph, &m);
1495 if((render&3) != RENDER_INVISIBLE) {
1496 gfxline_t*add = gfxline_clone(tglyph);
1497 current_text_stroke = gfxline_append(current_text_stroke, add);
1499 if(render&RENDER_CLIP) {
1500 gfxline_t*add = gfxline_clone(tglyph);
1501 current_text_clip = gfxline_append(current_text_clip, add);
1502 if(!current_text_clip) {
1503 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1506 gfxline_free(tglyph);
1510 void GFXOutputDev::endString(GfxState *state)
1512 int render = state->getRender();
1513 msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
1515 if(current_text_stroke) {
1516 /* fillstroke and stroke text rendering objects we can process right
1517 now (as there may be texts of other rendering modes in this
1518 text object)- clipping objects have to wait until endTextObject,
1520 device->setparameter(device, "mark","TXT");
1521 if((render&3) == RENDER_FILL) {
1522 fillGfxLine(state, current_text_stroke, 0);
1523 gfxline_free(current_text_stroke);
1524 current_text_stroke = 0;
1525 } else if((render&3) == RENDER_FILLSTROKE) {
1526 fillGfxLine(state, current_text_stroke, 0);
1527 strokeGfxline(state, current_text_stroke,0);
1528 gfxline_free(current_text_stroke);
1529 current_text_stroke = 0;
1530 } else if((render&3) == RENDER_STROKE) {
1531 strokeGfxline(state, current_text_stroke,0);
1532 gfxline_free(current_text_stroke);
1533 current_text_stroke = 0;
1535 device->setparameter(device, "mark","");
1539 void GFXOutputDev::endTextObject(GfxState *state)
1541 int render = state->getRender();
1542 msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
1544 if(current_text_clip) {
1545 device->setparameter(device, "mark","TXT");
1546 clipToGfxLine(state, current_text_clip, 0);
1547 device->setparameter(device, "mark","");
1548 gfxline_free(current_text_clip);
1549 current_text_clip = 0;
1553 /* the logic seems to be as following:
1554 first, beginType3Char is called, with the charcode and the coordinates.
1555 if this function returns true, it already knew about the char and has now drawn it.
1556 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1557 called with some parameters.
1558 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1559 at the position first passed to beginType3Char). the char ends with endType3Char.
1561 The drawing operations between beginType3Char and endType3Char are somewhat different to
1562 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1563 color determines the color of a font)
1566 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1568 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1571 if(config_extrafontdata && current_fontinfo) {
1573 gfxmatrix_t m = this->current_font_matrix;
1574 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1576 /*m.m00*=INTERNAL_FONT_SIZE;
1577 m.m01*=INTERNAL_FONT_SIZE;
1578 m.m10*=INTERNAL_FONT_SIZE;
1579 m.m11*=INTERNAL_FONT_SIZE;*/
1581 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1582 msg("<error> Invalid charid %d for font", charid);
1585 gfxcolor_t col={0,0,0,0};
1586 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1587 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1591 /* the character itself is going to be passed using the draw functions */
1592 return gFalse; /* gTrue= is_in_cache? */
1595 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1597 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1600 void GFXOutputDev::endType3Char(GfxState *state)
1603 msg("<debug> endType3Char");
1606 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1608 this->currentpage = pageNum;
1610 int rot = doc->getPageRotate(1);
1611 gfxcolor_t white = {255,255,255,255};
1612 gfxcolor_t black = {255,0,0,0};
1614 gfxline_t clippath[5];
1616 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1617 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1618 Use CropBox, not MediaBox, as page size
1625 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1626 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1628 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1629 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1631 this->clipmovex = -(int)x1;
1632 this->clipmovey = -(int)y1;
1634 /* apply user clip box */
1635 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1636 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1637 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1638 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1639 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1640 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1642 x1 += this->clipmovex;
1643 y1 += this->clipmovey;
1644 x2 += this->clipmovex;
1645 y2 += this->clipmovey;
1648 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1650 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);
1652 msg("<verbose> page is rotated %d degrees", rot);
1654 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1655 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1656 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1657 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1658 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1659 device->startclip(device, clippath); outer_clip_box = 1;
1660 if(!config_transparent) {
1661 device->fill(device, clippath, &white);
1663 states[statepos].clipbbox.xmin = x1;
1664 states[statepos].clipbbox.ymin = x1;
1665 states[statepos].clipbbox.xmax = x2;
1666 states[statepos].clipbbox.ymax = y2;
1668 states[statepos].dashPattern = 0;
1669 states[statepos].dashLength = 0;
1670 states[statepos].dashStart = 0;
1672 this->last_char_gfxfont = 0;
1676 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1678 double x1, y1, x2, y2;
1679 gfxline_t points[5];
1682 msg("<debug> drawlink");
1684 link->getRect(&x1, &y1, &x2, &y2);
1685 cvtUserToDev(x1, y1, &x, &y);
1686 points[0].type = gfx_moveTo;
1687 points[0].x = points[4].x = x + user_movex + clipmovex;
1688 points[0].y = points[4].y = y + user_movey + clipmovey;
1689 points[0].next = &points[1];
1690 cvtUserToDev(x2, y1, &x, &y);
1691 points[1].type = gfx_lineTo;
1692 points[1].x = x + user_movex + clipmovex;
1693 points[1].y = y + user_movey + clipmovey;
1694 points[1].next = &points[2];
1695 cvtUserToDev(x2, y2, &x, &y);
1696 points[2].type = gfx_lineTo;
1697 points[2].x = x + user_movex + clipmovex;
1698 points[2].y = y + user_movey + clipmovey;
1699 points[2].next = &points[3];
1700 cvtUserToDev(x1, y2, &x, &y);
1701 points[3].type = gfx_lineTo;
1702 points[3].x = x + user_movex + clipmovex;
1703 points[3].y = y + user_movey + clipmovey;
1704 points[3].next = &points[4];
1705 cvtUserToDev(x1, y1, &x, &y);
1706 points[4].type = gfx_lineTo;
1707 points[4].x = x + user_movex + clipmovex;
1708 points[4].y = y + user_movey + clipmovey;
1711 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1712 points[0].x, points[0].y,
1713 points[1].x, points[1].y,
1714 points[2].x, points[2].y,
1715 points[3].x, points[3].y);
1717 if(getLogLevel() >= LOGLEVEL_TRACE) {
1718 dump_outline(points);
1721 LinkAction*action=link->getAction();
1724 const char*type = "-?-";
1727 msg("<trace> drawlink action=%d", action->getKind());
1728 switch(action->getKind())
1732 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1733 LinkDest *dest=NULL;
1734 if (ha->getDest()==NULL)
1735 dest=catalog->findDest(ha->getNamedDest());
1737 dest=ha->getDest()->copy();
1739 if (dest->isPageRef()){
1740 Ref pageref=dest->getPageRef();
1741 page=catalog->findPage(pageref.num,pageref.gen);
1743 else page=dest->getPageNum();
1744 sprintf(buf, "%d", page);
1752 LinkGoToR*l = (LinkGoToR*)action;
1753 GString*g = l->getFileName();
1755 s = strdup(g->getCString());
1757 /* if the GoToR link has no filename, then
1758 try to find a refernce in the *local*
1760 GString*g = l->getNamedDest();
1762 s = strdup(g->getCString());
1768 LinkNamed*l = (LinkNamed*)action;
1769 GString*name = l->getName();
1771 s = strdup(name->lowerCase()->getCString());
1772 named = name->getCString();
1775 if(strstr(s, "next") || strstr(s, "forward"))
1777 page = currentpage + 1;
1779 else if(strstr(s, "prev") || strstr(s, "back"))
1781 page = currentpage - 1;
1783 else if(strstr(s, "last") || strstr(s, "end"))
1785 if(this->page2page && this->num_pages) {
1786 page = this->page2page[this->num_pages-1];
1789 else if(strstr(s, "first") || strstr(s, "top"))
1797 case actionLaunch: {
1799 LinkLaunch*l = (LinkLaunch*)action;
1800 GString * str = new GString(l->getFileName());
1801 GString * params = l->getParams();
1803 str->append(params);
1804 s = strdup(str->getCString());
1811 LinkURI*l = (LinkURI*)action;
1812 GString*g = l->getURI();
1814 url = g->getCString();
1819 case actionUnknown: {
1821 LinkUnknown*l = (LinkUnknown*)action;
1826 msg("<error> Unknown link type!");
1831 if(!s) s = strdup("-?-");
1833 msg("<trace> drawlink s=%s", s);
1835 if(!gfxglobals->linkinfo && (page || s))
1837 msg("<notice> File contains links");
1838 gfxglobals->linkinfo = 1;
1844 for(t=1;t<=this->num_pages;t++) {
1845 if(this->page2page[t]==page) {
1855 sprintf(buf, "page%d", lpage);
1856 device->drawlink(device, points, buf);
1860 device->drawlink(device, points, s);
1861 if(this->config_linkdatafile) {
1862 FILE*fi = fopen(config_linkdatafile, "ab+");
1863 fprintf(fi, "%s\n", s);
1868 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1872 void GFXOutputDev::saveState(GfxState *state) {
1873 dbg("saveState %p", state); dbgindent+=2;
1875 msg("<trace> saveState %p", state);
1878 msg("<fatal> Too many nested states in pdf.");
1882 states[statepos].state = state;
1883 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1884 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1885 states[statepos].clipping = 0;
1886 states[statepos].olddevice = 0;
1887 states[statepos].clipbbox = states[statepos-1].clipbbox;
1889 states[statepos].dashPattern = states[statepos-1].dashPattern;
1890 states[statepos].dashStart = states[statepos-1].dashStart;
1891 states[statepos].dashLength = states[statepos-1].dashLength;
1894 void GFXOutputDev::restoreState(GfxState *state) {
1895 dbgindent-=2; dbg("restoreState %p", state);
1898 msg("<fatal> Invalid restoreState");
1901 msg("<trace> restoreState %p%s%s", state,
1902 states[statepos].softmask?" (end softmask)":"",
1903 states[statepos].clipping?" (end clipping)":"");
1904 if(states[statepos].softmask) {
1905 clearSoftMask(state);
1908 if(states[statepos].dashPattern) {
1909 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1910 free(states[statepos].dashPattern);
1911 states[statepos].dashPattern = 0;
1917 while(states[statepos].clipping) {
1918 device->endclip(device);
1919 states[statepos].clipping--;
1921 if(states[statepos].state!=state) {
1922 msg("<fatal> bad state nesting");
1925 for(t=0;t<=statepos;t++) {
1926 printf("%p ", states[t].state);
1932 states[statepos].state=0;
1936 void GFXOutputDev::updateLineDash(GfxState *state)
1938 if(states[statepos].dashPattern &&
1939 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1940 free(states[statepos].dashPattern);
1941 states[statepos].dashPattern = 0;
1943 double *pattern = 0;
1946 state->getLineDash(&pattern, &dashLength, &dashStart);
1947 msg("<debug> updateLineDash, %d dashes", dashLength);
1949 states[statepos].dashPattern = 0;
1950 states[statepos].dashLength = 0;
1952 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1953 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1954 states[statepos].dashPattern = p;
1955 states[statepos].dashLength = dashLength;
1956 states[statepos].dashStart = dashStart;
1960 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1962 this->page2page = page2page;
1963 this->num_pages = num_pages;
1966 void GFXOutputDev::updateLineWidth(GfxState *state)
1968 double width = state->getTransformedLineWidth();
1971 void GFXOutputDev::updateLineCap(GfxState *state)
1973 int c = state->getLineCap();
1976 void GFXOutputDev::updateLineJoin(GfxState *state)
1978 int j = state->getLineJoin();
1981 void GFXOutputDev::updateFillColor(GfxState *state)
1984 double opaq = state->getFillOpacity();
1985 state->getFillRGB(&rgb);
1987 void GFXOutputDev::updateFillOpacity(GfxState *state)
1990 double opaq = state->getFillOpacity();
1991 state->getFillRGB(&rgb);
1992 dbg("update fillopaq %f", opaq);
1994 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1996 double opaq = state->getFillOpacity();
1997 dbg("update strokeopaq %f", opaq);
1999 void GFXOutputDev::updateFillOverprint(GfxState *state)
2001 double opaq = state->getFillOverprint();
2002 dbg("update filloverprint %f", opaq);
2004 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2006 double opaq = state->getStrokeOverprint();
2007 dbg("update strokeoverprint %f", opaq);
2009 void GFXOutputDev::updateTransfer(GfxState *state)
2011 dbg("update transfer");
2015 void GFXOutputDev::updateStrokeColor(GfxState *state)
2018 double opaq = state->getStrokeOpacity();
2019 state->getStrokeRGB(&rgb);
2022 void GFXOutputDev::updateFont(GfxState *state)
2024 GfxFont* gfxFont = state->getFont();
2028 char*id = getFontID(gfxFont);
2029 msg("<verbose> Updating font to %s", id);
2030 if(gfxFont->getType() == fontType3) {
2031 infofeature("Type3 fonts");
2032 if(!config_extrafontdata) {
2037 msg("<error> Internal Error: FontID is null");
2041 this->current_fontinfo = this->info->getFont(id);
2043 if(!this->current_fontinfo) {
2044 msg("<error> Internal Error: no fontinfo for font %s", id);
2047 if(!this->current_fontinfo->seen) {
2048 dumpFontInfo("<verbose>", gfxFont);
2051 current_gfxfont = this->current_fontinfo->getGfxFont();
2052 device->addfont(device, current_gfxfont);
2055 updateFontMatrix(state);
2058 #define SQR(x) ((x)*(x))
2060 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2062 if((newwidth<1 || newheight<1) ||
2063 (width<=newwidth || height<=newheight))
2065 unsigned char*newdata;
2067 newdata= (unsigned char*)malloc(newwidth*newheight);
2068 double fx = ((double)width)/newwidth;
2069 double fy = ((double)height)/newheight;
2071 int blocksize = (int)(8192/(fx*fy));
2072 int r = 8192*256/palettesize;
2073 for(x=0;x<newwidth;x++) {
2074 double ex = px + fx;
2075 int fromx = (int)px;
2077 int xweight1 = (int)((1-(px-fromx))*256);
2078 int xweight2 = (int)((ex-tox)*256);
2080 for(y=0;y<newheight;y++) {
2081 double ey = py + fy;
2082 int fromy = (int)py;
2084 int yweight1 = (int)((1-(py-fromy))*256);
2085 int yweight2 = (int)((ey-toy)*256);
2092 for(xx=fromx;xx<=tox;xx++)
2093 for(yy=fromy;yy<=toy;yy++) {
2094 int b = 1-data[width*yy+xx];
2096 if(xx==fromx) weight = (weight*xweight1)/256;
2097 if(xx==tox) weight = (weight*xweight2)/256;
2098 if(yy==fromy) weight = (weight*yweight1)/256;
2099 if(yy==toy) weight = (weight*yweight2)/256;
2102 //if(a) a=(palettesize-1)*r/blocksize;
2103 newdata[y*newwidth+x] = (a*blocksize)/r;
2111 #define IMAGE_TYPE_JPEG 0
2112 #define IMAGE_TYPE_LOSSLESS 1
2114 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2115 double x1,double y1,
2116 double x2,double y2,
2117 double x3,double y3,
2118 double x4,double y4, int type, int multiply)
2120 gfxcolor_t*newpic=0;
2122 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2123 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2125 gfxline_t p1,p2,p3,p4,p5;
2126 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2127 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2128 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2129 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2130 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2132 {p1.x = (int)(p1.x*20)/20.0;
2133 p1.y = (int)(p1.y*20)/20.0;
2134 p2.x = (int)(p2.x*20)/20.0;
2135 p2.y = (int)(p2.y*20)/20.0;
2136 p3.x = (int)(p3.x*20)/20.0;
2137 p3.y = (int)(p3.y*20)/20.0;
2138 p4.x = (int)(p4.x*20)/20.0;
2139 p4.y = (int)(p4.y*20)/20.0;
2140 p5.x = (int)(p5.x*20)/20.0;
2141 p5.y = (int)(p5.y*20)/20.0;
2145 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2146 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2148 m.tx = p1.x - 0.5*multiply;
2149 m.ty = p1.y - 0.5*multiply;
2152 img.data = (gfxcolor_t*)data;
2156 if(type == IMAGE_TYPE_JPEG)
2157 /* TODO: pass image_dpi to device instead */
2158 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2161 dev->fillbitmap(dev, &p1, &img, &m, 0);
2164 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2165 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2167 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2170 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2171 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2173 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2177 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2178 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2179 GBool inlineImg, int mask, int*maskColors,
2180 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2182 /* the code in this function is *old*. It's not pretty, but it works. */
2184 double x1,y1,x2,y2,x3,y3,x4,y4;
2185 ImageStream *imgStr;
2190 unsigned char* maskbitmap = 0;
2193 ncomps = colorMap->getNumPixelComps();
2194 bits = colorMap->getBits();
2199 unsigned char buf[8];
2200 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2202 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2203 imgMaskStr->reset();
2204 unsigned char pal[256];
2205 int n = 1 << colorMap->getBits();
2210 maskColorMap->getGray(pixBuf, &gray);
2211 pal[t] = colToByte(gray);
2213 for (y = 0; y < maskHeight; y++) {
2214 for (x = 0; x < maskWidth; x++) {
2215 imgMaskStr->getPixel(buf);
2216 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2221 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2222 imgMaskStr->reset();
2223 for (y = 0; y < maskHeight; y++) {
2224 for (x = 0; x < maskWidth; x++) {
2225 imgMaskStr->getPixel(buf);
2227 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2235 imgStr = new ImageStream(str, width, ncomps,bits);
2238 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2240 msg("<verbose> Ignoring %d by %d image", width, height);
2241 unsigned char buf[8];
2243 for (y = 0; y < height; ++y)
2244 for (x = 0; x < width; ++x) {
2245 imgStr->getPixel(buf);
2253 this->transformXY(state, 0, 1, &x1, &y1);
2254 this->transformXY(state, 0, 0, &x2, &y2);
2255 this->transformXY(state, 1, 0, &x3, &y3);
2256 this->transformXY(state, 1, 1, &x4, &y4);
2259 /* as type 3 bitmaps are antialized, we need to place them
2260 at integer coordinates, otherwise flash player's antializing
2261 will kick in and make everything blurry */
2262 x1 = (int)(x1);y1 = (int)(y1);
2263 x2 = (int)(x2);y2 = (int)(y2);
2264 x3 = (int)(x3);y3 = (int)(y3);
2265 x4 = (int)(x4);y4 = (int)(y4);
2268 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2270 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2271 gfxglobals->pbminfo = 1;
2274 msg("<verbose> drawing %d by %d masked picture", width, height);
2276 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2277 msg("<notice> File contains jpeg pictures");
2278 gfxglobals->jpeginfo = 1;
2282 unsigned char buf[8];
2284 unsigned char*pic = new unsigned char[width*height];
2285 gfxcolor_t pal[256];
2287 state->getFillRGB(&rgb);
2289 memset(pal,255,sizeof(pal));
2290 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2291 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2292 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2293 pal[0].a = 255; pal[1].a = 0;
2296 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2297 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2298 for (y = 0; y < height; ++y)
2299 for (x = 0; x < width; ++x)
2301 imgStr->getPixel(buf);
2304 pic[width*y+x] = buf[0];
2308 unsigned char*pic2 = 0;
2311 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2320 height = realheight;
2324 /* make a black/white palette */
2326 float r = 255./(float)(numpalette-1);
2328 for(t=0;t<numpalette;t++) {
2329 pal[t].r = colToByte(rgb.r);
2330 pal[t].g = colToByte(rgb.g);
2331 pal[t].b = colToByte(rgb.b);
2332 pal[t].a = (unsigned char)(t*r);
2337 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2338 for (y = 0; y < height; ++y) {
2339 for (x = 0; x < width; ++x) {
2340 pic2[width*y+x] = pal[pic[y*width+x]];
2343 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2347 if(maskbitmap) free(maskbitmap);
2353 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2354 gfxcolor_t*pic=new gfxcolor_t[width*height];
2355 for (y = 0; y < height; ++y) {
2356 for (x = 0; x < width; ++x) {
2357 imgStr->getPixel(pixBuf);
2358 colorMap->getRGB(pixBuf, &rgb);
2359 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2360 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2361 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2362 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2364 int x1 = x*maskWidth/width;
2365 int y1 = y*maskHeight/height;
2366 int x2 = (x+1)*maskWidth/width;
2367 int y2 = (y+1)*maskHeight/height;
2369 unsigned int alpha=0;
2370 unsigned int count=0;
2371 for(xx=x1;xx<x2;xx++)
2372 for(yy=y1;yy<y2;yy++) {
2373 alpha += maskbitmap[yy*maskWidth+xx];
2377 pic[width*y+x].a = alpha / count;
2379 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2384 if(str->getKind()==strDCT)
2385 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2387 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2390 if(maskbitmap) free(maskbitmap);
2393 gfxcolor_t*pic=new gfxcolor_t[width*height];
2394 gfxcolor_t pal[256];
2395 int n = 1 << colorMap->getBits();
2397 for(t=0;t<256;t++) {
2399 colorMap->getRGB(pixBuf, &rgb);
2400 pal[t].r = (unsigned char)(colToByte(rgb.r));
2401 pal[t].g = (unsigned char)(colToByte(rgb.g));
2402 pal[t].b = (unsigned char)(colToByte(rgb.b));
2403 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2405 for (y = 0; y < height; ++y) {
2406 for (x = 0; x < width; ++x) {
2407 imgStr->getPixel(pixBuf);
2408 pic[width*y+x] = pal[pixBuf[0]];
2409 if(maskColors && *maskColors==pixBuf[0]) {
2410 pic[width*y+x].a = 0;
2415 if(maskWidth < width && maskHeight < height) {
2416 for(y = 0; y < height; y++) {
2417 for (x = 0; x < width; x++) {
2418 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2422 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2423 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2424 double dx = width / (double)maskWidth;
2425 double dy = height / (double)maskHeight;
2427 for(y = 0; y < maskHeight; y++) {
2429 for (x = 0; x < maskWidth; x++) {
2430 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2431 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2439 height = maskHeight;
2442 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2446 if(maskbitmap) free(maskbitmap);
2451 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2452 int width, int height, GBool invert,
2455 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2456 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2457 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2460 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2461 int width, int height, GfxImageColorMap *colorMap,
2462 int *maskColors, GBool inlineImg)
2464 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2465 colorMap?"colorMap":"no colorMap",
2466 maskColors?"maskColors":"no maskColors",
2468 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2469 colorMap?"colorMap":"no colorMap",
2470 maskColors?"maskColors":"no maskColors",
2473 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2474 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2475 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2478 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2479 int width, int height,
2480 GfxImageColorMap *colorMap,
2481 Stream *maskStr, int maskWidth, int maskHeight,
2484 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2485 colorMap?"colorMap":"no colorMap",
2486 maskWidth, maskHeight);
2487 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2488 colorMap?"colorMap":"no colorMap",
2489 maskWidth, maskHeight);
2491 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2492 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2493 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2496 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2497 int width, int height,
2498 GfxImageColorMap *colorMap,
2500 int maskWidth, int maskHeight,
2501 GfxImageColorMap *maskColorMap)
2503 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2504 colorMap?"colorMap":"no colorMap",
2505 maskWidth, maskHeight);
2506 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2507 colorMap?"colorMap":"no colorMap",
2508 maskWidth, maskHeight);
2510 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2511 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2512 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2515 void GFXOutputDev::stroke(GfxState *state)
2519 GfxPath * path = state->getPath();
2520 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2521 strokeGfxline(state, line, 0);
2525 void GFXOutputDev::fill(GfxState *state)
2527 gfxcolor_t col = getFillColor(state);
2528 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2530 GfxPath * path = state->getPath();
2531 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2532 if(!config_disable_polygon_conversion) {
2533 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2537 fillGfxLine(state, line, 0);
2541 void GFXOutputDev::eoFill(GfxState *state)
2543 gfxcolor_t col = getFillColor(state);
2544 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2546 GfxPath * path = state->getPath();
2547 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2548 fillGfxLine(state, line, 1);
2553 static const char* dirseparator()
2562 void addGlobalFont(const char*filename)
2564 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2565 memset(f, 0, sizeof(fontfile_t));
2566 f->filename = filename;
2567 int len = strlen(filename);
2568 char*r1 = strrchr((char*)filename, '/');
2569 char*r2 = strrchr((char*)filename, '\\');
2577 msg("<verbose> Adding font \"%s\".", filename);
2578 if(global_fonts_next) {
2579 global_fonts_next->next = f;
2580 global_fonts_next = global_fonts_next->next;
2582 global_fonts_next = global_fonts = f;
2586 void addGlobalLanguageDir(const char*dir)
2588 msg("<notice> Adding %s to language pack directories", dir);
2591 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2592 strcpy(config_file, dir);
2593 strcat(config_file, dirseparator());
2594 strcat(config_file, "add-to-xpdfrc");
2596 fi = fopen(config_file, "rb");
2598 msg("<error> Could not open %s", config_file);
2601 globalParams->parseFile(new GString(config_file), fi);
2605 void addGlobalFontDir(const char*dirname)
2607 #ifdef HAVE_DIRENT_H
2608 DIR*dir = opendir(dirname);
2610 msg("<warning> Couldn't open directory %s", dirname);
2616 ent = readdir (dir);
2620 char*name = ent->d_name;
2626 if(!strncasecmp(&name[l-4], ".pfa", 4))
2628 if(!strncasecmp(&name[l-4], ".pfb", 4))
2630 if(!strncasecmp(&name[l-4], ".ttf", 4))
2633 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2634 strcpy(fontname, dirname);
2635 strcat(fontname, dirseparator());
2636 strcat(fontname, name);
2637 addGlobalFont(fontname);
2641 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2644 msg("<warning> No dirent.h");
2648 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2649 GfxColorSpace *blendingColorSpace,
2650 GBool isolated, GBool knockout,
2653 const char*colormodename = "";
2655 if(blendingColorSpace) {
2656 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2658 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);
2659 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);
2661 //states[statepos].createsoftmask |= forSoftMask;
2662 states[statepos].createsoftmask = forSoftMask;
2663 states[statepos].transparencygroup = !forSoftMask;
2664 states[statepos].isolated = isolated;
2666 states[statepos].olddevice = this->device;
2667 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2668 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2670 gfxdevice_record_init(this->device, 0);
2672 /*if(!forSoftMask) { ////???
2673 state->setFillOpacity(0.0);
2678 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2681 gfxdevice_t*r = this->device;
2683 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2685 this->device = states[statepos].olddevice;
2687 msg("<error> Invalid state nesting");
2689 states[statepos].olddevice = 0;
2691 gfxresult_t*recording = r->finish(r);
2693 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2694 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2696 if(states[statepos].createsoftmask) {
2697 states[statepos-1].softmaskrecording = recording;
2699 states[statepos-1].grouprecording = recording;
2702 states[statepos].createsoftmask = 0;
2703 states[statepos].transparencygroup = 0;
2707 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2709 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2710 "colordodge","colorburn","hardlight","softlight","difference",
2711 "exclusion","hue","saturation","color","luminosity"};
2713 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2714 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2716 if(state->getBlendMode() == gfxBlendNormal)
2717 infofeature("transparency groups");
2720 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2721 warnfeature(buffer, 0);
2724 gfxresult_t*grouprecording = states[statepos].grouprecording;
2726 int blendmode = state->getBlendMode();
2727 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2728 int alpha = (int)(state->getFillOpacity()*255);
2729 if(blendmode == gfxBlendMultiply && alpha>200)
2732 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2733 gfxdevice_ops_init(&ops, this->device, alpha);
2734 gfxresult_record_replay(grouprecording, &ops);
2737 grouprecording->destroy(grouprecording);
2739 states[statepos].grouprecording = 0;
2742 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2744 if(states[statepos].softmask) {
2745 /* shouldn't happen, but *does* happen */
2746 clearSoftMask(state);
2749 /* alpha = 1: retrieve mask values from alpha layer
2750 alpha = 0: retrieve mask values from luminance */
2752 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2753 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2754 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2755 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2757 infofeature("soft masks");
2759 warnfeature("soft masks from alpha channel",0);
2761 if(states[statepos].olddevice) {
2762 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2765 states[statepos].olddevice = this->device;
2766 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2767 gfxdevice_record_init(this->device, 0);
2769 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2771 states[statepos].softmask = 1;
2772 states[statepos].softmask_alpha = alpha;
2775 static inline Guchar div255(int x) {
2776 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2779 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2781 if(c < min) c = min;
2782 if(c > max) c = max;
2786 void GFXOutputDev::clearSoftMask(GfxState *state)
2788 if(!states[statepos].softmask)
2790 states[statepos].softmask = 0;
2791 dbg("clearSoftMask statepos=%d", statepos);
2792 msg("<verbose> clearSoftMask statepos=%d", statepos);
2794 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2795 msg("<error> Error in softmask/tgroup ordering");
2799 gfxresult_t*mask = states[statepos].softmaskrecording;
2800 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2801 this->device = states[statepos].olddevice;
2803 /* get outline of all objects below the soft mask */
2804 gfxdevice_t uniondev;
2805 gfxdevice_union_init(&uniondev, 0);
2806 gfxresult_record_replay(below, &uniondev);
2807 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2808 uniondev.finish(&uniondev);
2809 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2810 gfxline_free(belowoutline);belowoutline=0;
2812 this->device->startclip(this->device, belowoutline);
2813 gfxresult_record_replay(below, this->device);
2814 gfxresult_record_replay(mask, this->device);
2815 this->device->endclip(this->device);
2818 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2819 if(width<=0 || height<=0)
2822 gfxdevice_t belowrender;
2823 gfxdevice_render_init(&belowrender);
2824 if(states[statepos+1].isolated) {
2825 belowrender.setparameter(&belowrender, "fillwhite", "1");
2827 belowrender.setparameter(&belowrender, "antialize", "2");
2828 belowrender.startpage(&belowrender, width, height);
2829 gfxresult_record_replay(below, &belowrender);
2830 belowrender.endpage(&belowrender);
2831 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2832 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2833 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2835 gfxdevice_t maskrender;
2836 gfxdevice_render_init(&maskrender);
2837 maskrender.startpage(&maskrender, width, height);
2838 gfxresult_record_replay(mask, &maskrender);
2839 maskrender.endpage(&maskrender);
2840 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2841 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2843 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2844 msg("<fatal> Internal error in mask drawing");
2849 for(y=0;y<height;y++) {
2850 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2851 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2852 for(x=0;x<width;x++) {
2854 if(states[statepos].softmask_alpha) {
2857 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2860 l2->a = div255(alpha*l2->a);
2862 /* DON'T premultiply alpha- this is done by fillbitmap,
2863 depending on the output device */
2864 //l2->r = div255(alpha*l2->r);
2865 //l2->g = div255(alpha*l2->g);
2866 //l2->b = div255(alpha*l2->b);
2872 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2875 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2876 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2878 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2880 mask->destroy(mask);
2881 below->destroy(below);
2882 maskresult->destroy(maskresult);
2883 belowresult->destroy(belowresult);
2884 states[statepos].softmaskrecording = 0;
2889 // public: ~MemCheck()
2891 // delete globalParams;globalParams=0;
2892 // Object::memCheck(stderr);
2893 // gMemReport(stderr);