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) ||
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=%08x", 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=%08x clipstroke=%08x", 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 %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 device->addfont(device, current_gfxfont);
2051 updateFontMatrix(state);
2054 #define SQR(x) ((x)*(x))
2056 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2058 if((newwidth<1 || newheight<1) ||
2059 (width<=newwidth || height<=newheight))
2061 unsigned char*newdata;
2063 newdata= (unsigned char*)malloc(newwidth*newheight);
2064 double fx = ((double)width)/newwidth;
2065 double fy = ((double)height)/newheight;
2067 int blocksize = (int)(8192/(fx*fy));
2068 int r = 8192*256/palettesize;
2069 for(x=0;x<newwidth;x++) {
2070 double ex = px + fx;
2071 int fromx = (int)px;
2073 int xweight1 = (int)((1-(px-fromx))*256);
2074 int xweight2 = (int)((ex-tox)*256);
2076 for(y=0;y<newheight;y++) {
2077 double ey = py + fy;
2078 int fromy = (int)py;
2080 int yweight1 = (int)((1-(py-fromy))*256);
2081 int yweight2 = (int)((ey-toy)*256);
2088 for(xx=fromx;xx<=tox;xx++)
2089 for(yy=fromy;yy<=toy;yy++) {
2090 int b = 1-data[width*yy+xx];
2092 if(xx==fromx) weight = (weight*xweight1)/256;
2093 if(xx==tox) weight = (weight*xweight2)/256;
2094 if(yy==fromy) weight = (weight*yweight1)/256;
2095 if(yy==toy) weight = (weight*yweight2)/256;
2098 //if(a) a=(palettesize-1)*r/blocksize;
2099 newdata[y*newwidth+x] = (a*blocksize)/r;
2107 #define IMAGE_TYPE_JPEG 0
2108 #define IMAGE_TYPE_LOSSLESS 1
2110 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2111 double x1,double y1,
2112 double x2,double y2,
2113 double x3,double y3,
2114 double x4,double y4, int type, int multiply)
2116 gfxcolor_t*newpic=0;
2118 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2119 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2121 gfxline_t p1,p2,p3,p4,p5;
2122 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2123 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2124 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2125 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2126 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2128 {p1.x = (int)(p1.x*20)/20.0;
2129 p1.y = (int)(p1.y*20)/20.0;
2130 p2.x = (int)(p2.x*20)/20.0;
2131 p2.y = (int)(p2.y*20)/20.0;
2132 p3.x = (int)(p3.x*20)/20.0;
2133 p3.y = (int)(p3.y*20)/20.0;
2134 p4.x = (int)(p4.x*20)/20.0;
2135 p4.y = (int)(p4.y*20)/20.0;
2136 p5.x = (int)(p5.x*20)/20.0;
2137 p5.y = (int)(p5.y*20)/20.0;
2141 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2142 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2144 m.tx = p1.x - 0.5*multiply;
2145 m.ty = p1.y - 0.5*multiply;
2148 img.data = (gfxcolor_t*)data;
2152 if(type == IMAGE_TYPE_JPEG)
2153 /* TODO: pass image_dpi to device instead */
2154 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2157 dev->fillbitmap(dev, &p1, &img, &m, 0);
2160 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2161 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2163 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2166 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2167 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2169 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2173 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2174 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2175 GBool inlineImg, int mask, int*maskColors,
2176 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2178 /* the code in this function is *old*. It's not pretty, but it works. */
2180 double x1,y1,x2,y2,x3,y3,x4,y4;
2181 ImageStream *imgStr;
2186 unsigned char* maskbitmap = 0;
2189 ncomps = colorMap->getNumPixelComps();
2190 bits = colorMap->getBits();
2195 unsigned char buf[8];
2196 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2198 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2199 imgMaskStr->reset();
2200 unsigned char pal[256];
2201 int n = 1 << colorMap->getBits();
2206 maskColorMap->getGray(pixBuf, &gray);
2207 pal[t] = colToByte(gray);
2209 for (y = 0; y < maskHeight; y++) {
2210 for (x = 0; x < maskWidth; x++) {
2211 imgMaskStr->getPixel(buf);
2212 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2217 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2218 imgMaskStr->reset();
2219 for (y = 0; y < maskHeight; y++) {
2220 for (x = 0; x < maskWidth; x++) {
2221 imgMaskStr->getPixel(buf);
2223 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2231 imgStr = new ImageStream(str, width, ncomps,bits);
2234 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2236 msg("<verbose> Ignoring %d by %d image", width, height);
2237 unsigned char buf[8];
2239 for (y = 0; y < height; ++y)
2240 for (x = 0; x < width; ++x) {
2241 imgStr->getPixel(buf);
2249 this->transformXY(state, 0, 1, &x1, &y1);
2250 this->transformXY(state, 0, 0, &x2, &y2);
2251 this->transformXY(state, 1, 0, &x3, &y3);
2252 this->transformXY(state, 1, 1, &x4, &y4);
2255 /* as type 3 bitmaps are antialized, we need to place them
2256 at integer coordinates, otherwise flash player's antializing
2257 will kick in and make everything blurry */
2258 x1 = (int)(x1);y1 = (int)(y1);
2259 x2 = (int)(x2);y2 = (int)(y2);
2260 x3 = (int)(x3);y3 = (int)(y3);
2261 x4 = (int)(x4);y4 = (int)(y4);
2264 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2266 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2267 gfxglobals->pbminfo = 1;
2270 msg("<verbose> drawing %d by %d masked picture", width, height);
2272 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2273 msg("<notice> File contains jpeg pictures");
2274 gfxglobals->jpeginfo = 1;
2278 unsigned char buf[8];
2280 unsigned char*pic = new unsigned char[width*height];
2281 gfxcolor_t pal[256];
2283 state->getFillRGB(&rgb);
2285 memset(pal,255,sizeof(pal));
2286 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2287 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2288 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2289 pal[0].a = 255; pal[1].a = 0;
2292 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2293 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2294 for (y = 0; y < height; ++y)
2295 for (x = 0; x < width; ++x)
2297 imgStr->getPixel(buf);
2300 pic[width*y+x] = buf[0];
2304 unsigned char*pic2 = 0;
2307 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2316 height = realheight;
2320 /* make a black/white palette */
2322 float r = 255./(float)(numpalette-1);
2324 for(t=0;t<numpalette;t++) {
2325 pal[t].r = colToByte(rgb.r);
2326 pal[t].g = colToByte(rgb.g);
2327 pal[t].b = colToByte(rgb.b);
2328 pal[t].a = (unsigned char)(t*r);
2333 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2334 for (y = 0; y < height; ++y) {
2335 for (x = 0; x < width; ++x) {
2336 pic2[width*y+x] = pal[pic[y*width+x]];
2339 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2343 if(maskbitmap) free(maskbitmap);
2349 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2350 gfxcolor_t*pic=new gfxcolor_t[width*height];
2351 for (y = 0; y < height; ++y) {
2352 for (x = 0; x < width; ++x) {
2353 imgStr->getPixel(pixBuf);
2354 colorMap->getRGB(pixBuf, &rgb);
2355 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2356 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2357 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2358 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2360 int x1 = x*maskWidth/width;
2361 int y1 = y*maskHeight/height;
2362 int x2 = (x+1)*maskWidth/width;
2363 int y2 = (y+1)*maskHeight/height;
2365 unsigned int alpha=0;
2366 unsigned int count=0;
2367 for(xx=x1;xx<x2;xx++)
2368 for(yy=y1;yy<y2;yy++) {
2369 alpha += maskbitmap[yy*maskWidth+xx];
2373 pic[width*y+x].a = alpha / count;
2375 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2380 if(str->getKind()==strDCT)
2381 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2383 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2386 if(maskbitmap) free(maskbitmap);
2389 gfxcolor_t*pic=new gfxcolor_t[width*height];
2390 gfxcolor_t pal[256];
2391 int n = 1 << colorMap->getBits();
2393 for(t=0;t<256;t++) {
2395 colorMap->getRGB(pixBuf, &rgb);
2397 {/*if(maskColors && *maskColors==t) {
2398 msg("<notice> Color %d is transparent", t);
2399 if (imgData->maskColors) {
2401 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2402 if (pix[i] < imgData->maskColors[2*i] ||
2403 pix[i] > imgData->maskColors[2*i+1]) {
2418 pal[t].r = (unsigned char)(colToByte(rgb.r));
2419 pal[t].g = (unsigned char)(colToByte(rgb.g));
2420 pal[t].b = (unsigned char)(colToByte(rgb.b));
2421 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2424 for (y = 0; y < height; ++y) {
2425 for (x = 0; x < width; ++x) {
2426 imgStr->getPixel(pixBuf);
2427 pic[width*y+x] = pal[pixBuf[0]];
2431 if(maskWidth < width && maskHeight < height) {
2432 for(y = 0; y < height; y++) {
2433 for (x = 0; x < width; x++) {
2434 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2438 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2439 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2440 double dx = width / maskWidth;
2441 double dy = height / maskHeight;
2443 for(y = 0; y < maskHeight; y++) {
2445 for (x = 0; x < maskWidth; x++) {
2446 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2447 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2455 height = maskHeight;
2458 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2462 if(maskbitmap) free(maskbitmap);
2467 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2468 int width, int height, GBool invert,
2471 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2472 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2473 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2476 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2477 int width, int height, GfxImageColorMap *colorMap,
2478 int *maskColors, GBool inlineImg)
2480 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2481 colorMap?"colorMap":"no colorMap",
2482 maskColors?"maskColors":"no maskColors",
2484 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2485 colorMap?"colorMap":"no colorMap",
2486 maskColors?"maskColors":"no maskColors",
2489 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2490 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2491 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2494 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2495 int width, int height,
2496 GfxImageColorMap *colorMap,
2497 Stream *maskStr, int maskWidth, int maskHeight,
2500 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2501 colorMap?"colorMap":"no colorMap",
2502 maskWidth, maskHeight);
2503 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2504 colorMap?"colorMap":"no colorMap",
2505 maskWidth, maskHeight);
2507 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2508 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2509 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2512 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2513 int width, int height,
2514 GfxImageColorMap *colorMap,
2516 int maskWidth, int maskHeight,
2517 GfxImageColorMap *maskColorMap)
2519 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2520 colorMap?"colorMap":"no colorMap",
2521 maskWidth, maskHeight);
2522 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2523 colorMap?"colorMap":"no colorMap",
2524 maskWidth, maskHeight);
2526 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2527 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2528 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2531 void GFXOutputDev::stroke(GfxState *state)
2535 GfxPath * path = state->getPath();
2536 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2537 strokeGfxline(state, line, 0);
2541 void GFXOutputDev::fill(GfxState *state)
2543 gfxcolor_t col = getFillColor(state);
2544 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2546 GfxPath * path = state->getPath();
2547 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2548 if(!config_disable_polygon_conversion) {
2549 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2553 fillGfxLine(state, line, 0);
2557 void GFXOutputDev::eoFill(GfxState *state)
2559 gfxcolor_t col = getFillColor(state);
2560 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2562 GfxPath * path = state->getPath();
2563 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2564 fillGfxLine(state, line, 1);
2569 static const char* dirseparator()
2578 void addGlobalFont(const char*filename)
2580 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2581 memset(f, 0, sizeof(fontfile_t));
2582 f->filename = filename;
2583 int len = strlen(filename);
2584 char*r1 = strrchr((char*)filename, '/');
2585 char*r2 = strrchr((char*)filename, '\\');
2593 msg("<verbose> Adding font \"%s\".", filename);
2594 if(global_fonts_next) {
2595 global_fonts_next->next = f;
2596 global_fonts_next = global_fonts_next->next;
2598 global_fonts_next = global_fonts = f;
2602 void addGlobalLanguageDir(const char*dir)
2604 msg("<notice> Adding %s to language pack directories", dir);
2607 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2608 strcpy(config_file, dir);
2609 strcat(config_file, dirseparator());
2610 strcat(config_file, "add-to-xpdfrc");
2612 fi = fopen(config_file, "rb");
2614 msg("<error> Could not open %s", config_file);
2617 globalParams->parseFile(new GString(config_file), fi);
2621 void addGlobalFontDir(const char*dirname)
2623 #ifdef HAVE_DIRENT_H
2624 DIR*dir = opendir(dirname);
2626 msg("<warning> Couldn't open directory %s", dirname);
2632 ent = readdir (dir);
2636 char*name = ent->d_name;
2642 if(!strncasecmp(&name[l-4], ".pfa", 4))
2644 if(!strncasecmp(&name[l-4], ".pfb", 4))
2646 if(!strncasecmp(&name[l-4], ".ttf", 4))
2649 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2650 strcpy(fontname, dirname);
2651 strcat(fontname, dirseparator());
2652 strcat(fontname, name);
2653 addGlobalFont(fontname);
2657 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2660 msg("<warning> No dirent.h");
2664 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2665 GfxColorSpace *blendingColorSpace,
2666 GBool isolated, GBool knockout,
2669 const char*colormodename = "";
2671 if(blendingColorSpace) {
2672 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2674 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);
2675 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);
2677 //states[statepos].createsoftmask |= forSoftMask;
2678 states[statepos].createsoftmask = forSoftMask;
2679 states[statepos].transparencygroup = !forSoftMask;
2680 states[statepos].isolated = isolated;
2682 states[statepos].olddevice = this->device;
2683 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2684 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2686 gfxdevice_record_init(this->device);
2688 /*if(!forSoftMask) { ////???
2689 state->setFillOpacity(0.0);
2694 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2697 gfxdevice_t*r = this->device;
2699 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2701 this->device = states[statepos].olddevice;
2703 msg("<error> Invalid state nesting");
2705 states[statepos].olddevice = 0;
2707 gfxresult_t*recording = r->finish(r);
2709 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2710 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2712 if(states[statepos].createsoftmask) {
2713 states[statepos-1].softmaskrecording = recording;
2715 states[statepos-1].grouprecording = recording;
2718 states[statepos].createsoftmask = 0;
2719 states[statepos].transparencygroup = 0;
2723 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2725 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2726 "colordodge","colorburn","hardlight","softlight","difference",
2727 "exclusion","hue","saturation","color","luminosity"};
2729 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2730 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2732 if(state->getBlendMode() == gfxBlendNormal)
2733 infofeature("transparency groups");
2736 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2737 warnfeature(buffer, 0);
2740 gfxresult_t*grouprecording = states[statepos].grouprecording;
2742 int blendmode = state->getBlendMode();
2743 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2744 int alpha = (int)(state->getFillOpacity()*255);
2745 if(blendmode == gfxBlendMultiply && alpha>200)
2748 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2749 gfxdevice_ops_init(&ops, this->device, alpha);
2750 gfxresult_record_replay(grouprecording, &ops);
2753 grouprecording->destroy(grouprecording);
2755 states[statepos].grouprecording = 0;
2758 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2760 if(states[statepos].softmask) {
2761 /* shouldn't happen, but *does* happen */
2762 clearSoftMask(state);
2765 /* alpha = 1: retrieve mask values from alpha layer
2766 alpha = 0: retrieve mask values from luminance */
2768 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2769 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2770 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2771 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2773 infofeature("soft masks");
2775 warnfeature("soft masks from alpha channel",0);
2777 if(states[statepos].olddevice) {
2778 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2781 states[statepos].olddevice = this->device;
2782 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2783 gfxdevice_record_init(this->device);
2785 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2787 states[statepos].softmask = 1;
2788 states[statepos].softmask_alpha = alpha;
2791 static inline Guchar div255(int x) {
2792 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2795 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2797 if(c < min) c = min;
2798 if(c > max) c = max;
2802 void GFXOutputDev::clearSoftMask(GfxState *state)
2804 if(!states[statepos].softmask)
2806 states[statepos].softmask = 0;
2807 dbg("clearSoftMask statepos=%d", statepos);
2808 msg("<verbose> clearSoftMask statepos=%d", statepos);
2810 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2811 msg("<error> Error in softmask/tgroup ordering");
2815 gfxresult_t*mask = states[statepos].softmaskrecording;
2816 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2817 this->device = states[statepos].olddevice;
2819 /* get outline of all objects below the soft mask */
2820 gfxdevice_t uniondev;
2821 gfxdevice_union_init(&uniondev, 0);
2822 gfxresult_record_replay(below, &uniondev);
2823 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2824 uniondev.finish(&uniondev);
2825 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2826 gfxline_free(belowoutline);belowoutline=0;
2828 this->device->startclip(this->device, belowoutline);
2829 gfxresult_record_replay(below, this->device);
2830 gfxresult_record_replay(mask, this->device);
2831 this->device->endclip(this->device);
2834 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2835 if(width<=0 || height<=0)
2838 gfxdevice_t belowrender;
2839 gfxdevice_render_init(&belowrender);
2840 if(states[statepos+1].isolated) {
2841 belowrender.setparameter(&belowrender, "fillwhite", "1");
2843 belowrender.setparameter(&belowrender, "antialize", "2");
2844 belowrender.startpage(&belowrender, width, height);
2845 gfxresult_record_replay(below, &belowrender);
2846 belowrender.endpage(&belowrender);
2847 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2848 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2849 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2851 gfxdevice_t maskrender;
2852 gfxdevice_render_init(&maskrender);
2853 maskrender.startpage(&maskrender, width, height);
2854 gfxresult_record_replay(mask, &maskrender);
2855 maskrender.endpage(&maskrender);
2856 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2857 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2859 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2860 msg("<fatal> Internal error in mask drawing");
2865 for(y=0;y<height;y++) {
2866 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2867 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2868 for(x=0;x<width;x++) {
2870 if(states[statepos].softmask_alpha) {
2873 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2876 l2->a = div255(alpha*l2->a);
2878 /* DON'T premultiply alpha- this is done by fillbitmap,
2879 depending on the output device */
2880 //l2->r = div255(alpha*l2->r);
2881 //l2->g = div255(alpha*l2->g);
2882 //l2->b = div255(alpha*l2->b);
2888 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2891 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2892 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2894 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2896 mask->destroy(mask);
2897 below->destroy(below);
2898 maskresult->destroy(maskresult);
2899 belowresult->destroy(belowresult);
2900 states[statepos].softmaskrecording = 0;
2905 // public: ~MemCheck()
2907 // delete globalParams;globalParams=0;
2908 // Object::memCheck(stderr);
2909 // gMemReport(stderr);