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) {
451 for(t=0;t<set->nfont;t++) {
452 char*fcfamily=0,*fcstyle=0,*filename=0;
453 FcBool scalable=FcFalse, outline=FcFalse;
454 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
455 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
456 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
457 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
458 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
459 if(scalable && outline) {
460 msg("<trace> %s (%s) -> %s", fcfamily, fcstyle, filename);
463 set = FcConfigGetFonts(config, FcSetApplication);
468 char*family = strdup(name);
470 char*dash = strchr(family, '-');
471 if(!dash) dash = strchr(family, ',');
473 FcPattern*pattern = 0;
477 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
478 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
480 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
481 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
485 FcConfigSubstitute(0, pattern, FcMatchPattern);
486 FcDefaultSubstitute(pattern);
488 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
491 for(t=0;t<set->nfont;t++) {
492 FcPattern*match = set->fonts[t];
493 //FcPattern*match = FcFontMatch(0, pattern, &result);
494 if(fc_ismatch(match, family, style)) {
496 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
497 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
500 //FcPatternDestroy(match);
501 msg("<debug> fontconfig: returning filename %s", filename);
503 FcPatternDestroy(pattern);
504 FcFontSetDestroy(set);
505 return filename?strdup(filename):0;
510 FcPatternDestroy(pattern);
511 FcFontSetDestroy(set);
518 static DisplayFontParamKind detectFontType(const char*filename)
520 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
521 return displayFontTT;
522 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
523 return displayFontT1;
524 return displayFontTT;
527 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
529 msg("<verbose> looking for font %s", fontName->getCString());
531 char*name = fontName->getCString();
533 /* see if it is a pdf standard font */
535 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
536 if(!strcmp(name, pdf2t1map[t].pdffont)) {
537 if(!pdf2t1map[t].fullfilename) {
538 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
539 if(!pdf2t1map[t].fullfilename) {
540 msg("<error> Couldn't save default font- is the Temp Directory writable?");
542 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
545 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
546 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
551 int bestlen = 0x7fffffff;
552 const char*bestfilename = 0;
554 #ifndef HAVE_FONTCONFIG
555 /* if we don't have fontconfig, try a simple filename-comparison approach */
556 fontfile_t*f = global_fonts;
558 if(strstr(f->filename, name)) {
559 if(f->len < bestlen) {
561 bestfilename = f->filename;
568 /* if we didn't find anything up to now, try looking for the
569 font via fontconfig */
572 filename = fontconfig_searchForFont(name);
574 filename = strdup(bestfilename);
578 msg("<verbose> Font %s maps to %s\n", name, filename);
579 DisplayFontParamKind kind = detectFontType(filename);
580 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
581 if(kind == displayFontTT) {
582 dfp->tt.fileName = new GString(filename);
584 dfp->t1.fileName = new GString(filename);
589 msg("<verbose> Font %s not found\n", name);
590 return GlobalParams::getDisplayFont(fontName);
594 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
597 gfxglobals = new GFXOutputGlobals();
601 this->xref = doc->getXRef();
603 this->type3active = 0;
606 this->user_movex = 0;
607 this->user_movey = 0;
610 this->user_clipx1 = 0;
611 this->user_clipy1 = 0;
612 this->user_clipx2 = 0;
613 this->user_clipy2 = 0;
614 this->current_gfxfont = 0;
615 this->current_fontinfo = 0;
616 this->current_text_stroke = 0;
617 this->current_text_clip = 0;
618 this->outer_clip_box = 0;
619 this->config_bigchar=0;
620 this->config_convertgradients=1;
621 this->config_break_on_warning=0;
622 this->config_remapunicode=0;
623 this->config_transparent=0;
624 this->config_extrafontdata = 0;
625 this->config_drawonlyshapes = 0;
626 this->config_disable_polygon_conversion = 0;
627 this->config_multiply = 1;
628 this->config_linkdatafile = 0;
632 memset(states, 0, sizeof(states));
635 void GFXOutputDev::setParameter(const char*key, const char*value)
637 if(!strcmp(key,"breakonwarning")) {
638 this->config_break_on_warning = atoi(value);
639 } else if(!strcmp(key,"remapunicode")) {
640 this->config_remapunicode = atoi(value);
641 } else if(!strcmp(key,"transparent")) {
642 this->config_transparent = atoi(value);
643 } else if(!strcmp(key,"drawonlyshapes")) {
644 this->config_drawonlyshapes = atoi(value);
645 } else if(!strcmp(key,"extrafontdata")) {
646 this->config_extrafontdata = atoi(value);
647 } else if(!strcmp(key,"linkdatafile")) {
648 this->config_linkdatafile = strdup(value);
649 } else if(!strcmp(key,"convertgradients")) {
650 this->config_convertgradients = atoi(value);
651 } else if(!strcmp(key,"multiply")) {
652 this->config_multiply = atoi(value);
653 if(this->config_multiply<1)
654 this->config_multiply=1;
655 } else if(!strcmp(key,"disable_polygon_conversion")) {
656 this->config_disable_polygon_conversion = atoi(value);
660 void GFXOutputDev::setDevice(gfxdevice_t*dev)
665 void GFXOutputDev::setMove(int x,int y)
667 this->user_movex = x;
668 this->user_movey = y;
671 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
673 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
674 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
676 this->user_clipx1 = x1;
677 this->user_clipy1 = y1;
678 this->user_clipx2 = x2;
679 this->user_clipy2 = y2;
682 static char*getFontName(GfxFont*font)
685 GString*gstr = font->getName();
686 char* fname = gstr==0?0:gstr->getCString();
690 sprintf(buf, "UFONT%d", r->num);
691 fontid = strdup(buf);
693 fontid = strdup(fname);
697 char* plus = strchr(fontid, '+');
698 if(plus && plus < &fontid[strlen(fontid)-1]) {
699 fontname = strdup(plus+1);
701 fontname = strdup(fontid);
707 static void dumpFontInfo(const char*loglevel, GfxFont*font);
708 static int lastdumps[1024];
709 static int lastdumppos = 0;
714 static void showFontError(GfxFont*font, int nr)
718 for(t=0;t<lastdumppos;t++)
719 if(lastdumps[t] == r->num)
723 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
724 lastdumps[lastdumppos++] = r->num;
726 msg("<warning> The following font caused problems:");
728 msg("<warning> The following font caused problems (substituting):");
730 msg("<warning> The following Type 3 Font will be rendered as graphics:");
731 dumpFontInfo("<warning>", font);
734 static void dumpFontInfo(const char*loglevel, GfxFont*font)
736 char* id = getFontID(font);
737 char* name = getFontName(font);
738 Ref* r=font->getID();
739 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
741 GString*gstr = font->getTag();
743 msg("%s| Tag: %s", loglevel, id);
745 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
747 GfxFontType type=font->getType();
749 case fontUnknownType:
750 msg("%s| Type: unknown",loglevel);
753 msg("%s| Type: 1",loglevel);
756 msg("%s| Type: 1C",loglevel);
759 msg("%s| Type: 3",loglevel);
762 msg("%s| Type: TrueType",loglevel);
765 msg("%s| Type: CIDType0",loglevel);
768 msg("%s| Type: CIDType0C",loglevel);
771 msg("%s| Type: CIDType2",loglevel);
776 GBool embedded = font->getEmbeddedFontID(&embRef);
778 if(font->getEmbeddedFontName()) {
779 embeddedName = font->getEmbeddedFontName()->getCString();
782 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
784 gstr = font->getExtFontFile();
786 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
788 // Get font descriptor flags.
789 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
790 if(font->isSerif()) msg("%s| is serif", loglevel);
791 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
792 if(font->isItalic()) msg("%s| is italic", loglevel);
793 if(font->isBold()) msg("%s| is bold", loglevel);
799 //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");}
800 //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");}
802 void dump_outline(gfxline_t*line)
804 /*gfxbbox_t*r = gfxline_isrectangle(line);
806 printf("is not a rectangle\n");
808 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
812 if(line->type == gfx_moveTo) {
813 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
814 } else if(line->type == gfx_lineTo) {
815 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
816 } else if(line->type == gfx_splineTo) {
817 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
823 void gfxPath_dump(GfxPath*path)
825 int num = path->getNumSubpaths();
828 for(t = 0; t < num; t++) {
829 GfxSubpath *subpath = path->getSubpath(t);
830 int subnum = subpath->getNumPoints();
832 for(s=0;s<subnum;s++) {
833 double x=subpath->getX(s);
834 double y=subpath->getY(s);
835 if(s==0 && !subpath->getCurve(s)) {
836 printf("M %f %f\n", x, y);
837 } else if(s==0 && subpath->getCurve(s)) {
838 printf("E %f %f\n", x, y);
839 } else if(subpath->getCurve(s)) {
840 printf("C %f %f\n", x, y);
842 printf("T %f %f\n", x, y);
848 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
850 int num = path->getNumSubpaths();
853 double lastx=0,lasty=0,posx=0,posy=0;
856 msg("<warning> empty path");
860 gfxdrawer_target_gfxline(&draw);
862 for(t = 0; t < num; t++) {
863 GfxSubpath *subpath = path->getSubpath(t);
864 int subnum = subpath->getNumPoints();
865 double bx=0,by=0,cx=0,cy=0;
867 for(s=0;s<subnum;s++) {
870 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
873 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
874 draw.lineTo(&draw, lastx, lasty);
876 draw.moveTo(&draw, x,y);
881 } else if(subpath->getCurve(s) && cpos==0) {
885 } else if(subpath->getCurve(s) && cpos==1) {
893 draw.lineTo(&draw, x,y);
895 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
902 /* fix non-closed lines */
903 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
904 draw.lineTo(&draw, lastx, lasty);
906 gfxline_t*result = (gfxline_t*)draw.result(&draw);
908 gfxline_optimize(result);
913 GBool GFXOutputDev::useTilingPatternFill()
915 infofeature("tiled patterns");
916 // if(config_convertgradients)
920 GBool GFXOutputDev::useShadedFills()
922 infofeature("shaded fills");
923 if(config_convertgradients)
928 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
930 state->transform(x,y,nx,ny);
931 *nx += user_movex + clipmovex;
932 *ny += user_movey + clipmovey;
936 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
937 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
938 int paintType, Dict *resDict,
939 double *mat, double *bbox,
940 int x0, int y0, int x1, int y1,
941 double xStep, double yStep)
943 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
944 int paintType, Dict *resDict,
945 double *mat, double *bbox,
946 int x0, int y0, int x1, int y1,
947 double xStep, double yStep)
950 msg("<debug> tilingPatternFill");
951 infofeature("tiling pattern fills");
954 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
956 msg("<error> functionShadedFill not supported yet");
957 infofeature("function shaded fills");
960 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
964 colspace->getRGB(col, &rgb);
965 c.r = colToByte(rgb.r);
966 c.g = colToByte(rgb.g);
967 c.b = colToByte(rgb.b);
972 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
974 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
975 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
978 this->transformXY(state, x0,y0, &x0,&y0);
979 this->transformXY(state, x1,y1, &x1,&y1);
980 this->transformXY(state, x2,y2, &x2,&y2);
985 shading->getColor(0.0, &color0);
986 shading->getColor(0.5, &color1);
987 shading->getColor(1.0, &color2);
989 GfxColorSpace* colspace = shading->getColorSpace();
991 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
992 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
993 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
994 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
995 infofeature("radial shaded fills");
997 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1001 g[0].color = col2col(colspace, &color0);
1002 g[1].color = col2col(colspace, &color1);
1003 g[2].color = col2col(colspace, &color2);
1008 gfxbbox_t b = states[statepos].clipbbox;
1009 gfxline_t p1,p2,p3,p4,p5;
1010 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1011 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1012 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1013 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1014 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1017 //m.m00 = (x3-x0); m.m10 = (x1-x0);
1018 //m.m01 = (y3-y0); m.m11 = (y1-y0);
1019 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1020 m.m00 = (x1-x0); m.m10 = (x2-x0);
1021 m.m01 = (y1-y0); m.m11 = (y2-y0);
1025 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1029 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1032 shading->getCoords(&x0,&y0,&x1,&y1);
1033 this->transformXY(state, x0,y0,&x0,&y0);
1034 this->transformXY(state, x1,y1,&x1,&y1);
1039 shading->getColor(0.0, &color0);
1040 shading->getColor(0.5, &color1);
1041 shading->getColor(1.0, &color2);
1043 GfxColorSpace* colspace = shading->getColorSpace();
1045 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1046 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1047 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1048 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1050 infofeature("axial shaded fills");
1052 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1056 g[0].color = col2col(colspace, &color0);
1057 g[1].color = col2col(colspace, &color1);
1058 g[2].color = col2col(colspace, &color2);
1063 double xMin,yMin,xMax,yMax;
1064 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1065 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1066 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1069 xMin = 1024; yMin = 1024;
1071 gfxbbox_t b = states[statepos].clipbbox;
1072 gfxline_t p1,p2,p3,p4,p5;
1073 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1074 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1075 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1076 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1077 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1079 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1080 the middle of the two control points */
1082 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1083 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1084 m.tx = (x0 + x1)/2 - 0.5;
1085 m.ty = (y0 + y1)/2 - 0.5;
1087 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1093 GBool GFXOutputDev::useDrawForm()
1095 infofeature("forms");
1098 void GFXOutputDev::drawForm(Ref id)
1100 msg("<error> drawForm not implemented");
1102 GBool GFXOutputDev::needNonText()
1106 void GFXOutputDev::endPage()
1108 msg("<verbose> endPage (GfxOutputDev)");
1109 if(outer_clip_box) {
1110 device->endclip(device);
1113 /* notice: we're not fully done yet with this page- there might still be
1114 a few calls to drawLink() yet to come */
1117 static inline double sqr(double x) {return x*x;}
1119 #define STROKE_FILL 1
1120 #define STROKE_CLIP 2
1121 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1123 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1124 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1125 double miterLimit = state->getMiterLimit();
1126 double width = state->getTransformedLineWidth();
1129 double opaq = state->getStrokeOpacity();
1131 state->getFillRGB(&rgb);
1133 state->getStrokeRGB(&rgb);
1135 col.r = colToByte(rgb.r);
1136 col.g = colToByte(rgb.g);
1137 col.b = colToByte(rgb.b);
1138 col.a = (unsigned char)(opaq*255);
1140 gfx_capType capType = gfx_capRound;
1141 if(lineCap == 0) capType = gfx_capButt;
1142 else if(lineCap == 1) capType = gfx_capRound;
1143 else if(lineCap == 2) capType = gfx_capSquare;
1144 else msg("<error> Invalid line cap type");
1146 gfx_joinType joinType = gfx_joinRound;
1147 if(lineJoin == 0) joinType = gfx_joinMiter;
1148 else if(lineJoin == 1) joinType = gfx_joinRound;
1149 else if(lineJoin == 2) joinType = gfx_joinBevel;
1150 else msg("<error> Invalid line join type");
1152 gfxline_t*line2 = 0;
1154 int dashLength = states[statepos].dashLength;
1155 double*dashPattern = states[statepos].dashPattern;
1156 double dashStart = states[statepos].dashStart;
1157 if(dashLength && dashPattern) {
1158 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1161 /* try to find out how much the transformation matrix would
1162 stretch the dashes, and factor that into the dash lengths.
1163 This is not the entirely correct approach- it would be
1164 better to first convert the path to an unscaled version,
1165 then apply dashing, and then transform the path using
1166 the current transformation matrix. However there are few
1167 PDFs which actually stretch a dashed path in a non-orthonormal
1169 double tx1, ty1, tx2, ty2, tx3, ty3;
1170 this->transformXY(state, 0, 0, &tx1, &ty1);
1171 this->transformXY(state, 0, 1, &tx2, &ty2);
1172 this->transformXY(state, 1, 0, &tx3, &ty3);
1173 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1174 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1176 warnfeature("non-ortogonally dashed strokes", 0);
1177 double f = (d1+d2)/2;
1179 msg("<trace> %d dashes", dashLength);
1180 msg("<trace> | phase: %f", dashStart);
1181 for(t=0;t<dashLength;t++) {
1182 dash[t] = (float)dashPattern[t] * f;
1186 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1188 dash[dashLength] = -1;
1189 if(getLogLevel() >= LOGLEVEL_TRACE) {
1193 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1197 msg("<trace> After dashing:");
1200 if(getLogLevel() >= LOGLEVEL_TRACE) {
1201 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1203 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1204 lineCap==0?"butt": (lineCap==1?"round":"square"),
1206 col.r,col.g,col.b,col.a
1211 if(flags&STROKE_FILL) {
1212 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1213 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1214 if(getLogLevel() >= LOGLEVEL_TRACE) {
1215 dump_outline(gfxline);
1218 msg("<warning> Empty polygon (resulting from stroked line)");
1220 if(flags&STROKE_CLIP) {
1221 device->startclip(device, gfxline);
1222 states[statepos].clipping++;
1224 device->fill(device, gfxline, &col);
1226 gfxline_free(gfxline);
1227 gfxpoly_destroy(poly);
1229 if(flags&STROKE_CLIP)
1230 msg("<error> Stroke&clip not supported at the same time");
1231 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1235 gfxline_free(line2);
1238 gfxcolor_t getFillColor(GfxState * state)
1241 double opaq = state->getFillOpacity();
1242 state->getFillRGB(&rgb);
1244 col.r = colToByte(rgb.r);
1245 col.g = colToByte(rgb.g);
1246 col.b = colToByte(rgb.b);
1247 col.a = (unsigned char)(opaq*255);
1251 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1253 gfxcolor_t col = getFillColor(state);
1255 if(getLogLevel() >= LOGLEVEL_TRACE) {
1256 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1259 device->fill(device, line, &col);
1262 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1264 if(getLogLevel() >= LOGLEVEL_TRACE) {
1265 msg("<trace> %sclip", evenodd?"eo":"");
1268 gfxbbox_t bbox = gfxline_getbbox(line);
1269 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1271 device->startclip(device, line);
1272 states[statepos].clipping++;
1275 void GFXOutputDev::clip(GfxState *state)
1277 GfxPath * path = state->getPath();
1278 msg("<trace> clip");
1279 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1280 if(!config_disable_polygon_conversion) {
1281 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1285 clipToGfxLine(state, line, 0);
1289 void GFXOutputDev::eoClip(GfxState *state)
1291 GfxPath * path = state->getPath();
1292 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1293 clipToGfxLine(state, line, 1);
1296 void GFXOutputDev::clipToStrokePath(GfxState *state)
1298 GfxPath * path = state->getPath();
1299 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1301 if(getLogLevel() >= LOGLEVEL_TRACE) {
1302 double width = state->getTransformedLineWidth();
1303 msg("<trace> cliptostrokepath width=%f", width);
1307 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1311 void GFXOutputDev::finish()
1313 if(outer_clip_box) {
1315 device->endclip(device);
1321 GFXOutputDev::~GFXOutputDev()
1325 GBool GFXOutputDev::upsideDown()
1329 GBool GFXOutputDev::useDrawChar()
1334 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1335 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1337 static char tmp_printstr[4096];
1338 char* makeStringPrintable(char*str)
1340 int len = strlen(str);
1347 for(t=0;t<len;t++) {
1352 tmp_printstr[t] = c;
1355 tmp_printstr[len++] = '.';
1356 tmp_printstr[len++] = '.';
1357 tmp_printstr[len++] = '.';
1359 tmp_printstr[len] = 0;
1360 return tmp_printstr;
1362 void GFXOutputDev::updateFontMatrix(GfxState*state)
1364 double* ctm = state->getCTM();
1365 double fontSize = state->getFontSize();
1366 double*textMat = state->getTextMat();
1368 /* taking the absolute value of horizScaling seems to be required for
1369 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1370 double hscale = fabs(state->getHorizScaling());
1372 // from xpdf-3.02/SplashOutputDev:updateFont
1373 double mm11 = textMat[0] * fontSize * hscale;
1374 double mm12 = textMat[1] * fontSize * hscale;
1375 double mm21 = textMat[2] * fontSize;
1376 double mm22 = textMat[3] * fontSize;
1378 // multiply with ctm, like state->getFontTransMat() does
1379 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1380 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1381 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1382 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1383 this->current_font_matrix.tx = 0;
1384 this->current_font_matrix.ty = 0;
1387 void GFXOutputDev::beginString(GfxState *state, GString *s)
1389 int render = state->getRender();
1390 if(current_text_stroke) {
1391 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1393 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1396 static gfxline_t* mkEmptyGfxShape(double x, double y)
1398 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1399 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1403 static char isValidUnicode(int c)
1405 if(c>=32 && c<0x2fffe)
1410 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1411 double dx, double dy,
1412 double originX, double originY,
1413 CharCode charid, int nBytes, Unicode *_u, int uLen)
1415 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1416 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1420 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1422 int render = state->getRender();
1423 gfxcolor_t col = getFillColor(state);
1425 // check for invisible text -- this is used by Acrobat Capture
1426 if (render == RENDER_INVISIBLE) {
1428 if(!config_extrafontdata)
1432 GfxFont*font = state->getFont();
1434 if(font->getType() == fontType3) {
1435 /* type 3 chars are passed as graphics */
1436 msg("<debug> type3 char at %f/%f", x, y);
1440 Unicode u = uLen?(_u[0]):0;
1442 gfxmatrix_t m = this->current_font_matrix;
1443 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1444 //m.tx += originX; m.ty += originY;
1446 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d font=%08x",m.tx,m.ty,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
1448 if((render == RENDER_FILL && !config_drawonlyshapes) || render == RENDER_INVISIBLE) {
1449 int space = this->current_fontinfo->space_char;
1450 if(config_extrafontdata && space>=0 && m.m00 && !m.m01) {
1451 /* space char detection */
1452 if(last_char_gfxfont == current_gfxfont &&
1453 last_char_y == m.ty &&
1454 !last_char_was_space) {
1455 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1456 int space = this->current_fontinfo->space_char;
1457 if(m.tx - expected_x >= m.m00*64) {
1458 msg("<debug> There's a %f (%f) pixel gap between char %d and char %d, I'm inserting a space here",
1460 (m.tx-expected_x)/m.m00,
1461 last_char, glyphid);
1463 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1464 if(m2.tx < expected_x) m2.tx = expected_x;
1465 device->drawchar(device, current_gfxfont, space, &col, &m2);
1468 last_char_gfxfont = current_gfxfont;
1469 last_char = glyphid;
1472 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1474 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1476 msg("<debug> Drawing glyph %d as shape", charid);
1477 if(!gfxglobals->textmodeinfo) {
1478 msg("<notice> Some texts will be rendered as shape");
1479 gfxglobals->textmodeinfo = 1;
1481 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1482 gfxline_t*tglyph = gfxline_clone(glyph);
1483 gfxline_transform(tglyph, &m);
1484 if((render&3) != RENDER_INVISIBLE) {
1485 gfxline_t*add = gfxline_clone(tglyph);
1486 current_text_stroke = gfxline_append(current_text_stroke, add);
1488 if(render&RENDER_CLIP) {
1489 gfxline_t*add = gfxline_clone(tglyph);
1490 current_text_clip = gfxline_append(current_text_clip, add);
1491 if(!current_text_clip) {
1492 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1495 gfxline_free(tglyph);
1499 void GFXOutputDev::endString(GfxState *state)
1501 int render = state->getRender();
1502 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1504 if(current_text_stroke) {
1505 /* fillstroke and stroke text rendering objects we can process right
1506 now (as there may be texts of other rendering modes in this
1507 text object)- clipping objects have to wait until endTextObject,
1509 device->setparameter(device, "mark","TXT");
1510 if((render&3) == RENDER_FILL) {
1511 fillGfxLine(state, current_text_stroke, 0);
1512 gfxline_free(current_text_stroke);
1513 current_text_stroke = 0;
1514 } else if((render&3) == RENDER_FILLSTROKE) {
1515 fillGfxLine(state, current_text_stroke, 0);
1516 strokeGfxline(state, current_text_stroke,0);
1517 gfxline_free(current_text_stroke);
1518 current_text_stroke = 0;
1519 } else if((render&3) == RENDER_STROKE) {
1520 strokeGfxline(state, current_text_stroke,0);
1521 gfxline_free(current_text_stroke);
1522 current_text_stroke = 0;
1524 device->setparameter(device, "mark","");
1528 void GFXOutputDev::endTextObject(GfxState *state)
1530 int render = state->getRender();
1531 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1533 if(current_text_clip) {
1534 device->setparameter(device, "mark","TXT");
1535 clipToGfxLine(state, current_text_clip, 0);
1536 device->setparameter(device, "mark","");
1537 gfxline_free(current_text_clip);
1538 current_text_clip = 0;
1542 /* the logic seems to be as following:
1543 first, beginType3Char is called, with the charcode and the coordinates.
1544 if this function returns true, it already knew about the char and has now drawn it.
1545 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1546 called with some parameters.
1547 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1548 at the position first passed to beginType3Char). the char ends with endType3Char.
1550 The drawing operations between beginType3Char and endType3Char are somewhat different to
1551 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1552 color determines the color of a font)
1555 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1557 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1560 if(config_extrafontdata && current_fontinfo) {
1562 gfxmatrix_t m = this->current_font_matrix;
1563 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1564 m.m00*=INTERNAL_FONT_SIZE;
1565 m.m01*=INTERNAL_FONT_SIZE;
1566 m.m10*=INTERNAL_FONT_SIZE;
1567 m.m11*=INTERNAL_FONT_SIZE;
1569 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1570 msg("<error> Invalid charid %d for font", charid);
1573 gfxcolor_t col={0,0,0,0};
1574 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1575 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1579 /* the character itself is going to be passed using the draw functions */
1580 return gFalse; /* gTrue= is_in_cache? */
1583 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1585 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1588 void GFXOutputDev::endType3Char(GfxState *state)
1591 msg("<debug> endType3Char");
1594 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1596 this->currentpage = pageNum;
1598 int rot = doc->getPageRotate(1);
1599 gfxcolor_t white = {255,255,255,255};
1600 gfxcolor_t black = {255,0,0,0};
1602 gfxline_t clippath[5];
1604 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1605 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1606 Use CropBox, not MediaBox, as page size
1613 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1614 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1616 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1617 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1619 this->clipmovex = -(int)x1;
1620 this->clipmovey = -(int)y1;
1622 /* apply user clip box */
1623 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1624 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1625 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1626 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1627 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1628 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1630 x1 += this->clipmovex;
1631 y1 += this->clipmovey;
1632 x2 += this->clipmovex;
1633 y2 += this->clipmovey;
1636 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1638 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);
1640 msg("<verbose> page is rotated %d degrees", rot);
1642 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1643 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1644 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1645 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1646 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1647 device->startclip(device, clippath); outer_clip_box = 1;
1648 if(!config_transparent) {
1649 device->fill(device, clippath, &white);
1651 states[statepos].clipbbox.xmin = x1;
1652 states[statepos].clipbbox.ymin = x1;
1653 states[statepos].clipbbox.xmax = x2;
1654 states[statepos].clipbbox.ymax = y2;
1656 states[statepos].dashPattern = 0;
1657 states[statepos].dashLength = 0;
1658 states[statepos].dashStart = 0;
1660 this->last_char_gfxfont = 0;
1664 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1666 double x1, y1, x2, y2;
1667 gfxline_t points[5];
1670 msg("<debug> drawlink");
1672 link->getRect(&x1, &y1, &x2, &y2);
1673 cvtUserToDev(x1, y1, &x, &y);
1674 points[0].type = gfx_moveTo;
1675 points[0].x = points[4].x = x + user_movex + clipmovex;
1676 points[0].y = points[4].y = y + user_movey + clipmovey;
1677 points[0].next = &points[1];
1678 cvtUserToDev(x2, y1, &x, &y);
1679 points[1].type = gfx_lineTo;
1680 points[1].x = x + user_movex + clipmovex;
1681 points[1].y = y + user_movey + clipmovey;
1682 points[1].next = &points[2];
1683 cvtUserToDev(x2, y2, &x, &y);
1684 points[2].type = gfx_lineTo;
1685 points[2].x = x + user_movex + clipmovex;
1686 points[2].y = y + user_movey + clipmovey;
1687 points[2].next = &points[3];
1688 cvtUserToDev(x1, y2, &x, &y);
1689 points[3].type = gfx_lineTo;
1690 points[3].x = x + user_movex + clipmovex;
1691 points[3].y = y + user_movey + clipmovey;
1692 points[3].next = &points[4];
1693 cvtUserToDev(x1, y1, &x, &y);
1694 points[4].type = gfx_lineTo;
1695 points[4].x = x + user_movex + clipmovex;
1696 points[4].y = y + user_movey + clipmovey;
1699 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1700 points[0].x, points[0].y,
1701 points[1].x, points[1].y,
1702 points[2].x, points[2].y,
1703 points[3].x, points[3].y);
1705 if(getLogLevel() >= LOGLEVEL_TRACE) {
1706 dump_outline(points);
1709 LinkAction*action=link->getAction();
1712 const char*type = "-?-";
1715 msg("<trace> drawlink action=%d", action->getKind());
1716 switch(action->getKind())
1720 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1721 LinkDest *dest=NULL;
1722 if (ha->getDest()==NULL)
1723 dest=catalog->findDest(ha->getNamedDest());
1725 dest=ha->getDest()->copy();
1727 if (dest->isPageRef()){
1728 Ref pageref=dest->getPageRef();
1729 page=catalog->findPage(pageref.num,pageref.gen);
1731 else page=dest->getPageNum();
1732 sprintf(buf, "%d", page);
1740 LinkGoToR*l = (LinkGoToR*)action;
1741 GString*g = l->getFileName();
1743 s = strdup(g->getCString());
1745 /* if the GoToR link has no filename, then
1746 try to find a refernce in the *local*
1748 GString*g = l->getNamedDest();
1750 s = strdup(g->getCString());
1756 LinkNamed*l = (LinkNamed*)action;
1757 GString*name = l->getName();
1759 s = strdup(name->lowerCase()->getCString());
1760 named = name->getCString();
1763 if(strstr(s, "next") || strstr(s, "forward"))
1765 page = currentpage + 1;
1767 else if(strstr(s, "prev") || strstr(s, "back"))
1769 page = currentpage - 1;
1771 else if(strstr(s, "last") || strstr(s, "end"))
1773 if(this->page2page && this->num_pages) {
1774 page = this->page2page[this->num_pages-1];
1777 else if(strstr(s, "first") || strstr(s, "top"))
1785 case actionLaunch: {
1787 LinkLaunch*l = (LinkLaunch*)action;
1788 GString * str = new GString(l->getFileName());
1789 GString * params = l->getParams();
1791 str->append(params);
1792 s = strdup(str->getCString());
1799 LinkURI*l = (LinkURI*)action;
1800 GString*g = l->getURI();
1802 url = g->getCString();
1807 case actionUnknown: {
1809 LinkUnknown*l = (LinkUnknown*)action;
1814 msg("<error> Unknown link type!");
1819 if(!s) s = strdup("-?-");
1821 msg("<trace> drawlink s=%s", s);
1823 if(!gfxglobals->linkinfo && (page || s))
1825 msg("<notice> File contains links");
1826 gfxglobals->linkinfo = 1;
1832 for(t=1;t<=this->num_pages;t++) {
1833 if(this->page2page[t]==page) {
1843 sprintf(buf, "page%d", lpage);
1844 device->drawlink(device, points, buf);
1848 device->drawlink(device, points, s);
1849 if(this->config_linkdatafile) {
1850 FILE*fi = fopen(config_linkdatafile, "ab+");
1851 fprintf(fi, "%s\n", s);
1856 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1860 void GFXOutputDev::saveState(GfxState *state) {
1861 dbg("saveState %08x", state); dbgindent+=2;
1863 msg("<trace> saveState %08x", state);
1866 msg("<fatal> Too many nested states in pdf.");
1870 states[statepos].state = state;
1871 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1872 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1873 states[statepos].clipping = 0;
1874 states[statepos].olddevice = 0;
1875 states[statepos].clipbbox = states[statepos-1].clipbbox;
1877 states[statepos].dashPattern = states[statepos-1].dashPattern;
1878 states[statepos].dashStart = states[statepos-1].dashStart;
1879 states[statepos].dashLength = states[statepos-1].dashLength;
1882 void GFXOutputDev::restoreState(GfxState *state) {
1883 dbgindent-=2; dbg("restoreState %08x", state);
1886 msg("<fatal> Invalid restoreState");
1889 msg("<trace> restoreState %08x%s%s", state,
1890 states[statepos].softmask?" (end softmask)":"",
1891 states[statepos].clipping?" (end clipping)":"");
1892 if(states[statepos].softmask) {
1893 clearSoftMask(state);
1896 if(states[statepos].dashPattern) {
1897 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1898 free(states[statepos].dashPattern);
1899 states[statepos].dashPattern = 0;
1905 while(states[statepos].clipping) {
1906 device->endclip(device);
1907 states[statepos].clipping--;
1909 if(states[statepos].state!=state) {
1910 msg("<fatal> bad state nesting");
1913 for(t=0;t<=statepos;t++) {
1914 printf("%08x ", states[t].state);
1920 states[statepos].state=0;
1924 void GFXOutputDev::updateLineDash(GfxState *state)
1926 if(states[statepos].dashPattern &&
1927 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1928 free(states[statepos].dashPattern);
1929 states[statepos].dashPattern = 0;
1931 double *pattern = 0;
1934 state->getLineDash(&pattern, &dashLength, &dashStart);
1935 msg("<debug> updateLineDash, %d dashes", dashLength);
1937 states[statepos].dashPattern = 0;
1938 states[statepos].dashLength = 0;
1940 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1941 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1942 states[statepos].dashPattern = p;
1943 states[statepos].dashLength = dashLength;
1944 states[statepos].dashStart = dashStart;
1948 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1950 this->page2page = page2page;
1951 this->num_pages = num_pages;
1954 void GFXOutputDev::updateLineWidth(GfxState *state)
1956 double width = state->getTransformedLineWidth();
1959 void GFXOutputDev::updateLineCap(GfxState *state)
1961 int c = state->getLineCap();
1964 void GFXOutputDev::updateLineJoin(GfxState *state)
1966 int j = state->getLineJoin();
1969 void GFXOutputDev::updateFillColor(GfxState *state)
1972 double opaq = state->getFillOpacity();
1973 state->getFillRGB(&rgb);
1975 void GFXOutputDev::updateFillOpacity(GfxState *state)
1978 double opaq = state->getFillOpacity();
1979 state->getFillRGB(&rgb);
1980 dbg("update fillopaq %f", opaq);
1982 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1984 double opaq = state->getFillOpacity();
1985 dbg("update strokeopaq %f", opaq);
1987 void GFXOutputDev::updateFillOverprint(GfxState *state)
1989 double opaq = state->getFillOverprint();
1990 dbg("update filloverprint %f", opaq);
1992 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1994 double opaq = state->getStrokeOverprint();
1995 dbg("update strokeoverprint %f", opaq);
1997 void GFXOutputDev::updateTransfer(GfxState *state)
1999 dbg("update transfer");
2003 void GFXOutputDev::updateStrokeColor(GfxState *state)
2006 double opaq = state->getStrokeOpacity();
2007 state->getStrokeRGB(&rgb);
2010 void GFXOutputDev::updateFont(GfxState *state)
2012 GfxFont* gfxFont = state->getFont();
2016 char*id = getFontID(gfxFont);
2017 msg("<verbose> Updating font to %s", id);
2018 if(gfxFont->getType() == fontType3) {
2019 infofeature("Type3 fonts");
2020 if(!config_extrafontdata) {
2025 msg("<error> Internal Error: FontID is null");
2029 this->current_fontinfo = this->info->getFont(id);
2031 if(!this->current_fontinfo) {
2032 msg("<error> Internal Error: no fontinfo for font %s", id);
2035 if(!this->current_fontinfo->seen) {
2036 dumpFontInfo("<verbose>", gfxFont);
2039 current_gfxfont = this->current_fontinfo->getGfxFont();
2040 device->addfont(device, current_gfxfont);
2043 updateFontMatrix(state);
2046 #define SQR(x) ((x)*(x))
2048 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2050 if((newwidth<1 || newheight<1) ||
2051 (width<=newwidth || height<=newheight))
2053 unsigned char*newdata;
2055 newdata= (unsigned char*)malloc(newwidth*newheight);
2056 double fx = ((double)width)/newwidth;
2057 double fy = ((double)height)/newheight;
2059 int blocksize = (int)(8192/(fx*fy));
2060 int r = 8192*256/palettesize;
2061 for(x=0;x<newwidth;x++) {
2062 double ex = px + fx;
2063 int fromx = (int)px;
2065 int xweight1 = (int)((1-(px-fromx))*256);
2066 int xweight2 = (int)((ex-tox)*256);
2068 for(y=0;y<newheight;y++) {
2069 double ey = py + fy;
2070 int fromy = (int)py;
2072 int yweight1 = (int)((1-(py-fromy))*256);
2073 int yweight2 = (int)((ey-toy)*256);
2080 for(xx=fromx;xx<=tox;xx++)
2081 for(yy=fromy;yy<=toy;yy++) {
2082 int b = 1-data[width*yy+xx];
2084 if(xx==fromx) weight = (weight*xweight1)/256;
2085 if(xx==tox) weight = (weight*xweight2)/256;
2086 if(yy==fromy) weight = (weight*yweight1)/256;
2087 if(yy==toy) weight = (weight*yweight2)/256;
2090 //if(a) a=(palettesize-1)*r/blocksize;
2091 newdata[y*newwidth+x] = (a*blocksize)/r;
2099 #define IMAGE_TYPE_JPEG 0
2100 #define IMAGE_TYPE_LOSSLESS 1
2102 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2103 double x1,double y1,
2104 double x2,double y2,
2105 double x3,double y3,
2106 double x4,double y4, int type, int multiply)
2108 gfxcolor_t*newpic=0;
2110 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2111 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2113 gfxline_t p1,p2,p3,p4,p5;
2114 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2115 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2116 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2117 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2118 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2120 {p1.x = (int)(p1.x*20)/20.0;
2121 p1.y = (int)(p1.y*20)/20.0;
2122 p2.x = (int)(p2.x*20)/20.0;
2123 p2.y = (int)(p2.y*20)/20.0;
2124 p3.x = (int)(p3.x*20)/20.0;
2125 p3.y = (int)(p3.y*20)/20.0;
2126 p4.x = (int)(p4.x*20)/20.0;
2127 p4.y = (int)(p4.y*20)/20.0;
2128 p5.x = (int)(p5.x*20)/20.0;
2129 p5.y = (int)(p5.y*20)/20.0;
2133 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2134 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2136 m.tx = p1.x - 0.5*multiply;
2137 m.ty = p1.y - 0.5*multiply;
2140 img.data = (gfxcolor_t*)data;
2144 if(type == IMAGE_TYPE_JPEG)
2145 /* TODO: pass image_dpi to device instead */
2146 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2149 dev->fillbitmap(dev, &p1, &img, &m, 0);
2152 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2153 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2155 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2158 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2159 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2161 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2165 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2166 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2167 GBool inlineImg, int mask, int*maskColors,
2168 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2170 /* the code in this function is *old*. It's not pretty, but it works. */
2172 double x1,y1,x2,y2,x3,y3,x4,y4;
2173 ImageStream *imgStr;
2178 unsigned char* maskbitmap = 0;
2181 ncomps = colorMap->getNumPixelComps();
2182 bits = colorMap->getBits();
2187 unsigned char buf[8];
2188 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2190 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2191 imgMaskStr->reset();
2192 unsigned char pal[256];
2193 int n = 1 << colorMap->getBits();
2198 maskColorMap->getGray(pixBuf, &gray);
2199 pal[t] = colToByte(gray);
2201 for (y = 0; y < maskHeight; y++) {
2202 for (x = 0; x < maskWidth; x++) {
2203 imgMaskStr->getPixel(buf);
2204 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2209 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2210 imgMaskStr->reset();
2211 for (y = 0; y < maskHeight; y++) {
2212 for (x = 0; x < maskWidth; x++) {
2213 imgMaskStr->getPixel(buf);
2215 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2223 imgStr = new ImageStream(str, width, ncomps,bits);
2226 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2228 msg("<verbose> Ignoring %d by %d image", width, height);
2229 unsigned char buf[8];
2231 for (y = 0; y < height; ++y)
2232 for (x = 0; x < width; ++x) {
2233 imgStr->getPixel(buf);
2241 this->transformXY(state, 0, 1, &x1, &y1);
2242 this->transformXY(state, 0, 0, &x2, &y2);
2243 this->transformXY(state, 1, 0, &x3, &y3);
2244 this->transformXY(state, 1, 1, &x4, &y4);
2247 /* as type 3 bitmaps are antialized, we need to place them
2248 at integer coordinates, otherwise flash player's antializing
2249 will kick in and make everything blurry */
2250 x1 = (int)(x1);y1 = (int)(y1);
2251 x2 = (int)(x2);y2 = (int)(y2);
2252 x3 = (int)(x3);y3 = (int)(y3);
2253 x4 = (int)(x4);y4 = (int)(y4);
2256 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2258 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2259 gfxglobals->pbminfo = 1;
2262 msg("<verbose> drawing %d by %d masked picture", width, height);
2264 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2265 msg("<notice> File contains jpeg pictures");
2266 gfxglobals->jpeginfo = 1;
2270 unsigned char buf[8];
2272 unsigned char*pic = new unsigned char[width*height];
2273 gfxcolor_t pal[256];
2275 state->getFillRGB(&rgb);
2277 memset(pal,255,sizeof(pal));
2278 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2279 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2280 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2281 pal[0].a = 255; pal[1].a = 0;
2284 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2285 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2286 for (y = 0; y < height; ++y)
2287 for (x = 0; x < width; ++x)
2289 imgStr->getPixel(buf);
2292 pic[width*y+x] = buf[0];
2296 unsigned char*pic2 = 0;
2299 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2308 height = realheight;
2312 /* make a black/white palette */
2314 float r = 255./(float)(numpalette-1);
2316 for(t=0;t<numpalette;t++) {
2317 pal[t].r = colToByte(rgb.r);
2318 pal[t].g = colToByte(rgb.g);
2319 pal[t].b = colToByte(rgb.b);
2320 pal[t].a = (unsigned char)(t*r);
2325 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2326 for (y = 0; y < height; ++y) {
2327 for (x = 0; x < width; ++x) {
2328 pic2[width*y+x] = pal[pic[y*width+x]];
2331 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2335 if(maskbitmap) free(maskbitmap);
2341 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2342 gfxcolor_t*pic=new gfxcolor_t[width*height];
2343 for (y = 0; y < height; ++y) {
2344 for (x = 0; x < width; ++x) {
2345 imgStr->getPixel(pixBuf);
2346 colorMap->getRGB(pixBuf, &rgb);
2347 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2348 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2349 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2350 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2352 int x1 = x*maskWidth/width;
2353 int y1 = y*maskHeight/height;
2354 int x2 = (x+1)*maskWidth/width;
2355 int y2 = (y+1)*maskHeight/height;
2357 unsigned int alpha=0;
2358 unsigned int count=0;
2359 for(xx=x1;xx<x2;xx++)
2360 for(yy=y1;yy<y2;yy++) {
2361 alpha += maskbitmap[yy*maskWidth+xx];
2365 pic[width*y+x].a = alpha / count;
2367 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2372 if(str->getKind()==strDCT)
2373 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2375 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2378 if(maskbitmap) free(maskbitmap);
2381 gfxcolor_t*pic=new gfxcolor_t[width*height];
2382 gfxcolor_t pal[256];
2383 int n = 1 << colorMap->getBits();
2385 for(t=0;t<256;t++) {
2387 colorMap->getRGB(pixBuf, &rgb);
2389 {/*if(maskColors && *maskColors==t) {
2390 msg("<notice> Color %d is transparent", t);
2391 if (imgData->maskColors) {
2393 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2394 if (pix[i] < imgData->maskColors[2*i] ||
2395 pix[i] > imgData->maskColors[2*i+1]) {
2410 pal[t].r = (unsigned char)(colToByte(rgb.r));
2411 pal[t].g = (unsigned char)(colToByte(rgb.g));
2412 pal[t].b = (unsigned char)(colToByte(rgb.b));
2413 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2416 for (y = 0; y < height; ++y) {
2417 for (x = 0; x < width; ++x) {
2418 imgStr->getPixel(pixBuf);
2419 pic[width*y+x] = pal[pixBuf[0]];
2423 if(maskWidth < width && maskHeight < height) {
2424 for(y = 0; y < height; y++) {
2425 for (x = 0; x < width; x++) {
2426 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2430 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2431 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2432 double dx = width / maskWidth;
2433 double dy = height / maskHeight;
2435 for(y = 0; y < maskHeight; y++) {
2437 for (x = 0; x < maskWidth; x++) {
2438 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2439 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2447 height = maskHeight;
2450 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2454 if(maskbitmap) free(maskbitmap);
2459 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2460 int width, int height, GBool invert,
2463 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2464 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2465 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2468 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2469 int width, int height, GfxImageColorMap *colorMap,
2470 int *maskColors, GBool inlineImg)
2472 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2473 colorMap?"colorMap":"no colorMap",
2474 maskColors?"maskColors":"no maskColors",
2476 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2477 colorMap?"colorMap":"no colorMap",
2478 maskColors?"maskColors":"no maskColors",
2481 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2482 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2483 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2486 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2487 int width, int height,
2488 GfxImageColorMap *colorMap,
2489 Stream *maskStr, int maskWidth, int maskHeight,
2492 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2493 colorMap?"colorMap":"no colorMap",
2494 maskWidth, maskHeight);
2495 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2496 colorMap?"colorMap":"no colorMap",
2497 maskWidth, maskHeight);
2499 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2500 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2501 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2504 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2505 int width, int height,
2506 GfxImageColorMap *colorMap,
2508 int maskWidth, int maskHeight,
2509 GfxImageColorMap *maskColorMap)
2511 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2512 colorMap?"colorMap":"no colorMap",
2513 maskWidth, maskHeight);
2514 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2515 colorMap?"colorMap":"no colorMap",
2516 maskWidth, maskHeight);
2518 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2519 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2520 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2523 void GFXOutputDev::stroke(GfxState *state)
2527 GfxPath * path = state->getPath();
2528 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2529 strokeGfxline(state, line, 0);
2533 void GFXOutputDev::fill(GfxState *state)
2535 gfxcolor_t col = getFillColor(state);
2536 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2538 GfxPath * path = state->getPath();
2539 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2540 if(!config_disable_polygon_conversion) {
2541 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2545 fillGfxLine(state, line, 0);
2549 void GFXOutputDev::eoFill(GfxState *state)
2551 gfxcolor_t col = getFillColor(state);
2552 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2554 GfxPath * path = state->getPath();
2555 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2556 fillGfxLine(state, line, 1);
2561 static const char* dirseparator()
2570 void addGlobalFont(const char*filename)
2572 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2573 memset(f, 0, sizeof(fontfile_t));
2574 f->filename = filename;
2575 int len = strlen(filename);
2576 char*r1 = strrchr((char*)filename, '/');
2577 char*r2 = strrchr((char*)filename, '\\');
2585 msg("<verbose> Adding font \"%s\".", filename);
2586 if(global_fonts_next) {
2587 global_fonts_next->next = f;
2588 global_fonts_next = global_fonts_next->next;
2590 global_fonts_next = global_fonts = f;
2594 void addGlobalLanguageDir(const char*dir)
2596 msg("<notice> Adding %s to language pack directories", dir);
2599 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2600 strcpy(config_file, dir);
2601 strcat(config_file, dirseparator());
2602 strcat(config_file, "add-to-xpdfrc");
2604 fi = fopen(config_file, "rb");
2606 msg("<error> Could not open %s", config_file);
2609 globalParams->parseFile(new GString(config_file), fi);
2613 void addGlobalFontDir(const char*dirname)
2615 #ifdef HAVE_DIRENT_H
2616 DIR*dir = opendir(dirname);
2618 msg("<warning> Couldn't open directory %s", dirname);
2624 ent = readdir (dir);
2628 char*name = ent->d_name;
2634 if(!strncasecmp(&name[l-4], ".pfa", 4))
2636 if(!strncasecmp(&name[l-4], ".pfb", 4))
2638 if(!strncasecmp(&name[l-4], ".ttf", 4))
2641 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2642 strcpy(fontname, dirname);
2643 strcat(fontname, dirseparator());
2644 strcat(fontname, name);
2645 addGlobalFont(fontname);
2649 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2652 msg("<warning> No dirent.h");
2656 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2657 GfxColorSpace *blendingColorSpace,
2658 GBool isolated, GBool knockout,
2661 const char*colormodename = "";
2663 if(blendingColorSpace) {
2664 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2666 dbg("beginTransparencyGroup device=%08x %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2667 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);
2669 //states[statepos].createsoftmask |= forSoftMask;
2670 states[statepos].createsoftmask = forSoftMask;
2671 states[statepos].transparencygroup = !forSoftMask;
2672 states[statepos].isolated = isolated;
2674 states[statepos].olddevice = this->device;
2675 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2676 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2678 gfxdevice_record_init(this->device);
2680 /*if(!forSoftMask) { ////???
2681 state->setFillOpacity(0.0);
2686 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2689 gfxdevice_t*r = this->device;
2691 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2693 this->device = states[statepos].olddevice;
2695 msg("<error> Invalid state nesting");
2697 states[statepos].olddevice = 0;
2699 gfxresult_t*recording = r->finish(r);
2701 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2702 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2704 if(states[statepos].createsoftmask) {
2705 states[statepos-1].softmaskrecording = recording;
2707 states[statepos-1].grouprecording = recording;
2710 states[statepos].createsoftmask = 0;
2711 states[statepos].transparencygroup = 0;
2715 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2717 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2718 "colordodge","colorburn","hardlight","softlight","difference",
2719 "exclusion","hue","saturation","color","luminosity"};
2721 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2722 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2724 if(state->getBlendMode() == gfxBlendNormal)
2725 infofeature("transparency groups");
2728 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2729 warnfeature(buffer, 0);
2732 gfxresult_t*grouprecording = states[statepos].grouprecording;
2734 int blendmode = state->getBlendMode();
2735 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2736 int alpha = (int)(state->getFillOpacity()*255);
2737 if(blendmode == gfxBlendMultiply && alpha>200)
2740 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2741 gfxdevice_ops_init(&ops, this->device, alpha);
2742 gfxresult_record_replay(grouprecording, &ops);
2745 grouprecording->destroy(grouprecording);
2747 states[statepos].grouprecording = 0;
2750 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2752 if(states[statepos].softmask) {
2753 /* shouldn't happen, but *does* happen */
2754 clearSoftMask(state);
2757 /* alpha = 1: retrieve mask values from alpha layer
2758 alpha = 0: retrieve mask values from luminance */
2760 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2761 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2762 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2763 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2765 infofeature("soft masks");
2767 warnfeature("soft masks from alpha channel",0);
2769 if(states[statepos].olddevice) {
2770 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2773 states[statepos].olddevice = this->device;
2774 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2775 gfxdevice_record_init(this->device);
2777 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2779 states[statepos].softmask = 1;
2780 states[statepos].softmask_alpha = alpha;
2783 static inline Guchar div255(int x) {
2784 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2787 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2789 if(c < min) c = min;
2790 if(c > max) c = max;
2794 void GFXOutputDev::clearSoftMask(GfxState *state)
2796 if(!states[statepos].softmask)
2798 states[statepos].softmask = 0;
2799 dbg("clearSoftMask statepos=%d", statepos);
2800 msg("<verbose> clearSoftMask statepos=%d", statepos);
2802 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2803 msg("<error> Error in softmask/tgroup ordering");
2807 gfxresult_t*mask = states[statepos].softmaskrecording;
2808 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2809 this->device = states[statepos].olddevice;
2811 /* get outline of all objects below the soft mask */
2812 gfxdevice_t uniondev;
2813 gfxdevice_union_init(&uniondev, 0);
2814 gfxresult_record_replay(below, &uniondev);
2815 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2816 uniondev.finish(&uniondev);
2817 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2818 gfxline_free(belowoutline);belowoutline=0;
2820 this->device->startclip(this->device, belowoutline);
2821 gfxresult_record_replay(below, this->device);
2822 gfxresult_record_replay(mask, this->device);
2823 this->device->endclip(this->device);
2826 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2827 if(width<=0 || height<=0)
2830 gfxdevice_t belowrender;
2831 gfxdevice_render_init(&belowrender);
2832 if(states[statepos+1].isolated) {
2833 belowrender.setparameter(&belowrender, "fillwhite", "1");
2835 belowrender.setparameter(&belowrender, "antialize", "2");
2836 belowrender.startpage(&belowrender, width, height);
2837 gfxresult_record_replay(below, &belowrender);
2838 belowrender.endpage(&belowrender);
2839 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2840 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2841 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2843 gfxdevice_t maskrender;
2844 gfxdevice_render_init(&maskrender);
2845 maskrender.startpage(&maskrender, width, height);
2846 gfxresult_record_replay(mask, &maskrender);
2847 maskrender.endpage(&maskrender);
2848 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2849 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2851 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2852 msg("<fatal> Internal error in mask drawing");
2857 for(y=0;y<height;y++) {
2858 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2859 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2860 for(x=0;x<width;x++) {
2862 if(states[statepos].softmask_alpha) {
2865 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2868 l2->a = div255(alpha*l2->a);
2870 /* DON'T premultiply alpha- this is done by fillbitmap,
2871 depending on the output device */
2872 //l2->r = div255(alpha*l2->r);
2873 //l2->g = div255(alpha*l2->g);
2874 //l2->b = div255(alpha*l2->b);
2880 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2883 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2884 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2886 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2888 mask->destroy(mask);
2889 below->destroy(below);
2890 maskresult->destroy(maskresult);
2891 belowresult->destroy(belowresult);
2892 states[statepos].softmaskrecording = 0;
2897 // public: ~MemCheck()
2899 // delete globalParams;globalParams=0;
2900 // Object::memCheck(stderr);
2901 // gMemReport(stderr);