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 if(m.tx - expected_x >= m.m00*64) {
1461 msg("<debug> There's a %f (%f) pixel gap between char %d and char %d, I'm inserting a space here",
1463 (m.tx-expected_x)/m.m00,
1464 last_char, glyphid);
1466 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1467 if(m2.tx < expected_x) m2.tx = expected_x;
1468 device->drawchar(device, current_gfxfont, space, &col, &m2);
1471 last_char_gfxfont = current_gfxfont;
1472 last_char = glyphid;
1475 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1477 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1479 msg("<debug> Drawing glyph %d as shape", charid);
1480 if(!gfxglobals->textmodeinfo) {
1481 msg("<notice> Some texts will be rendered as shape");
1482 gfxglobals->textmodeinfo = 1;
1485 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1486 gfxline_t*tglyph = gfxline_clone(glyph);
1487 gfxline_transform(tglyph, &m);
1488 if((render&3) != RENDER_INVISIBLE) {
1489 gfxline_t*add = gfxline_clone(tglyph);
1490 current_text_stroke = gfxline_append(current_text_stroke, add);
1492 if(render&RENDER_CLIP) {
1493 gfxline_t*add = gfxline_clone(tglyph);
1494 current_text_clip = gfxline_append(current_text_clip, add);
1495 if(!current_text_clip) {
1496 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1499 gfxline_free(tglyph);
1503 void GFXOutputDev::endString(GfxState *state)
1505 int render = state->getRender();
1506 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1508 if(current_text_stroke) {
1509 /* fillstroke and stroke text rendering objects we can process right
1510 now (as there may be texts of other rendering modes in this
1511 text object)- clipping objects have to wait until endTextObject,
1513 device->setparameter(device, "mark","TXT");
1514 if((render&3) == RENDER_FILL) {
1515 fillGfxLine(state, current_text_stroke, 0);
1516 gfxline_free(current_text_stroke);
1517 current_text_stroke = 0;
1518 } else if((render&3) == RENDER_FILLSTROKE) {
1519 fillGfxLine(state, current_text_stroke, 0);
1520 strokeGfxline(state, current_text_stroke,0);
1521 gfxline_free(current_text_stroke);
1522 current_text_stroke = 0;
1523 } else if((render&3) == RENDER_STROKE) {
1524 strokeGfxline(state, current_text_stroke,0);
1525 gfxline_free(current_text_stroke);
1526 current_text_stroke = 0;
1528 device->setparameter(device, "mark","");
1532 void GFXOutputDev::endTextObject(GfxState *state)
1534 int render = state->getRender();
1535 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1537 if(current_text_clip) {
1538 device->setparameter(device, "mark","TXT");
1539 clipToGfxLine(state, current_text_clip, 0);
1540 device->setparameter(device, "mark","");
1541 gfxline_free(current_text_clip);
1542 current_text_clip = 0;
1546 /* the logic seems to be as following:
1547 first, beginType3Char is called, with the charcode and the coordinates.
1548 if this function returns true, it already knew about the char and has now drawn it.
1549 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1550 called with some parameters.
1551 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1552 at the position first passed to beginType3Char). the char ends with endType3Char.
1554 The drawing operations between beginType3Char and endType3Char are somewhat different to
1555 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1556 color determines the color of a font)
1559 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1561 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1564 if(config_extrafontdata && current_fontinfo) {
1566 gfxmatrix_t m = this->current_font_matrix;
1567 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1569 /*m.m00*=INTERNAL_FONT_SIZE;
1570 m.m01*=INTERNAL_FONT_SIZE;
1571 m.m10*=INTERNAL_FONT_SIZE;
1572 m.m11*=INTERNAL_FONT_SIZE;*/
1574 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1575 msg("<error> Invalid charid %d for font", charid);
1578 gfxcolor_t col={0,0,0,0};
1579 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1580 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1584 /* the character itself is going to be passed using the draw functions */
1585 return gFalse; /* gTrue= is_in_cache? */
1588 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1590 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1593 void GFXOutputDev::endType3Char(GfxState *state)
1596 msg("<debug> endType3Char");
1599 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1601 this->currentpage = pageNum;
1603 int rot = doc->getPageRotate(1);
1604 gfxcolor_t white = {255,255,255,255};
1605 gfxcolor_t black = {255,0,0,0};
1607 gfxline_t clippath[5];
1609 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1610 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1611 Use CropBox, not MediaBox, as page size
1618 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1619 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1621 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1622 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1624 this->clipmovex = -(int)x1;
1625 this->clipmovey = -(int)y1;
1627 /* apply user clip box */
1628 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1629 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1630 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1631 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1632 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1633 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1635 x1 += this->clipmovex;
1636 y1 += this->clipmovey;
1637 x2 += this->clipmovex;
1638 y2 += this->clipmovey;
1641 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1643 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);
1645 msg("<verbose> page is rotated %d degrees", rot);
1647 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1648 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1649 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1650 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1651 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1652 device->startclip(device, clippath); outer_clip_box = 1;
1653 if(!config_transparent) {
1654 device->fill(device, clippath, &white);
1656 states[statepos].clipbbox.xmin = x1;
1657 states[statepos].clipbbox.ymin = x1;
1658 states[statepos].clipbbox.xmax = x2;
1659 states[statepos].clipbbox.ymax = y2;
1661 states[statepos].dashPattern = 0;
1662 states[statepos].dashLength = 0;
1663 states[statepos].dashStart = 0;
1665 this->last_char_gfxfont = 0;
1669 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1671 double x1, y1, x2, y2;
1672 gfxline_t points[5];
1675 msg("<debug> drawlink");
1677 link->getRect(&x1, &y1, &x2, &y2);
1678 cvtUserToDev(x1, y1, &x, &y);
1679 points[0].type = gfx_moveTo;
1680 points[0].x = points[4].x = x + user_movex + clipmovex;
1681 points[0].y = points[4].y = y + user_movey + clipmovey;
1682 points[0].next = &points[1];
1683 cvtUserToDev(x2, y1, &x, &y);
1684 points[1].type = gfx_lineTo;
1685 points[1].x = x + user_movex + clipmovex;
1686 points[1].y = y + user_movey + clipmovey;
1687 points[1].next = &points[2];
1688 cvtUserToDev(x2, y2, &x, &y);
1689 points[2].type = gfx_lineTo;
1690 points[2].x = x + user_movex + clipmovex;
1691 points[2].y = y + user_movey + clipmovey;
1692 points[2].next = &points[3];
1693 cvtUserToDev(x1, y2, &x, &y);
1694 points[3].type = gfx_lineTo;
1695 points[3].x = x + user_movex + clipmovex;
1696 points[3].y = y + user_movey + clipmovey;
1697 points[3].next = &points[4];
1698 cvtUserToDev(x1, y1, &x, &y);
1699 points[4].type = gfx_lineTo;
1700 points[4].x = x + user_movex + clipmovex;
1701 points[4].y = y + user_movey + clipmovey;
1704 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1705 points[0].x, points[0].y,
1706 points[1].x, points[1].y,
1707 points[2].x, points[2].y,
1708 points[3].x, points[3].y);
1710 if(getLogLevel() >= LOGLEVEL_TRACE) {
1711 dump_outline(points);
1714 LinkAction*action=link->getAction();
1717 const char*type = "-?-";
1720 msg("<trace> drawlink action=%d", action->getKind());
1721 switch(action->getKind())
1725 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1726 LinkDest *dest=NULL;
1727 if (ha->getDest()==NULL)
1728 dest=catalog->findDest(ha->getNamedDest());
1730 dest=ha->getDest()->copy();
1732 if (dest->isPageRef()){
1733 Ref pageref=dest->getPageRef();
1734 page=catalog->findPage(pageref.num,pageref.gen);
1736 else page=dest->getPageNum();
1737 sprintf(buf, "%d", page);
1745 LinkGoToR*l = (LinkGoToR*)action;
1746 GString*g = l->getFileName();
1748 s = strdup(g->getCString());
1750 /* if the GoToR link has no filename, then
1751 try to find a refernce in the *local*
1753 GString*g = l->getNamedDest();
1755 s = strdup(g->getCString());
1761 LinkNamed*l = (LinkNamed*)action;
1762 GString*name = l->getName();
1764 s = strdup(name->lowerCase()->getCString());
1765 named = name->getCString();
1768 if(strstr(s, "next") || strstr(s, "forward"))
1770 page = currentpage + 1;
1772 else if(strstr(s, "prev") || strstr(s, "back"))
1774 page = currentpage - 1;
1776 else if(strstr(s, "last") || strstr(s, "end"))
1778 if(this->page2page && this->num_pages) {
1779 page = this->page2page[this->num_pages-1];
1782 else if(strstr(s, "first") || strstr(s, "top"))
1790 case actionLaunch: {
1792 LinkLaunch*l = (LinkLaunch*)action;
1793 GString * str = new GString(l->getFileName());
1794 GString * params = l->getParams();
1796 str->append(params);
1797 s = strdup(str->getCString());
1804 LinkURI*l = (LinkURI*)action;
1805 GString*g = l->getURI();
1807 url = g->getCString();
1812 case actionUnknown: {
1814 LinkUnknown*l = (LinkUnknown*)action;
1819 msg("<error> Unknown link type!");
1824 if(!s) s = strdup("-?-");
1826 msg("<trace> drawlink s=%s", s);
1828 if(!gfxglobals->linkinfo && (page || s))
1830 msg("<notice> File contains links");
1831 gfxglobals->linkinfo = 1;
1837 for(t=1;t<=this->num_pages;t++) {
1838 if(this->page2page[t]==page) {
1848 sprintf(buf, "page%d", lpage);
1849 device->drawlink(device, points, buf);
1853 device->drawlink(device, points, s);
1854 if(this->config_linkdatafile) {
1855 FILE*fi = fopen(config_linkdatafile, "ab+");
1856 fprintf(fi, "%s\n", s);
1861 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1865 void GFXOutputDev::saveState(GfxState *state) {
1866 dbg("saveState %08x", state); dbgindent+=2;
1868 msg("<trace> saveState %08x", state);
1871 msg("<fatal> Too many nested states in pdf.");
1875 states[statepos].state = state;
1876 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1877 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1878 states[statepos].clipping = 0;
1879 states[statepos].olddevice = 0;
1880 states[statepos].clipbbox = states[statepos-1].clipbbox;
1882 states[statepos].dashPattern = states[statepos-1].dashPattern;
1883 states[statepos].dashStart = states[statepos-1].dashStart;
1884 states[statepos].dashLength = states[statepos-1].dashLength;
1887 void GFXOutputDev::restoreState(GfxState *state) {
1888 dbgindent-=2; dbg("restoreState %08x", state);
1891 msg("<fatal> Invalid restoreState");
1894 msg("<trace> restoreState %08x%s%s", state,
1895 states[statepos].softmask?" (end softmask)":"",
1896 states[statepos].clipping?" (end clipping)":"");
1897 if(states[statepos].softmask) {
1898 clearSoftMask(state);
1901 if(states[statepos].dashPattern) {
1902 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1903 free(states[statepos].dashPattern);
1904 states[statepos].dashPattern = 0;
1910 while(states[statepos].clipping) {
1911 device->endclip(device);
1912 states[statepos].clipping--;
1914 if(states[statepos].state!=state) {
1915 msg("<fatal> bad state nesting");
1918 for(t=0;t<=statepos;t++) {
1919 printf("%08x ", states[t].state);
1925 states[statepos].state=0;
1929 void GFXOutputDev::updateLineDash(GfxState *state)
1931 if(states[statepos].dashPattern &&
1932 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1933 free(states[statepos].dashPattern);
1934 states[statepos].dashPattern = 0;
1936 double *pattern = 0;
1939 state->getLineDash(&pattern, &dashLength, &dashStart);
1940 msg("<debug> updateLineDash, %d dashes", dashLength);
1942 states[statepos].dashPattern = 0;
1943 states[statepos].dashLength = 0;
1945 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1946 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1947 states[statepos].dashPattern = p;
1948 states[statepos].dashLength = dashLength;
1949 states[statepos].dashStart = dashStart;
1953 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1955 this->page2page = page2page;
1956 this->num_pages = num_pages;
1959 void GFXOutputDev::updateLineWidth(GfxState *state)
1961 double width = state->getTransformedLineWidth();
1964 void GFXOutputDev::updateLineCap(GfxState *state)
1966 int c = state->getLineCap();
1969 void GFXOutputDev::updateLineJoin(GfxState *state)
1971 int j = state->getLineJoin();
1974 void GFXOutputDev::updateFillColor(GfxState *state)
1977 double opaq = state->getFillOpacity();
1978 state->getFillRGB(&rgb);
1980 void GFXOutputDev::updateFillOpacity(GfxState *state)
1983 double opaq = state->getFillOpacity();
1984 state->getFillRGB(&rgb);
1985 dbg("update fillopaq %f", opaq);
1987 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1989 double opaq = state->getFillOpacity();
1990 dbg("update strokeopaq %f", opaq);
1992 void GFXOutputDev::updateFillOverprint(GfxState *state)
1994 double opaq = state->getFillOverprint();
1995 dbg("update filloverprint %f", opaq);
1997 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1999 double opaq = state->getStrokeOverprint();
2000 dbg("update strokeoverprint %f", opaq);
2002 void GFXOutputDev::updateTransfer(GfxState *state)
2004 dbg("update transfer");
2008 void GFXOutputDev::updateStrokeColor(GfxState *state)
2011 double opaq = state->getStrokeOpacity();
2012 state->getStrokeRGB(&rgb);
2015 void GFXOutputDev::updateFont(GfxState *state)
2017 GfxFont* gfxFont = state->getFont();
2021 char*id = getFontID(gfxFont);
2022 msg("<verbose> Updating font to %s", id);
2023 if(gfxFont->getType() == fontType3) {
2024 infofeature("Type3 fonts");
2025 if(!config_extrafontdata) {
2030 msg("<error> Internal Error: FontID is null");
2034 this->current_fontinfo = this->info->getFont(id);
2036 if(!this->current_fontinfo) {
2037 msg("<error> Internal Error: no fontinfo for font %s", id);
2040 if(!this->current_fontinfo->seen) {
2041 dumpFontInfo("<verbose>", gfxFont);
2044 current_gfxfont = this->current_fontinfo->getGfxFont();
2045 device->addfont(device, current_gfxfont);
2048 device->addfont(device, current_gfxfont);
2050 updateFontMatrix(state);
2053 #define SQR(x) ((x)*(x))
2055 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2057 if((newwidth<1 || newheight<1) ||
2058 (width<=newwidth || height<=newheight))
2060 unsigned char*newdata;
2062 newdata= (unsigned char*)malloc(newwidth*newheight);
2063 double fx = ((double)width)/newwidth;
2064 double fy = ((double)height)/newheight;
2066 int blocksize = (int)(8192/(fx*fy));
2067 int r = 8192*256/palettesize;
2068 for(x=0;x<newwidth;x++) {
2069 double ex = px + fx;
2070 int fromx = (int)px;
2072 int xweight1 = (int)((1-(px-fromx))*256);
2073 int xweight2 = (int)((ex-tox)*256);
2075 for(y=0;y<newheight;y++) {
2076 double ey = py + fy;
2077 int fromy = (int)py;
2079 int yweight1 = (int)((1-(py-fromy))*256);
2080 int yweight2 = (int)((ey-toy)*256);
2087 for(xx=fromx;xx<=tox;xx++)
2088 for(yy=fromy;yy<=toy;yy++) {
2089 int b = 1-data[width*yy+xx];
2091 if(xx==fromx) weight = (weight*xweight1)/256;
2092 if(xx==tox) weight = (weight*xweight2)/256;
2093 if(yy==fromy) weight = (weight*yweight1)/256;
2094 if(yy==toy) weight = (weight*yweight2)/256;
2097 //if(a) a=(palettesize-1)*r/blocksize;
2098 newdata[y*newwidth+x] = (a*blocksize)/r;
2106 #define IMAGE_TYPE_JPEG 0
2107 #define IMAGE_TYPE_LOSSLESS 1
2109 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2110 double x1,double y1,
2111 double x2,double y2,
2112 double x3,double y3,
2113 double x4,double y4, int type, int multiply)
2115 gfxcolor_t*newpic=0;
2117 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2118 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2120 gfxline_t p1,p2,p3,p4,p5;
2121 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2122 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2123 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2124 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2125 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2127 {p1.x = (int)(p1.x*20)/20.0;
2128 p1.y = (int)(p1.y*20)/20.0;
2129 p2.x = (int)(p2.x*20)/20.0;
2130 p2.y = (int)(p2.y*20)/20.0;
2131 p3.x = (int)(p3.x*20)/20.0;
2132 p3.y = (int)(p3.y*20)/20.0;
2133 p4.x = (int)(p4.x*20)/20.0;
2134 p4.y = (int)(p4.y*20)/20.0;
2135 p5.x = (int)(p5.x*20)/20.0;
2136 p5.y = (int)(p5.y*20)/20.0;
2140 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2141 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2143 m.tx = p1.x - 0.5*multiply;
2144 m.ty = p1.y - 0.5*multiply;
2147 img.data = (gfxcolor_t*)data;
2151 if(type == IMAGE_TYPE_JPEG)
2152 /* TODO: pass image_dpi to device instead */
2153 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2156 dev->fillbitmap(dev, &p1, &img, &m, 0);
2159 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2160 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2162 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2165 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2166 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2168 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2172 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2173 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2174 GBool inlineImg, int mask, int*maskColors,
2175 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2177 /* the code in this function is *old*. It's not pretty, but it works. */
2179 double x1,y1,x2,y2,x3,y3,x4,y4;
2180 ImageStream *imgStr;
2185 unsigned char* maskbitmap = 0;
2188 ncomps = colorMap->getNumPixelComps();
2189 bits = colorMap->getBits();
2194 unsigned char buf[8];
2195 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2197 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2198 imgMaskStr->reset();
2199 unsigned char pal[256];
2200 int n = 1 << colorMap->getBits();
2205 maskColorMap->getGray(pixBuf, &gray);
2206 pal[t] = colToByte(gray);
2208 for (y = 0; y < maskHeight; y++) {
2209 for (x = 0; x < maskWidth; x++) {
2210 imgMaskStr->getPixel(buf);
2211 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2216 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2217 imgMaskStr->reset();
2218 for (y = 0; y < maskHeight; y++) {
2219 for (x = 0; x < maskWidth; x++) {
2220 imgMaskStr->getPixel(buf);
2222 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2230 imgStr = new ImageStream(str, width, ncomps,bits);
2233 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2235 msg("<verbose> Ignoring %d by %d image", width, height);
2236 unsigned char buf[8];
2238 for (y = 0; y < height; ++y)
2239 for (x = 0; x < width; ++x) {
2240 imgStr->getPixel(buf);
2248 this->transformXY(state, 0, 1, &x1, &y1);
2249 this->transformXY(state, 0, 0, &x2, &y2);
2250 this->transformXY(state, 1, 0, &x3, &y3);
2251 this->transformXY(state, 1, 1, &x4, &y4);
2254 /* as type 3 bitmaps are antialized, we need to place them
2255 at integer coordinates, otherwise flash player's antializing
2256 will kick in and make everything blurry */
2257 x1 = (int)(x1);y1 = (int)(y1);
2258 x2 = (int)(x2);y2 = (int)(y2);
2259 x3 = (int)(x3);y3 = (int)(y3);
2260 x4 = (int)(x4);y4 = (int)(y4);
2263 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2265 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2266 gfxglobals->pbminfo = 1;
2269 msg("<verbose> drawing %d by %d masked picture", width, height);
2271 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2272 msg("<notice> File contains jpeg pictures");
2273 gfxglobals->jpeginfo = 1;
2277 unsigned char buf[8];
2279 unsigned char*pic = new unsigned char[width*height];
2280 gfxcolor_t pal[256];
2282 state->getFillRGB(&rgb);
2284 memset(pal,255,sizeof(pal));
2285 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2286 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2287 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2288 pal[0].a = 255; pal[1].a = 0;
2291 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2292 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2293 for (y = 0; y < height; ++y)
2294 for (x = 0; x < width; ++x)
2296 imgStr->getPixel(buf);
2299 pic[width*y+x] = buf[0];
2303 unsigned char*pic2 = 0;
2306 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2315 height = realheight;
2319 /* make a black/white palette */
2321 float r = 255./(float)(numpalette-1);
2323 for(t=0;t<numpalette;t++) {
2324 pal[t].r = colToByte(rgb.r);
2325 pal[t].g = colToByte(rgb.g);
2326 pal[t].b = colToByte(rgb.b);
2327 pal[t].a = (unsigned char)(t*r);
2332 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2333 for (y = 0; y < height; ++y) {
2334 for (x = 0; x < width; ++x) {
2335 pic2[width*y+x] = pal[pic[y*width+x]];
2338 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2342 if(maskbitmap) free(maskbitmap);
2348 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2349 gfxcolor_t*pic=new gfxcolor_t[width*height];
2350 for (y = 0; y < height; ++y) {
2351 for (x = 0; x < width; ++x) {
2352 imgStr->getPixel(pixBuf);
2353 colorMap->getRGB(pixBuf, &rgb);
2354 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2355 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2356 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2357 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2359 int x1 = x*maskWidth/width;
2360 int y1 = y*maskHeight/height;
2361 int x2 = (x+1)*maskWidth/width;
2362 int y2 = (y+1)*maskHeight/height;
2364 unsigned int alpha=0;
2365 unsigned int count=0;
2366 for(xx=x1;xx<x2;xx++)
2367 for(yy=y1;yy<y2;yy++) {
2368 alpha += maskbitmap[yy*maskWidth+xx];
2372 pic[width*y+x].a = alpha / count;
2374 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2379 if(str->getKind()==strDCT)
2380 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2382 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2385 if(maskbitmap) free(maskbitmap);
2388 gfxcolor_t*pic=new gfxcolor_t[width*height];
2389 gfxcolor_t pal[256];
2390 int n = 1 << colorMap->getBits();
2392 for(t=0;t<256;t++) {
2394 colorMap->getRGB(pixBuf, &rgb);
2396 {/*if(maskColors && *maskColors==t) {
2397 msg("<notice> Color %d is transparent", t);
2398 if (imgData->maskColors) {
2400 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2401 if (pix[i] < imgData->maskColors[2*i] ||
2402 pix[i] > imgData->maskColors[2*i+1]) {
2417 pal[t].r = (unsigned char)(colToByte(rgb.r));
2418 pal[t].g = (unsigned char)(colToByte(rgb.g));
2419 pal[t].b = (unsigned char)(colToByte(rgb.b));
2420 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2423 for (y = 0; y < height; ++y) {
2424 for (x = 0; x < width; ++x) {
2425 imgStr->getPixel(pixBuf);
2426 pic[width*y+x] = pal[pixBuf[0]];
2430 if(maskWidth < width && maskHeight < height) {
2431 for(y = 0; y < height; y++) {
2432 for (x = 0; x < width; x++) {
2433 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2437 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2438 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2439 double dx = width / maskWidth;
2440 double dy = height / maskHeight;
2442 for(y = 0; y < maskHeight; y++) {
2444 for (x = 0; x < maskWidth; x++) {
2445 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2446 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2454 height = maskHeight;
2457 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2461 if(maskbitmap) free(maskbitmap);
2466 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2467 int width, int height, GBool invert,
2470 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2471 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2472 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2475 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2476 int width, int height, GfxImageColorMap *colorMap,
2477 int *maskColors, GBool inlineImg)
2479 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2480 colorMap?"colorMap":"no colorMap",
2481 maskColors?"maskColors":"no maskColors",
2483 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2484 colorMap?"colorMap":"no colorMap",
2485 maskColors?"maskColors":"no maskColors",
2488 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2489 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2490 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2493 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2494 int width, int height,
2495 GfxImageColorMap *colorMap,
2496 Stream *maskStr, int maskWidth, int maskHeight,
2499 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2500 colorMap?"colorMap":"no colorMap",
2501 maskWidth, maskHeight);
2502 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2503 colorMap?"colorMap":"no colorMap",
2504 maskWidth, maskHeight);
2506 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2507 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2508 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2511 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2512 int width, int height,
2513 GfxImageColorMap *colorMap,
2515 int maskWidth, int maskHeight,
2516 GfxImageColorMap *maskColorMap)
2518 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2519 colorMap?"colorMap":"no colorMap",
2520 maskWidth, maskHeight);
2521 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2522 colorMap?"colorMap":"no colorMap",
2523 maskWidth, maskHeight);
2525 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2526 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2527 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2530 void GFXOutputDev::stroke(GfxState *state)
2534 GfxPath * path = state->getPath();
2535 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2536 strokeGfxline(state, line, 0);
2540 void GFXOutputDev::fill(GfxState *state)
2542 gfxcolor_t col = getFillColor(state);
2543 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2545 GfxPath * path = state->getPath();
2546 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2547 if(!config_disable_polygon_conversion) {
2548 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2552 fillGfxLine(state, line, 0);
2556 void GFXOutputDev::eoFill(GfxState *state)
2558 gfxcolor_t col = getFillColor(state);
2559 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2561 GfxPath * path = state->getPath();
2562 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2563 fillGfxLine(state, line, 1);
2568 static const char* dirseparator()
2577 void addGlobalFont(const char*filename)
2579 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2580 memset(f, 0, sizeof(fontfile_t));
2581 f->filename = filename;
2582 int len = strlen(filename);
2583 char*r1 = strrchr((char*)filename, '/');
2584 char*r2 = strrchr((char*)filename, '\\');
2592 msg("<verbose> Adding font \"%s\".", filename);
2593 if(global_fonts_next) {
2594 global_fonts_next->next = f;
2595 global_fonts_next = global_fonts_next->next;
2597 global_fonts_next = global_fonts = f;
2601 void addGlobalLanguageDir(const char*dir)
2603 msg("<notice> Adding %s to language pack directories", dir);
2606 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2607 strcpy(config_file, dir);
2608 strcat(config_file, dirseparator());
2609 strcat(config_file, "add-to-xpdfrc");
2611 fi = fopen(config_file, "rb");
2613 msg("<error> Could not open %s", config_file);
2616 globalParams->parseFile(new GString(config_file), fi);
2620 void addGlobalFontDir(const char*dirname)
2622 #ifdef HAVE_DIRENT_H
2623 DIR*dir = opendir(dirname);
2625 msg("<warning> Couldn't open directory %s", dirname);
2631 ent = readdir (dir);
2635 char*name = ent->d_name;
2641 if(!strncasecmp(&name[l-4], ".pfa", 4))
2643 if(!strncasecmp(&name[l-4], ".pfb", 4))
2645 if(!strncasecmp(&name[l-4], ".ttf", 4))
2648 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2649 strcpy(fontname, dirname);
2650 strcat(fontname, dirseparator());
2651 strcat(fontname, name);
2652 addGlobalFont(fontname);
2656 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2659 msg("<warning> No dirent.h");
2663 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2664 GfxColorSpace *blendingColorSpace,
2665 GBool isolated, GBool knockout,
2668 const char*colormodename = "";
2670 if(blendingColorSpace) {
2671 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2673 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);
2674 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);
2676 //states[statepos].createsoftmask |= forSoftMask;
2677 states[statepos].createsoftmask = forSoftMask;
2678 states[statepos].transparencygroup = !forSoftMask;
2679 states[statepos].isolated = isolated;
2681 states[statepos].olddevice = this->device;
2682 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2683 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2685 gfxdevice_record_init(this->device);
2687 /*if(!forSoftMask) { ////???
2688 state->setFillOpacity(0.0);
2693 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2696 gfxdevice_t*r = this->device;
2698 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2700 this->device = states[statepos].olddevice;
2702 msg("<error> Invalid state nesting");
2704 states[statepos].olddevice = 0;
2706 gfxresult_t*recording = r->finish(r);
2708 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2709 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2711 if(states[statepos].createsoftmask) {
2712 states[statepos-1].softmaskrecording = recording;
2714 states[statepos-1].grouprecording = recording;
2717 states[statepos].createsoftmask = 0;
2718 states[statepos].transparencygroup = 0;
2722 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2724 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2725 "colordodge","colorburn","hardlight","softlight","difference",
2726 "exclusion","hue","saturation","color","luminosity"};
2728 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2729 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2731 if(state->getBlendMode() == gfxBlendNormal)
2732 infofeature("transparency groups");
2735 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2736 warnfeature(buffer, 0);
2739 gfxresult_t*grouprecording = states[statepos].grouprecording;
2741 int blendmode = state->getBlendMode();
2742 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2743 int alpha = (int)(state->getFillOpacity()*255);
2744 if(blendmode == gfxBlendMultiply && alpha>200)
2747 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2748 gfxdevice_ops_init(&ops, this->device, alpha);
2749 gfxresult_record_replay(grouprecording, &ops);
2752 grouprecording->destroy(grouprecording);
2754 states[statepos].grouprecording = 0;
2757 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2759 if(states[statepos].softmask) {
2760 /* shouldn't happen, but *does* happen */
2761 clearSoftMask(state);
2764 /* alpha = 1: retrieve mask values from alpha layer
2765 alpha = 0: retrieve mask values from luminance */
2767 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2768 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2769 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2770 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2772 infofeature("soft masks");
2774 warnfeature("soft masks from alpha channel",0);
2776 if(states[statepos].olddevice) {
2777 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2780 states[statepos].olddevice = this->device;
2781 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2782 gfxdevice_record_init(this->device);
2784 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2786 states[statepos].softmask = 1;
2787 states[statepos].softmask_alpha = alpha;
2790 static inline Guchar div255(int x) {
2791 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2794 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2796 if(c < min) c = min;
2797 if(c > max) c = max;
2801 void GFXOutputDev::clearSoftMask(GfxState *state)
2803 if(!states[statepos].softmask)
2805 states[statepos].softmask = 0;
2806 dbg("clearSoftMask statepos=%d", statepos);
2807 msg("<verbose> clearSoftMask statepos=%d", statepos);
2809 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2810 msg("<error> Error in softmask/tgroup ordering");
2814 gfxresult_t*mask = states[statepos].softmaskrecording;
2815 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2816 this->device = states[statepos].olddevice;
2818 /* get outline of all objects below the soft mask */
2819 gfxdevice_t uniondev;
2820 gfxdevice_union_init(&uniondev, 0);
2821 gfxresult_record_replay(below, &uniondev);
2822 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2823 uniondev.finish(&uniondev);
2824 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2825 gfxline_free(belowoutline);belowoutline=0;
2827 this->device->startclip(this->device, belowoutline);
2828 gfxresult_record_replay(below, this->device);
2829 gfxresult_record_replay(mask, this->device);
2830 this->device->endclip(this->device);
2833 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2834 if(width<=0 || height<=0)
2837 gfxdevice_t belowrender;
2838 gfxdevice_render_init(&belowrender);
2839 if(states[statepos+1].isolated) {
2840 belowrender.setparameter(&belowrender, "fillwhite", "1");
2842 belowrender.setparameter(&belowrender, "antialize", "2");
2843 belowrender.startpage(&belowrender, width, height);
2844 gfxresult_record_replay(below, &belowrender);
2845 belowrender.endpage(&belowrender);
2846 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2847 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2848 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2850 gfxdevice_t maskrender;
2851 gfxdevice_render_init(&maskrender);
2852 maskrender.startpage(&maskrender, width, height);
2853 gfxresult_record_replay(mask, &maskrender);
2854 maskrender.endpage(&maskrender);
2855 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2856 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2858 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2859 msg("<fatal> Internal error in mask drawing");
2864 for(y=0;y<height;y++) {
2865 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2866 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2867 for(x=0;x<width;x++) {
2869 if(states[statepos].softmask_alpha) {
2872 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2875 l2->a = div255(alpha*l2->a);
2877 /* DON'T premultiply alpha- this is done by fillbitmap,
2878 depending on the output device */
2879 //l2->r = div255(alpha*l2->r);
2880 //l2->g = div255(alpha*l2->g);
2881 //l2->b = div255(alpha*l2->b);
2887 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2890 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2891 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2893 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2895 mask->destroy(mask);
2896 below->destroy(below);
2897 maskresult->destroy(maskresult);
2898 belowresult->destroy(belowresult);
2899 states[statepos].softmaskrecording = 0;
2904 // public: ~MemCheck()
2906 // delete globalParams;globalParams=0;
2907 // Object::memCheck(stderr);
2908 // gMemReport(stderr);