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;
1662 if(this->config_linkdatafile) {
1663 FILE*fi = fopen(config_linkdatafile, "ab+");
1664 fprintf(fi, "[page %d]\n", pageNum);
1670 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1672 double x1, y1, x2, y2;
1673 gfxline_t points[5];
1676 msg("<debug> drawlink");
1678 link->getRect(&x1, &y1, &x2, &y2);
1679 cvtUserToDev(x1, y1, &x, &y);
1680 points[0].type = gfx_moveTo;
1681 points[0].x = points[4].x = x + user_movex + clipmovex;
1682 points[0].y = points[4].y = y + user_movey + clipmovey;
1683 points[0].next = &points[1];
1684 cvtUserToDev(x2, y1, &x, &y);
1685 points[1].type = gfx_lineTo;
1686 points[1].x = x + user_movex + clipmovex;
1687 points[1].y = y + user_movey + clipmovey;
1688 points[1].next = &points[2];
1689 cvtUserToDev(x2, y2, &x, &y);
1690 points[2].type = gfx_lineTo;
1691 points[2].x = x + user_movex + clipmovex;
1692 points[2].y = y + user_movey + clipmovey;
1693 points[2].next = &points[3];
1694 cvtUserToDev(x1, y2, &x, &y);
1695 points[3].type = gfx_lineTo;
1696 points[3].x = x + user_movex + clipmovex;
1697 points[3].y = y + user_movey + clipmovey;
1698 points[3].next = &points[4];
1699 cvtUserToDev(x1, y1, &x, &y);
1700 points[4].type = gfx_lineTo;
1701 points[4].x = x + user_movex + clipmovex;
1702 points[4].y = y + user_movey + clipmovey;
1705 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1706 points[0].x, points[0].y,
1707 points[1].x, points[1].y,
1708 points[2].x, points[2].y,
1709 points[3].x, points[3].y);
1711 if(getLogLevel() >= LOGLEVEL_TRACE) {
1712 dump_outline(points);
1715 LinkAction*action=link->getAction();
1718 const char*type = "-?-";
1721 msg("<trace> drawlink action=%d", action->getKind());
1722 switch(action->getKind())
1726 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1727 LinkDest *dest=NULL;
1728 if (ha->getDest()==NULL)
1729 dest=catalog->findDest(ha->getNamedDest());
1731 dest=ha->getDest()->copy();
1733 if (dest->isPageRef()){
1734 Ref pageref=dest->getPageRef();
1735 page=catalog->findPage(pageref.num,pageref.gen);
1737 else page=dest->getPageNum();
1738 sprintf(buf, "%d", page);
1746 LinkGoToR*l = (LinkGoToR*)action;
1747 GString*g = l->getFileName();
1749 s = strdup(g->getCString());
1751 /* if the GoToR link has no filename, then
1752 try to find a refernce in the *local*
1754 GString*g = l->getNamedDest();
1756 s = strdup(g->getCString());
1762 LinkNamed*l = (LinkNamed*)action;
1763 GString*name = l->getName();
1765 s = strdup(name->lowerCase()->getCString());
1766 named = name->getCString();
1769 if(strstr(s, "next") || strstr(s, "forward"))
1771 page = currentpage + 1;
1773 else if(strstr(s, "prev") || strstr(s, "back"))
1775 page = currentpage - 1;
1777 else if(strstr(s, "last") || strstr(s, "end"))
1779 if(this->page2page && this->num_pages) {
1780 page = this->page2page[this->num_pages-1];
1783 else if(strstr(s, "first") || strstr(s, "top"))
1791 case actionLaunch: {
1793 LinkLaunch*l = (LinkLaunch*)action;
1794 GString * str = new GString(l->getFileName());
1795 GString * params = l->getParams();
1797 str->append(params);
1798 s = strdup(str->getCString());
1805 LinkURI*l = (LinkURI*)action;
1806 GString*g = l->getURI();
1808 url = g->getCString();
1813 case actionUnknown: {
1815 LinkUnknown*l = (LinkUnknown*)action;
1820 msg("<error> Unknown link type!");
1825 if(!s) s = strdup("-?-");
1827 msg("<trace> drawlink s=%s", s);
1829 if(!gfxglobals->linkinfo && (page || s))
1831 msg("<notice> File contains links");
1832 gfxglobals->linkinfo = 1;
1838 for(t=1;t<=this->num_pages;t++) {
1839 if(this->page2page[t]==page) {
1849 sprintf(buf, "page%d", lpage);
1850 device->drawlink(device, points, buf);
1854 device->drawlink(device, points, s);
1855 if(this->config_linkdatafile) {
1856 FILE*fi = fopen(config_linkdatafile, "ab+");
1857 fprintf(fi, "%s\n", s);
1862 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1866 void GFXOutputDev::saveState(GfxState *state) {
1867 dbg("saveState %08x", state); dbgindent+=2;
1869 msg("<trace> saveState %08x", state);
1872 msg("<fatal> Too many nested states in pdf.");
1876 states[statepos].state = state;
1877 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1878 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1879 states[statepos].clipping = 0;
1880 states[statepos].olddevice = 0;
1881 states[statepos].clipbbox = states[statepos-1].clipbbox;
1883 states[statepos].dashPattern = states[statepos-1].dashPattern;
1884 states[statepos].dashStart = states[statepos-1].dashStart;
1885 states[statepos].dashLength = states[statepos-1].dashLength;
1888 void GFXOutputDev::restoreState(GfxState *state) {
1889 dbgindent-=2; dbg("restoreState %08x", state);
1892 msg("<fatal> Invalid restoreState");
1895 msg("<trace> restoreState %08x%s%s", state,
1896 states[statepos].softmask?" (end softmask)":"",
1897 states[statepos].clipping?" (end clipping)":"");
1898 if(states[statepos].softmask) {
1899 clearSoftMask(state);
1902 if(states[statepos].dashPattern) {
1903 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1904 free(states[statepos].dashPattern);
1905 states[statepos].dashPattern = 0;
1911 while(states[statepos].clipping) {
1912 device->endclip(device);
1913 states[statepos].clipping--;
1915 if(states[statepos].state!=state) {
1916 msg("<fatal> bad state nesting");
1919 for(t=0;t<=statepos;t++) {
1920 printf("%08x ", states[t].state);
1926 states[statepos].state=0;
1930 void GFXOutputDev::updateLineDash(GfxState *state)
1932 if(states[statepos].dashPattern &&
1933 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1934 free(states[statepos].dashPattern);
1935 states[statepos].dashPattern = 0;
1937 double *pattern = 0;
1940 state->getLineDash(&pattern, &dashLength, &dashStart);
1941 msg("<debug> updateLineDash, %d dashes", dashLength);
1943 states[statepos].dashPattern = 0;
1944 states[statepos].dashLength = 0;
1946 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1947 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1948 states[statepos].dashPattern = p;
1949 states[statepos].dashLength = dashLength;
1950 states[statepos].dashStart = dashStart;
1954 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1956 this->page2page = page2page;
1957 this->num_pages = num_pages;
1960 void GFXOutputDev::updateLineWidth(GfxState *state)
1962 double width = state->getTransformedLineWidth();
1965 void GFXOutputDev::updateLineCap(GfxState *state)
1967 int c = state->getLineCap();
1970 void GFXOutputDev::updateLineJoin(GfxState *state)
1972 int j = state->getLineJoin();
1975 void GFXOutputDev::updateFillColor(GfxState *state)
1978 double opaq = state->getFillOpacity();
1979 state->getFillRGB(&rgb);
1981 void GFXOutputDev::updateFillOpacity(GfxState *state)
1984 double opaq = state->getFillOpacity();
1985 state->getFillRGB(&rgb);
1986 dbg("update fillopaq %f", opaq);
1988 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1990 double opaq = state->getFillOpacity();
1991 dbg("update strokeopaq %f", opaq);
1993 void GFXOutputDev::updateFillOverprint(GfxState *state)
1995 double opaq = state->getFillOverprint();
1996 dbg("update filloverprint %f", opaq);
1998 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2000 double opaq = state->getStrokeOverprint();
2001 dbg("update strokeoverprint %f", opaq);
2003 void GFXOutputDev::updateTransfer(GfxState *state)
2005 dbg("update transfer");
2009 void GFXOutputDev::updateStrokeColor(GfxState *state)
2012 double opaq = state->getStrokeOpacity();
2013 state->getStrokeRGB(&rgb);
2016 void GFXOutputDev::updateFont(GfxState *state)
2018 GfxFont* gfxFont = state->getFont();
2022 char*id = getFontID(gfxFont);
2023 msg("<verbose> Updating font to %s", id);
2024 if(gfxFont->getType() == fontType3) {
2025 infofeature("Type3 fonts");
2026 if(!config_extrafontdata) {
2031 msg("<error> Internal Error: FontID is null");
2035 this->current_fontinfo = this->info->getFont(id);
2037 if(!this->current_fontinfo) {
2038 msg("<error> Internal Error: no fontinfo for font %s", id);
2041 if(!this->current_fontinfo->seen) {
2042 dumpFontInfo("<verbose>", gfxFont);
2045 current_gfxfont = this->current_fontinfo->getGfxFont();
2046 device->addfont(device, current_gfxfont);
2049 updateFontMatrix(state);
2052 #define SQR(x) ((x)*(x))
2054 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2056 if((newwidth<1 || newheight<1) ||
2057 (width<=newwidth || height<=newheight))
2059 unsigned char*newdata;
2061 newdata= (unsigned char*)malloc(newwidth*newheight);
2062 double fx = ((double)width)/newwidth;
2063 double fy = ((double)height)/newheight;
2065 int blocksize = (int)(8192/(fx*fy));
2066 int r = 8192*256/palettesize;
2067 for(x=0;x<newwidth;x++) {
2068 double ex = px + fx;
2069 int fromx = (int)px;
2071 int xweight1 = (int)((1-(px-fromx))*256);
2072 int xweight2 = (int)((ex-tox)*256);
2074 for(y=0;y<newheight;y++) {
2075 double ey = py + fy;
2076 int fromy = (int)py;
2078 int yweight1 = (int)((1-(py-fromy))*256);
2079 int yweight2 = (int)((ey-toy)*256);
2086 for(xx=fromx;xx<=tox;xx++)
2087 for(yy=fromy;yy<=toy;yy++) {
2088 int b = 1-data[width*yy+xx];
2090 if(xx==fromx) weight = (weight*xweight1)/256;
2091 if(xx==tox) weight = (weight*xweight2)/256;
2092 if(yy==fromy) weight = (weight*yweight1)/256;
2093 if(yy==toy) weight = (weight*yweight2)/256;
2096 //if(a) a=(palettesize-1)*r/blocksize;
2097 newdata[y*newwidth+x] = (a*blocksize)/r;
2105 #define IMAGE_TYPE_JPEG 0
2106 #define IMAGE_TYPE_LOSSLESS 1
2108 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2109 double x1,double y1,
2110 double x2,double y2,
2111 double x3,double y3,
2112 double x4,double y4, int type, int multiply)
2114 gfxcolor_t*newpic=0;
2116 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2117 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2119 gfxline_t p1,p2,p3,p4,p5;
2120 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2121 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2122 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2123 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2124 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2126 {p1.x = (int)(p1.x*20)/20.0;
2127 p1.y = (int)(p1.y*20)/20.0;
2128 p2.x = (int)(p2.x*20)/20.0;
2129 p2.y = (int)(p2.y*20)/20.0;
2130 p3.x = (int)(p3.x*20)/20.0;
2131 p3.y = (int)(p3.y*20)/20.0;
2132 p4.x = (int)(p4.x*20)/20.0;
2133 p4.y = (int)(p4.y*20)/20.0;
2134 p5.x = (int)(p5.x*20)/20.0;
2135 p5.y = (int)(p5.y*20)/20.0;
2139 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2140 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2142 m.tx = p1.x - 0.5*multiply;
2143 m.ty = p1.y - 0.5*multiply;
2146 img.data = (gfxcolor_t*)data;
2150 if(type == IMAGE_TYPE_JPEG)
2151 /* TODO: pass image_dpi to device instead */
2152 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2155 dev->fillbitmap(dev, &p1, &img, &m, 0);
2158 void drawimagejpeg(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_JPEG, multiply);
2164 void drawimagelossless(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_LOSSLESS, multiply);
2171 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2172 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2173 GBool inlineImg, int mask, int*maskColors,
2174 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2176 /* the code in this function is *old*. It's not pretty, but it works. */
2178 double x1,y1,x2,y2,x3,y3,x4,y4;
2179 ImageStream *imgStr;
2184 unsigned char* maskbitmap = 0;
2187 ncomps = colorMap->getNumPixelComps();
2188 bits = colorMap->getBits();
2193 unsigned char buf[8];
2194 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2196 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2197 imgMaskStr->reset();
2198 unsigned char pal[256];
2199 int n = 1 << colorMap->getBits();
2204 maskColorMap->getGray(pixBuf, &gray);
2205 pal[t] = colToByte(gray);
2207 for (y = 0; y < maskHeight; y++) {
2208 for (x = 0; x < maskWidth; x++) {
2209 imgMaskStr->getPixel(buf);
2210 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2215 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2216 imgMaskStr->reset();
2217 for (y = 0; y < maskHeight; y++) {
2218 for (x = 0; x < maskWidth; x++) {
2219 imgMaskStr->getPixel(buf);
2221 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2229 imgStr = new ImageStream(str, width, ncomps,bits);
2232 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2234 msg("<verbose> Ignoring %d by %d image", width, height);
2235 unsigned char buf[8];
2237 for (y = 0; y < height; ++y)
2238 for (x = 0; x < width; ++x) {
2239 imgStr->getPixel(buf);
2247 this->transformXY(state, 0, 1, &x1, &y1);
2248 this->transformXY(state, 0, 0, &x2, &y2);
2249 this->transformXY(state, 1, 0, &x3, &y3);
2250 this->transformXY(state, 1, 1, &x4, &y4);
2253 /* as type 3 bitmaps are antialized, we need to place them
2254 at integer coordinates, otherwise flash player's antializing
2255 will kick in and make everything blurry */
2256 x1 = (int)(x1);y1 = (int)(y1);
2257 x2 = (int)(x2);y2 = (int)(y2);
2258 x3 = (int)(x3);y3 = (int)(y3);
2259 x4 = (int)(x4);y4 = (int)(y4);
2262 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2264 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2265 gfxglobals->pbminfo = 1;
2268 msg("<verbose> drawing %d by %d masked picture", width, height);
2270 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2271 msg("<notice> File contains jpeg pictures");
2272 gfxglobals->jpeginfo = 1;
2276 unsigned char buf[8];
2278 unsigned char*pic = new unsigned char[width*height];
2279 gfxcolor_t pal[256];
2281 state->getFillRGB(&rgb);
2283 memset(pal,255,sizeof(pal));
2284 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2285 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2286 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2287 pal[0].a = 255; pal[1].a = 0;
2290 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2291 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2292 for (y = 0; y < height; ++y)
2293 for (x = 0; x < width; ++x)
2295 imgStr->getPixel(buf);
2298 pic[width*y+x] = buf[0];
2302 unsigned char*pic2 = 0;
2305 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2314 height = realheight;
2318 /* make a black/white palette */
2320 float r = 255./(float)(numpalette-1);
2322 for(t=0;t<numpalette;t++) {
2323 pal[t].r = colToByte(rgb.r);
2324 pal[t].g = colToByte(rgb.g);
2325 pal[t].b = colToByte(rgb.b);
2326 pal[t].a = (unsigned char)(t*r);
2331 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2332 for (y = 0; y < height; ++y) {
2333 for (x = 0; x < width; ++x) {
2334 pic2[width*y+x] = pal[pic[y*width+x]];
2337 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2341 if(maskbitmap) free(maskbitmap);
2347 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2348 gfxcolor_t*pic=new gfxcolor_t[width*height];
2349 for (y = 0; y < height; ++y) {
2350 for (x = 0; x < width; ++x) {
2351 imgStr->getPixel(pixBuf);
2352 colorMap->getRGB(pixBuf, &rgb);
2353 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2354 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2355 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2356 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2358 int x1 = x*maskWidth/width;
2359 int y1 = y*maskHeight/height;
2360 int x2 = (x+1)*maskWidth/width;
2361 int y2 = (y+1)*maskHeight/height;
2363 unsigned int alpha=0;
2364 unsigned int count=0;
2365 for(xx=x1;xx<x2;xx++)
2366 for(yy=y1;yy<y2;yy++) {
2367 alpha += maskbitmap[yy*maskWidth+xx];
2371 pic[width*y+x].a = alpha / count;
2373 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2378 if(str->getKind()==strDCT)
2379 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2381 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2384 if(maskbitmap) free(maskbitmap);
2387 gfxcolor_t*pic=new gfxcolor_t[width*height];
2388 gfxcolor_t pal[256];
2389 int n = 1 << colorMap->getBits();
2391 for(t=0;t<256;t++) {
2393 colorMap->getRGB(pixBuf, &rgb);
2395 {/*if(maskColors && *maskColors==t) {
2396 msg("<notice> Color %d is transparent", t);
2397 if (imgData->maskColors) {
2399 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2400 if (pix[i] < imgData->maskColors[2*i] ||
2401 pix[i] > imgData->maskColors[2*i+1]) {
2416 pal[t].r = (unsigned char)(colToByte(rgb.r));
2417 pal[t].g = (unsigned char)(colToByte(rgb.g));
2418 pal[t].b = (unsigned char)(colToByte(rgb.b));
2419 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2422 for (y = 0; y < height; ++y) {
2423 for (x = 0; x < width; ++x) {
2424 imgStr->getPixel(pixBuf);
2425 pic[width*y+x] = pal[pixBuf[0]];
2429 if(maskWidth < width && maskHeight < height) {
2430 for(y = 0; y < height; y++) {
2431 for (x = 0; x < width; x++) {
2432 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2436 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2437 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2438 double dx = width / maskWidth;
2439 double dy = height / maskHeight;
2441 for(y = 0; y < maskHeight; y++) {
2443 for (x = 0; x < maskWidth; x++) {
2444 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2445 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2453 height = maskHeight;
2456 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2460 if(maskbitmap) free(maskbitmap);
2465 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2466 int width, int height, GBool invert,
2469 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2470 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2471 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2474 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2475 int width, int height, GfxImageColorMap *colorMap,
2476 int *maskColors, GBool inlineImg)
2478 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2479 colorMap?"colorMap":"no colorMap",
2480 maskColors?"maskColors":"no maskColors",
2482 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2483 colorMap?"colorMap":"no colorMap",
2484 maskColors?"maskColors":"no maskColors",
2487 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2488 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2489 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2492 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2493 int width, int height,
2494 GfxImageColorMap *colorMap,
2495 Stream *maskStr, int maskWidth, int maskHeight,
2498 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2499 colorMap?"colorMap":"no colorMap",
2500 maskWidth, maskHeight);
2501 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2502 colorMap?"colorMap":"no colorMap",
2503 maskWidth, maskHeight);
2505 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2506 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2507 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2510 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2511 int width, int height,
2512 GfxImageColorMap *colorMap,
2514 int maskWidth, int maskHeight,
2515 GfxImageColorMap *maskColorMap)
2517 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2518 colorMap?"colorMap":"no colorMap",
2519 maskWidth, maskHeight);
2520 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2521 colorMap?"colorMap":"no colorMap",
2522 maskWidth, maskHeight);
2524 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2525 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2526 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2529 void GFXOutputDev::stroke(GfxState *state)
2533 GfxPath * path = state->getPath();
2534 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2535 strokeGfxline(state, line, 0);
2539 void GFXOutputDev::fill(GfxState *state)
2541 gfxcolor_t col = getFillColor(state);
2542 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2544 GfxPath * path = state->getPath();
2545 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2546 if(!config_disable_polygon_conversion) {
2547 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2551 fillGfxLine(state, line, 0);
2555 void GFXOutputDev::eoFill(GfxState *state)
2557 gfxcolor_t col = getFillColor(state);
2558 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2560 GfxPath * path = state->getPath();
2561 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2562 fillGfxLine(state, line, 1);
2567 static const char* dirseparator()
2576 void addGlobalFont(const char*filename)
2578 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2579 memset(f, 0, sizeof(fontfile_t));
2580 f->filename = filename;
2581 int len = strlen(filename);
2582 char*r1 = strrchr((char*)filename, '/');
2583 char*r2 = strrchr((char*)filename, '\\');
2591 msg("<verbose> Adding font \"%s\".", filename);
2592 if(global_fonts_next) {
2593 global_fonts_next->next = f;
2594 global_fonts_next = global_fonts_next->next;
2596 global_fonts_next = global_fonts = f;
2600 void addGlobalLanguageDir(const char*dir)
2602 msg("<notice> Adding %s to language pack directories", dir);
2605 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2606 strcpy(config_file, dir);
2607 strcat(config_file, dirseparator());
2608 strcat(config_file, "add-to-xpdfrc");
2610 fi = fopen(config_file, "rb");
2612 msg("<error> Could not open %s", config_file);
2615 globalParams->parseFile(new GString(config_file), fi);
2619 void addGlobalFontDir(const char*dirname)
2621 #ifdef HAVE_DIRENT_H
2622 DIR*dir = opendir(dirname);
2624 msg("<warning> Couldn't open directory %s", dirname);
2630 ent = readdir (dir);
2634 char*name = ent->d_name;
2640 if(!strncasecmp(&name[l-4], ".pfa", 4))
2642 if(!strncasecmp(&name[l-4], ".pfb", 4))
2644 if(!strncasecmp(&name[l-4], ".ttf", 4))
2647 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2648 strcpy(fontname, dirname);
2649 strcat(fontname, dirseparator());
2650 strcat(fontname, name);
2651 addGlobalFont(fontname);
2655 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2658 msg("<warning> No dirent.h");
2662 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2663 GfxColorSpace *blendingColorSpace,
2664 GBool isolated, GBool knockout,
2667 const char*colormodename = "";
2669 if(blendingColorSpace) {
2670 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2672 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);
2673 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);
2675 //states[statepos].createsoftmask |= forSoftMask;
2676 states[statepos].createsoftmask = forSoftMask;
2677 states[statepos].transparencygroup = !forSoftMask;
2678 states[statepos].isolated = isolated;
2680 states[statepos].olddevice = this->device;
2681 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2682 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2684 gfxdevice_record_init(this->device);
2686 /*if(!forSoftMask) { ////???
2687 state->setFillOpacity(0.0);
2692 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2695 gfxdevice_t*r = this->device;
2697 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2699 while(statepos && !states[statepos].olddevice)
2702 this->device = states[statepos].olddevice;
2704 msg("<error> Invalid state nesting");
2706 states[statepos].olddevice = 0;
2708 gfxresult_t*recording = r->finish(r);
2710 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2711 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2713 if(states[statepos].createsoftmask) {
2714 states[statepos-1].softmaskrecording = recording;
2716 states[statepos-1].grouprecording = recording;
2719 states[statepos].createsoftmask = 0;
2720 states[statepos].transparencygroup = 0;
2724 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2726 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2727 "colordodge","colorburn","hardlight","softlight","difference",
2728 "exclusion","hue","saturation","color","luminosity"};
2730 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2731 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2733 if(state->getBlendMode() == gfxBlendNormal)
2734 infofeature("transparency groups");
2737 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2738 warnfeature(buffer, 0);
2741 gfxresult_t*grouprecording = states[statepos].grouprecording;
2743 int blendmode = state->getBlendMode();
2744 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2745 int alpha = (int)(state->getFillOpacity()*255);
2746 if(blendmode == gfxBlendMultiply && alpha>200)
2749 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2750 gfxdevice_ops_init(&ops, this->device, alpha);
2751 gfxresult_record_replay(grouprecording, &ops);
2754 grouprecording->destroy(grouprecording);
2756 states[statepos].grouprecording = 0;
2759 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2761 if(states[statepos].softmask) {
2762 /* shouldn't happen, but *does* happen */
2763 clearSoftMask(state);
2766 /* alpha = 1: retrieve mask values from alpha layer
2767 alpha = 0: retrieve mask values from luminance */
2769 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2770 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2771 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2772 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2774 infofeature("soft masks");
2776 warnfeature("soft masks from alpha channel",0);
2778 if(states[statepos].olddevice) {
2779 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2782 states[statepos].olddevice = this->device;
2783 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2784 gfxdevice_record_init(this->device);
2786 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2788 states[statepos].softmask = 1;
2789 states[statepos].softmask_alpha = alpha;
2792 static inline Guchar div255(int x) {
2793 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2796 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2798 if(c < min) c = min;
2799 if(c > max) c = max;
2803 void GFXOutputDev::clearSoftMask(GfxState *state)
2805 if(!states[statepos].softmask)
2807 states[statepos].softmask = 0;
2808 dbg("clearSoftMask statepos=%d", statepos);
2809 msg("<verbose> clearSoftMask statepos=%d", statepos);
2811 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2812 msg("<error> Error in softmask/tgroup ordering");
2816 gfxresult_t*mask = states[statepos].softmaskrecording;
2817 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2818 this->device = states[statepos].olddevice;
2820 /* get outline of all objects below the soft mask */
2821 gfxdevice_t uniondev;
2822 gfxdevice_union_init(&uniondev, 0);
2823 gfxresult_record_replay(below, &uniondev);
2824 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2825 uniondev.finish(&uniondev);
2826 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2827 gfxline_free(belowoutline);belowoutline=0;
2829 this->device->startclip(this->device, belowoutline);
2830 gfxresult_record_replay(below, this->device);
2831 gfxresult_record_replay(mask, this->device);
2832 this->device->endclip(this->device);
2835 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2836 if(width<=0 || height<=0)
2839 gfxdevice_t belowrender;
2840 gfxdevice_render_init(&belowrender);
2841 if(states[statepos+1].isolated) {
2842 belowrender.setparameter(&belowrender, "fillwhite", "1");
2844 belowrender.setparameter(&belowrender, "antialize", "2");
2845 belowrender.startpage(&belowrender, width, height);
2846 gfxresult_record_replay(below, &belowrender);
2847 belowrender.endpage(&belowrender);
2848 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2849 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2850 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2852 gfxdevice_t maskrender;
2853 gfxdevice_render_init(&maskrender);
2854 maskrender.startpage(&maskrender, width, height);
2855 gfxresult_record_replay(mask, &maskrender);
2856 maskrender.endpage(&maskrender);
2857 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2858 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2860 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2861 msg("<fatal> Internal error in mask drawing");
2866 for(y=0;y<height;y++) {
2867 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2868 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2869 for(x=0;x<width;x++) {
2871 if(states[statepos].softmask_alpha) {
2874 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2877 l2->a = div255(alpha*l2->a);
2879 /* DON'T premultiply alpha- this is done by fillbitmap,
2880 depending on the output device */
2881 //l2->r = div255(alpha*l2->r);
2882 //l2->g = div255(alpha*l2->g);
2883 //l2->b = div255(alpha*l2->b);
2889 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2892 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2893 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2895 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2897 mask->destroy(mask);
2898 below->destroy(below);
2899 maskresult->destroy(maskresult);
2900 belowresult->destroy(belowresult);
2901 states[statepos].softmaskrecording = 0;
2906 // public: ~MemCheck()
2908 // delete globalParams;globalParams=0;
2909 // Object::memCheck(stderr);
2910 // gMemReport(stderr);