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=%p",m.tx,m.ty,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
1448 if((render == RENDER_FILL && !config_drawonlyshapes) ||
1449 (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
1450 (render == RENDER_INVISIBLE)) {
1452 int space = this->current_fontinfo->space_char;
1453 if(config_extrafontdata && space>=0 && m.m00 && !m.m01) {
1454 /* space char detection */
1455 if(last_char_gfxfont == current_gfxfont &&
1456 last_char_y == m.ty &&
1457 !last_char_was_space) {
1458 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1459 int space = this->current_fontinfo->space_char;
1460 float width = this->current_fontinfo->average_advance;
1461 if(m.tx - expected_x >= m.m00*width*4/10) {
1462 msg("<debug> There's a %f pixel gap between char %d and char %d (expected no more than %f), I'm inserting a space here",
1465 last_char, glyphid);
1467 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1468 if(m2.tx < expected_x) m2.tx = expected_x;
1469 device->drawchar(device, current_gfxfont, space, &col, &m2);
1472 last_char_gfxfont = current_gfxfont;
1473 last_char = glyphid;
1476 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1478 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1480 msg("<debug> Drawing glyph %d as shape", charid);
1481 if(!gfxglobals->textmodeinfo) {
1482 msg("<notice> Some texts will be rendered as shape");
1483 gfxglobals->textmodeinfo = 1;
1486 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1487 gfxline_t*tglyph = gfxline_clone(glyph);
1488 gfxline_transform(tglyph, &m);
1489 if((render&3) != RENDER_INVISIBLE) {
1490 gfxline_t*add = gfxline_clone(tglyph);
1491 current_text_stroke = gfxline_append(current_text_stroke, add);
1493 if(render&RENDER_CLIP) {
1494 gfxline_t*add = gfxline_clone(tglyph);
1495 current_text_clip = gfxline_append(current_text_clip, add);
1496 if(!current_text_clip) {
1497 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1500 gfxline_free(tglyph);
1504 void GFXOutputDev::endString(GfxState *state)
1506 int render = state->getRender();
1507 msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
1509 if(current_text_stroke) {
1510 /* fillstroke and stroke text rendering objects we can process right
1511 now (as there may be texts of other rendering modes in this
1512 text object)- clipping objects have to wait until endTextObject,
1514 device->setparameter(device, "mark","TXT");
1515 if((render&3) == RENDER_FILL) {
1516 fillGfxLine(state, current_text_stroke, 0);
1517 gfxline_free(current_text_stroke);
1518 current_text_stroke = 0;
1519 } else if((render&3) == RENDER_FILLSTROKE) {
1520 fillGfxLine(state, current_text_stroke, 0);
1521 strokeGfxline(state, current_text_stroke,0);
1522 gfxline_free(current_text_stroke);
1523 current_text_stroke = 0;
1524 } else if((render&3) == RENDER_STROKE) {
1525 strokeGfxline(state, current_text_stroke,0);
1526 gfxline_free(current_text_stroke);
1527 current_text_stroke = 0;
1529 device->setparameter(device, "mark","");
1533 void GFXOutputDev::endTextObject(GfxState *state)
1535 int render = state->getRender();
1536 msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
1538 if(current_text_clip) {
1539 device->setparameter(device, "mark","TXT");
1540 clipToGfxLine(state, current_text_clip, 0);
1541 device->setparameter(device, "mark","");
1542 gfxline_free(current_text_clip);
1543 current_text_clip = 0;
1547 /* the logic seems to be as following:
1548 first, beginType3Char is called, with the charcode and the coordinates.
1549 if this function returns true, it already knew about the char and has now drawn it.
1550 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1551 called with some parameters.
1552 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1553 at the position first passed to beginType3Char). the char ends with endType3Char.
1555 The drawing operations between beginType3Char and endType3Char are somewhat different to
1556 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1557 color determines the color of a font)
1560 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1562 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1565 if(config_extrafontdata && current_fontinfo) {
1567 gfxmatrix_t m = this->current_font_matrix;
1568 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1570 /*m.m00*=INTERNAL_FONT_SIZE;
1571 m.m01*=INTERNAL_FONT_SIZE;
1572 m.m10*=INTERNAL_FONT_SIZE;
1573 m.m11*=INTERNAL_FONT_SIZE;*/
1575 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1576 msg("<error> Invalid charid %d for font", charid);
1579 gfxcolor_t col={0,0,0,0};
1580 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1581 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1585 /* the character itself is going to be passed using the draw functions */
1586 return gFalse; /* gTrue= is_in_cache? */
1589 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1591 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1594 void GFXOutputDev::endType3Char(GfxState *state)
1597 msg("<debug> endType3Char");
1600 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1602 this->currentpage = pageNum;
1604 int rot = doc->getPageRotate(1);
1605 gfxcolor_t white = {255,255,255,255};
1606 gfxcolor_t black = {255,0,0,0};
1608 gfxline_t clippath[5];
1610 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1611 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1612 Use CropBox, not MediaBox, as page size
1619 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1620 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1622 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1623 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1625 this->clipmovex = -(int)x1;
1626 this->clipmovey = -(int)y1;
1628 /* apply user clip box */
1629 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1630 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1631 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1632 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1633 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1634 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1636 x1 += this->clipmovex;
1637 y1 += this->clipmovey;
1638 x2 += this->clipmovex;
1639 y2 += this->clipmovey;
1642 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1644 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);
1646 msg("<verbose> page is rotated %d degrees", rot);
1648 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1649 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1650 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1651 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1652 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1653 device->startclip(device, clippath); outer_clip_box = 1;
1654 if(!config_transparent) {
1655 device->fill(device, clippath, &white);
1657 states[statepos].clipbbox.xmin = x1;
1658 states[statepos].clipbbox.ymin = x1;
1659 states[statepos].clipbbox.xmax = x2;
1660 states[statepos].clipbbox.ymax = y2;
1662 states[statepos].dashPattern = 0;
1663 states[statepos].dashLength = 0;
1664 states[statepos].dashStart = 0;
1666 this->last_char_gfxfont = 0;
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 %p", state); dbgindent+=2;
1869 msg("<trace> saveState %p", 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 %p", state);
1892 msg("<fatal> Invalid restoreState");
1895 msg("<trace> restoreState %p%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("%p ", 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);
2394 pal[t].r = (unsigned char)(colToByte(rgb.r));
2395 pal[t].g = (unsigned char)(colToByte(rgb.g));
2396 pal[t].b = (unsigned char)(colToByte(rgb.b));
2397 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2399 for (y = 0; y < height; ++y) {
2400 for (x = 0; x < width; ++x) {
2401 imgStr->getPixel(pixBuf);
2402 pic[width*y+x] = pal[pixBuf[0]];
2403 if(maskColors && *maskColors==pixBuf[0]) {
2404 pic[width*y+x].a = 0;
2409 if(maskWidth < width && maskHeight < height) {
2410 for(y = 0; y < height; y++) {
2411 for (x = 0; x < width; x++) {
2412 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2416 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2417 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2418 double dx = width / maskWidth;
2419 double dy = height / maskHeight;
2421 for(y = 0; y < maskHeight; y++) {
2423 for (x = 0; x < maskWidth; x++) {
2424 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2425 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2433 height = maskHeight;
2436 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2440 if(maskbitmap) free(maskbitmap);
2445 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2446 int width, int height, GBool invert,
2449 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2450 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2451 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2454 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2455 int width, int height, GfxImageColorMap *colorMap,
2456 int *maskColors, GBool inlineImg)
2458 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2459 colorMap?"colorMap":"no colorMap",
2460 maskColors?"maskColors":"no maskColors",
2462 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2463 colorMap?"colorMap":"no colorMap",
2464 maskColors?"maskColors":"no maskColors",
2467 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2468 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2469 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2472 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2473 int width, int height,
2474 GfxImageColorMap *colorMap,
2475 Stream *maskStr, int maskWidth, int maskHeight,
2478 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2479 colorMap?"colorMap":"no colorMap",
2480 maskWidth, maskHeight);
2481 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2482 colorMap?"colorMap":"no colorMap",
2483 maskWidth, maskHeight);
2485 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2486 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2487 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2490 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2491 int width, int height,
2492 GfxImageColorMap *colorMap,
2494 int maskWidth, int maskHeight,
2495 GfxImageColorMap *maskColorMap)
2497 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2498 colorMap?"colorMap":"no colorMap",
2499 maskWidth, maskHeight);
2500 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2501 colorMap?"colorMap":"no colorMap",
2502 maskWidth, maskHeight);
2504 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2505 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2506 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2509 void GFXOutputDev::stroke(GfxState *state)
2513 GfxPath * path = state->getPath();
2514 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2515 strokeGfxline(state, line, 0);
2519 void GFXOutputDev::fill(GfxState *state)
2521 gfxcolor_t col = getFillColor(state);
2522 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2524 GfxPath * path = state->getPath();
2525 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2526 if(!config_disable_polygon_conversion) {
2527 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2531 fillGfxLine(state, line, 0);
2535 void GFXOutputDev::eoFill(GfxState *state)
2537 gfxcolor_t col = getFillColor(state);
2538 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2540 GfxPath * path = state->getPath();
2541 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2542 fillGfxLine(state, line, 1);
2547 static const char* dirseparator()
2556 void addGlobalFont(const char*filename)
2558 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2559 memset(f, 0, sizeof(fontfile_t));
2560 f->filename = filename;
2561 int len = strlen(filename);
2562 char*r1 = strrchr((char*)filename, '/');
2563 char*r2 = strrchr((char*)filename, '\\');
2571 msg("<verbose> Adding font \"%s\".", filename);
2572 if(global_fonts_next) {
2573 global_fonts_next->next = f;
2574 global_fonts_next = global_fonts_next->next;
2576 global_fonts_next = global_fonts = f;
2580 void addGlobalLanguageDir(const char*dir)
2582 msg("<notice> Adding %s to language pack directories", dir);
2585 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2586 strcpy(config_file, dir);
2587 strcat(config_file, dirseparator());
2588 strcat(config_file, "add-to-xpdfrc");
2590 fi = fopen(config_file, "rb");
2592 msg("<error> Could not open %s", config_file);
2595 globalParams->parseFile(new GString(config_file), fi);
2599 void addGlobalFontDir(const char*dirname)
2601 #ifdef HAVE_DIRENT_H
2602 DIR*dir = opendir(dirname);
2604 msg("<warning> Couldn't open directory %s", dirname);
2610 ent = readdir (dir);
2614 char*name = ent->d_name;
2620 if(!strncasecmp(&name[l-4], ".pfa", 4))
2622 if(!strncasecmp(&name[l-4], ".pfb", 4))
2624 if(!strncasecmp(&name[l-4], ".ttf", 4))
2627 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2628 strcpy(fontname, dirname);
2629 strcat(fontname, dirseparator());
2630 strcat(fontname, name);
2631 addGlobalFont(fontname);
2635 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2638 msg("<warning> No dirent.h");
2642 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2643 GfxColorSpace *blendingColorSpace,
2644 GBool isolated, GBool knockout,
2647 const char*colormodename = "";
2649 if(blendingColorSpace) {
2650 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2652 dbg("beginTransparencyGroup device=%p %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2653 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);
2655 //states[statepos].createsoftmask |= forSoftMask;
2656 states[statepos].createsoftmask = forSoftMask;
2657 states[statepos].transparencygroup = !forSoftMask;
2658 states[statepos].isolated = isolated;
2660 states[statepos].olddevice = this->device;
2661 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2662 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2664 gfxdevice_record_init(this->device);
2666 /*if(!forSoftMask) { ////???
2667 state->setFillOpacity(0.0);
2672 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2675 gfxdevice_t*r = this->device;
2677 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2679 this->device = states[statepos].olddevice;
2681 msg("<error> Invalid state nesting");
2683 states[statepos].olddevice = 0;
2685 gfxresult_t*recording = r->finish(r);
2687 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2688 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2690 if(states[statepos].createsoftmask) {
2691 states[statepos-1].softmaskrecording = recording;
2693 states[statepos-1].grouprecording = recording;
2696 states[statepos].createsoftmask = 0;
2697 states[statepos].transparencygroup = 0;
2701 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2703 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2704 "colordodge","colorburn","hardlight","softlight","difference",
2705 "exclusion","hue","saturation","color","luminosity"};
2707 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2708 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2710 if(state->getBlendMode() == gfxBlendNormal)
2711 infofeature("transparency groups");
2714 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2715 warnfeature(buffer, 0);
2718 gfxresult_t*grouprecording = states[statepos].grouprecording;
2720 int blendmode = state->getBlendMode();
2721 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2722 int alpha = (int)(state->getFillOpacity()*255);
2723 if(blendmode == gfxBlendMultiply && alpha>200)
2726 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2727 gfxdevice_ops_init(&ops, this->device, alpha);
2728 gfxresult_record_replay(grouprecording, &ops);
2731 grouprecording->destroy(grouprecording);
2733 states[statepos].grouprecording = 0;
2736 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2738 if(states[statepos].softmask) {
2739 /* shouldn't happen, but *does* happen */
2740 clearSoftMask(state);
2743 /* alpha = 1: retrieve mask values from alpha layer
2744 alpha = 0: retrieve mask values from luminance */
2746 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2747 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2748 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2749 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2751 infofeature("soft masks");
2753 warnfeature("soft masks from alpha channel",0);
2755 if(states[statepos].olddevice) {
2756 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2759 states[statepos].olddevice = this->device;
2760 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2761 gfxdevice_record_init(this->device);
2763 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2765 states[statepos].softmask = 1;
2766 states[statepos].softmask_alpha = alpha;
2769 static inline Guchar div255(int x) {
2770 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2773 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2775 if(c < min) c = min;
2776 if(c > max) c = max;
2780 void GFXOutputDev::clearSoftMask(GfxState *state)
2782 if(!states[statepos].softmask)
2784 states[statepos].softmask = 0;
2785 dbg("clearSoftMask statepos=%d", statepos);
2786 msg("<verbose> clearSoftMask statepos=%d", statepos);
2788 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2789 msg("<error> Error in softmask/tgroup ordering");
2793 gfxresult_t*mask = states[statepos].softmaskrecording;
2794 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2795 this->device = states[statepos].olddevice;
2797 /* get outline of all objects below the soft mask */
2798 gfxdevice_t uniondev;
2799 gfxdevice_union_init(&uniondev, 0);
2800 gfxresult_record_replay(below, &uniondev);
2801 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2802 uniondev.finish(&uniondev);
2803 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2804 gfxline_free(belowoutline);belowoutline=0;
2806 this->device->startclip(this->device, belowoutline);
2807 gfxresult_record_replay(below, this->device);
2808 gfxresult_record_replay(mask, this->device);
2809 this->device->endclip(this->device);
2812 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2813 if(width<=0 || height<=0)
2816 gfxdevice_t belowrender;
2817 gfxdevice_render_init(&belowrender);
2818 if(states[statepos+1].isolated) {
2819 belowrender.setparameter(&belowrender, "fillwhite", "1");
2821 belowrender.setparameter(&belowrender, "antialize", "2");
2822 belowrender.startpage(&belowrender, width, height);
2823 gfxresult_record_replay(below, &belowrender);
2824 belowrender.endpage(&belowrender);
2825 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2826 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2827 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2829 gfxdevice_t maskrender;
2830 gfxdevice_render_init(&maskrender);
2831 maskrender.startpage(&maskrender, width, height);
2832 gfxresult_record_replay(mask, &maskrender);
2833 maskrender.endpage(&maskrender);
2834 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2835 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2837 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2838 msg("<fatal> Internal error in mask drawing");
2843 for(y=0;y<height;y++) {
2844 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2845 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2846 for(x=0;x<width;x++) {
2848 if(states[statepos].softmask_alpha) {
2851 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2854 l2->a = div255(alpha*l2->a);
2856 /* DON'T premultiply alpha- this is done by fillbitmap,
2857 depending on the output device */
2858 //l2->r = div255(alpha*l2->r);
2859 //l2->g = div255(alpha*l2->g);
2860 //l2->b = div255(alpha*l2->b);
2866 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2869 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2870 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2872 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2874 mask->destroy(mask);
2875 below->destroy(below);
2876 maskresult->destroy(maskresult);
2877 belowresult->destroy(belowresult);
2878 states[statepos].softmaskrecording = 0;
2883 // public: ~MemCheck()
2885 // delete globalParams;globalParams=0;
2886 // Object::memCheck(stderr);
2887 // gMemReport(stderr);