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;
100 static char* lastfontdir = 0;
111 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
112 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
113 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
114 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
115 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
116 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
117 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
118 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
119 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
120 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
121 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
122 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
123 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
124 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
127 static int verbose = 0;
128 static int dbgindent = 1;
129 static void dbg(const char*format, ...)
136 va_start(arglist, format);
137 vsprintf(buf, format, arglist);
140 while(l && buf[l-1]=='\n') {
145 int indent = dbgindent;
155 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
157 feature_t*f = this->featurewarnings;
159 if(!strcmp(feature, f->string))
163 f = (feature_t*)malloc(sizeof(feature_t));
164 f->string = strdup(feature);
165 f->next = this->featurewarnings;
166 this->featurewarnings = f;
168 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
169 if(this->config_break_on_warning) {
170 msg("<fatal> Aborting conversion due to unsupported feature");
174 msg("<notice> File contains %s",feature);
177 void GFXOutputDev::warnfeature(const char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(const char*feature)
183 showfeature(feature,0,0);
186 GFXOutputState::GFXOutputState() {
188 this->createsoftmask = 0;
189 this->transparencygroup = 0;
191 this->grouprecording = 0;
195 GBool GFXOutputDev::interpretType3Chars()
200 typedef struct _drawnchar
218 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
219 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224 free(chars);chars = 0;
231 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
235 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
238 chars[num_chars].x = x;
239 chars[num_chars].y = y;
240 chars[num_chars].color = color;
241 chars[num_chars].charid = charid;
245 char* writeOutStdFont(fontentry* f)
250 char* tmpFileName = mktmpname(namebuf1);
252 sprintf(namebuf2, "%s.afm", tmpFileName);
253 fi = fopen(namebuf2, "wb");
256 fwrite(f->afm, 1, f->afmlen, fi);
259 sprintf(namebuf2, "%s.pfb", tmpFileName);
260 fi = fopen(namebuf2, "wb");
263 fwrite(f->pfb, 1, f->pfblen, fi);
265 return strdup(namebuf2);
267 void unlinkfont(char* filename)
272 msg("<verbose> Removing temporary font file %s", filename);
275 if(!strncmp(&filename[l-4],".afm",4)) {
276 memcpy(&filename[l-4],".pfb",4); unlink(filename);
277 memcpy(&filename[l-4],".pfa",4); unlink(filename);
278 memcpy(&filename[l-4],".afm",4);
281 if(!strncmp(&filename[l-4],".pfa",4)) {
282 memcpy(&filename[l-4],".afm",4); unlink(filename);
283 memcpy(&filename[l-4],".pfa",4);
286 if(!strncmp(&filename[l-4],".pfb",4)) {
287 memcpy(&filename[l-4],".afm",4); unlink(filename);
288 memcpy(&filename[l-4],".pfb",4);
293 static int config_use_fontconfig = 1;
294 static int fcinitcalled = 0;
296 GFXGlobalParams::GFXGlobalParams()
299 //setupBaseFonts(char *dir); //not tested yet
301 GFXGlobalParams::~GFXGlobalParams()
303 msg("<verbose> Performing cleanups");
305 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
306 if(pdf2t1map[t].fullfilename) {
307 unlinkfont(pdf2t1map[t].fullfilename);
310 #ifdef HAVE_FONTCONFIG
311 if(config_use_fontconfig && fcinitcalled)
315 #ifdef HAVE_FONTCONFIG
316 static char fc_ismatch(FcPattern*match, char*family, char*style)
318 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
319 FcBool scalable=FcFalse, outline=FcFalse;
320 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
321 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
322 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
323 FcPatternGetBool(match, "outline", 0, &outline);
324 FcPatternGetBool(match, "scalable", 0, &scalable);
326 if(scalable!=FcTrue || outline!=FcTrue)
329 if (!strcasecmp(fcfamily, family)) {
330 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
333 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
339 char* fontconfig_searchForFont(char*name)
341 #ifdef HAVE_FONTCONFIG
342 if(!config_use_fontconfig)
345 // call init ony once
349 // check whether we have a config file
350 char* configfile = (char*)FcConfigFilename(0);
351 int configexists = 0;
352 FILE*fi = fopen(configfile, "rb");
354 configexists = 1;fclose(fi);
355 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
357 msg("<debug> Initializing FontConfig (no configfile)");
361 /* A fontconfig instance which didn't find a configfile is unbelievably
362 cranky, so let's just write out a small xml file and make fontconfig
364 FcConfig*c = FcConfigCreate();
366 char* tmpFileName = mktmpname(namebuf);
367 FILE*fi = fopen(tmpFileName, "wb");
368 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
370 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
372 fprintf(fi, "<dir>~/.fonts</dir>\n");
374 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
376 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
377 fprintf(fi, "</fontconfig>\n");
379 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
380 FcConfigBuildFonts(c);
381 FcConfigSetCurrent(c);
385 msg("<debug> FontConfig Initialization failed. Disabling.");
386 config_use_fontconfig = 0;
389 FcConfig * config = FcConfigGetCurrent();
391 msg("<debug> FontConfig Config Initialization failed. Disabling.");
392 config_use_fontconfig = 0;
395 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
396 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
397 if(!set || !set->nfont) {
398 msg("<debug> FontConfig has zero fonts. Disabling.");
399 config_use_fontconfig = 0;
403 if(getLogLevel() >= LOGLEVEL_TRACE) {
405 for(t=0;t<set->nfont;t++) {
406 char*fcfamily=0,*fcstyle=0,*filename=0;
407 FcBool scalable=FcFalse, outline=FcFalse;
408 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
409 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
410 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
411 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
412 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
413 if(scalable && outline) {
414 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
420 char*family = strdup(name);
422 char*dash = strchr(family, '-');
423 FcPattern*pattern = 0;
427 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
428 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
430 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
431 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
435 FcConfigSubstitute(0, pattern, FcMatchPattern);
436 FcDefaultSubstitute(pattern);
438 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
441 for(t=0;t<set->nfont;t++) {
442 FcPattern*match = set->fonts[t];
443 //FcPattern*match = FcFontMatch(0, pattern, &result);
444 if(fc_ismatch(match, family, style)) {
446 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
447 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
450 //FcPatternDestroy(match);
451 msg("<debug> fontconfig: returning filename %s", filename);
453 FcPatternDestroy(pattern);
454 FcFontSetDestroy(set);
455 return filename?strdup(filename):0;
460 FcPatternDestroy(pattern);
461 FcFontSetDestroy(set);
468 static DisplayFontParamKind detectFontType(const char*filename)
470 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
471 return displayFontTT;
472 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
473 return displayFontT1;
474 return displayFontTT;
477 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
479 msg("<verbose> looking for font %s in global params", fontName->getCString());
481 char*name = fontName->getCString();
483 /* see if it is a pdf standard font */
485 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
486 if(!strcmp(name, pdf2t1map[t].pdffont)) {
487 if(!pdf2t1map[t].fullfilename) {
488 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
489 if(!pdf2t1map[t].fullfilename) {
490 msg("<error> Couldn't save default font- is the Temp Directory writable?");
492 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
495 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
496 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
501 int bestlen = 0x7fffffff;
502 const char*bestfilename = 0;
504 fontfile_t*f = global_fonts;
506 if(strstr(f->filename, name)) {
507 if(f->len < bestlen) {
509 bestfilename = f->filename;
515 /* if we didn't find anything up to now, try looking for the
516 font via fontconfig */
519 filename = fontconfig_searchForFont(name);
521 filename = strdup(bestfilename);
525 DisplayFontParamKind kind = detectFontType(filename);
526 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
527 if(kind == displayFontTT) {
528 dfp->tt.fileName = new GString(filename);
530 dfp->t1.fileName = new GString(filename);
535 return GlobalParams::getDisplayFont(fontName);
538 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
542 this->xref = doc->getXRef();
545 this->textmodeinfo = 0;
548 this->type3active = 0;
551 this->substitutepos = 0;
552 this->type3Warning = 0;
553 this->user_movex = 0;
554 this->user_movey = 0;
557 this->user_clipx1 = 0;
558 this->user_clipy1 = 0;
559 this->user_clipx2 = 0;
560 this->user_clipy2 = 0;
561 this->current_text_stroke = 0;
562 this->current_text_clip = 0;
563 this->outer_clip_box = 0;
565 this->pagebuflen = 0;
567 this->config_convertgradients=1;
568 this->config_break_on_warning=0;
569 this->config_remapunicode=0;
570 this->config_transparent=0;
571 this->config_extrafontdata = 0;
572 this->config_fontquality = 10;
573 this->config_optimize_polygons = 0;
575 this->gfxfontlist = gfxfontlist_create();
577 memset(states, 0, sizeof(states));
578 this->featurewarnings = 0;
581 void GFXOutputDev::setParameter(const char*key, const char*value)
583 if(!strcmp(key,"breakonwarning")) {
584 this->config_break_on_warning = atoi(value);
585 } else if(!strcmp(key,"remapunicode")) {
586 this->config_remapunicode = atoi(value);
587 } else if(!strcmp(key,"transparent")) {
588 this->config_transparent = atoi(value);
589 } else if(!strcmp(key,"extrafontdata")) {
590 this->config_extrafontdata = atoi(value);
591 } else if(!strcmp(key,"convertgradients")) {
592 this->config_convertgradients = atoi(value);
593 } else if(!strcmp(key,"optimize_polygons")) {
594 this->config_optimize_polygons = atoi(value);
595 } else if(!strcmp(key,"fontquality")) {
596 this->config_fontquality = atof(value);
597 if(this->config_fontquality<=1)
598 this->config_fontquality=1;
603 void GFXOutputDev::setDevice(gfxdevice_t*dev)
608 void GFXOutputDev::setMove(int x,int y)
610 this->user_movex = x;
611 this->user_movey = y;
614 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
616 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
617 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
619 this->user_clipx1 = x1;
620 this->user_clipy1 = y1;
621 this->user_clipx2 = x2;
622 this->user_clipy2 = y2;
625 static char*getFontName(GfxFont*font)
628 GString*gstr = font->getName();
629 char* fname = gstr==0?0:gstr->getCString();
633 sprintf(buf, "UFONT%d", r->num);
634 fontid = strdup(buf);
636 fontid = strdup(fname);
640 char* plus = strchr(fontid, '+');
641 if(plus && plus < &fontid[strlen(fontid)-1]) {
642 fontname = strdup(plus+1);
644 fontname = strdup(fontid);
650 static void dumpFontInfo(const char*loglevel, GfxFont*font);
651 static int lastdumps[1024];
652 static int lastdumppos = 0;
657 static void showFontError(GfxFont*font, int nr)
661 for(t=0;t<lastdumppos;t++)
662 if(lastdumps[t] == r->num)
666 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
667 lastdumps[lastdumppos++] = r->num;
669 msg("<warning> The following font caused problems:");
671 msg("<warning> The following font caused problems (substituting):");
673 msg("<warning> The following Type 3 Font will be rendered as graphics:");
674 dumpFontInfo("<warning>", font);
677 static void dumpFontInfo(const char*loglevel, GfxFont*font)
679 char* id = getFontID(font);
680 char* name = getFontName(font);
681 Ref* r=font->getID();
682 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
684 GString*gstr = font->getTag();
686 msg("%s| Tag: %s", loglevel, id);
688 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
690 GfxFontType type=font->getType();
692 case fontUnknownType:
693 msg("%s| Type: unknown",loglevel);
696 msg("%s| Type: 1",loglevel);
699 msg("%s| Type: 1C",loglevel);
702 msg("%s| Type: 3",loglevel);
705 msg("%s| Type: TrueType",loglevel);
708 msg("%s| Type: CIDType0",loglevel);
711 msg("%s| Type: CIDType0C",loglevel);
714 msg("%s| Type: CIDType2",loglevel);
719 GBool embedded = font->getEmbeddedFontID(&embRef);
721 if(font->getEmbeddedFontName()) {
722 embeddedName = font->getEmbeddedFontName()->getCString();
725 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
727 gstr = font->getExtFontFile();
729 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
731 // Get font descriptor flags.
732 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
733 if(font->isSerif()) msg("%s| is serif", loglevel);
734 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
735 if(font->isItalic()) msg("%s| is italic", loglevel);
736 if(font->isBold()) msg("%s| is bold", loglevel);
742 //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");}
743 //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");}
745 void dump_outline(gfxline_t*line)
748 if(line->type == gfx_moveTo) {
749 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
750 } else if(line->type == gfx_lineTo) {
751 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
752 } else if(line->type == gfx_splineTo) {
753 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
759 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
761 int num = path->getNumSubpaths();
764 double lastx=0,lasty=0,posx=0,posy=0;
767 msg("<warning> empty path");
771 gfxdrawer_target_gfxline(&draw);
773 for(t = 0; t < num; t++) {
774 GfxSubpath *subpath = path->getSubpath(t);
775 int subnum = subpath->getNumPoints();
776 double bx=0,by=0,cx=0,cy=0;
778 for(s=0;s<subnum;s++) {
781 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
784 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
785 draw.lineTo(&draw, lastx, lasty);
787 draw.moveTo(&draw, x,y);
792 } else if(subpath->getCurve(s) && cpos==0) {
796 } else if(subpath->getCurve(s) && cpos==1) {
804 draw.lineTo(&draw, x,y);
806 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
813 /* fix non-closed lines */
814 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
815 draw.lineTo(&draw, lastx, lasty);
817 gfxline_t*result = (gfxline_t*)draw.result(&draw);
819 gfxline_optimize(result);
824 GBool GFXOutputDev::useTilingPatternFill()
826 infofeature("tiled patterns");
827 // if(config_convertgradients)
831 GBool GFXOutputDev::useShadedFills()
833 infofeature("shaded fills");
834 if(config_convertgradients)
839 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
841 state->transform(x,y,nx,ny);
842 *nx += user_movex + clipmovex;
843 *ny += user_movey + clipmovey;
847 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
848 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
849 int paintType, Dict *resDict,
850 double *mat, double *bbox,
851 int x0, int y0, int x1, int y1,
852 double xStep, double yStep)
854 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
855 int paintType, Dict *resDict,
856 double *mat, double *bbox,
857 int x0, int y0, int x1, int y1,
858 double xStep, double yStep)
861 msg("<debug> tilingPatternFill");
862 infofeature("tiling pattern fills");
865 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
867 msg("<error> functionShadedFill not supported yet");
868 infofeature("function shaded fills");
871 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
875 colspace->getRGB(col, &rgb);
876 c.r = colToByte(rgb.r);
877 c.g = colToByte(rgb.g);
878 c.b = colToByte(rgb.b);
883 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
885 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
886 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
889 this->transformXY(state, x0,y0, &x0,&y0);
890 this->transformXY(state, x1,y1, &x1,&y1);
891 this->transformXY(state, x2,y2, &x2,&y2);
896 shading->getColor(0.0, &color0);
897 shading->getColor(0.5, &color1);
898 shading->getColor(1.0, &color2);
900 GfxColorSpace* colspace = shading->getColorSpace();
902 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
903 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
904 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
905 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
906 infofeature("radial shaded fills");
908 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
912 g[0].color = col2col(colspace, &color0);
913 g[1].color = col2col(colspace, &color1);
914 g[2].color = col2col(colspace, &color2);
919 gfxbbox_t b = states[statepos].clipbbox;
920 gfxline_t p1,p2,p3,p4,p5;
921 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
922 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
923 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
924 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
925 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
928 //m.m00 = (x3-x0); m.m10 = (x1-x0);
929 //m.m01 = (y3-y0); m.m11 = (y1-y0);
930 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
931 m.m00 = (x1-x0); m.m10 = (x2-x0);
932 m.m01 = (y1-y0); m.m11 = (y2-y0);
936 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
940 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
943 shading->getCoords(&x0,&y0,&x1,&y1);
944 this->transformXY(state, x0,y0,&x0,&y0);
945 this->transformXY(state, x1,y1,&x1,&y1);
950 shading->getColor(0.0, &color0);
951 shading->getColor(0.5, &color1);
952 shading->getColor(1.0, &color2);
954 GfxColorSpace* colspace = shading->getColorSpace();
956 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
957 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
958 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
959 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
961 infofeature("axial shaded fills");
963 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
967 g[0].color = col2col(colspace, &color0);
968 g[1].color = col2col(colspace, &color1);
969 g[2].color = col2col(colspace, &color2);
974 double xMin,yMin,xMax,yMax;
975 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
976 this->transformXY(state, xMin, yMin, &xMin, &yMin);
977 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
980 xMin = 1024; yMin = 1024;
982 gfxbbox_t b = states[statepos].clipbbox;
983 gfxline_t p1,p2,p3,p4,p5;
984 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
985 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
986 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
987 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
988 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
990 /* the gradient starts at (-1.0,0.0), so move (0,0) to
991 the middle of the two control points */
993 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
994 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
995 m.tx = (x0 + x1)/2 - 0.5;
996 m.ty = (y0 + y1)/2 - 0.5;
998 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1002 GBool GFXOutputDev::useDrawForm()
1004 infofeature("forms");
1007 void GFXOutputDev::drawForm(Ref id)
1009 msg("<error> drawForm not implemented");
1011 GBool GFXOutputDev::needNonText()
1015 void GFXOutputDev::endPage()
1017 msg("<verbose> endPage (GfxOutputDev)");
1018 if(outer_clip_box) {
1019 device->endclip(device);
1022 this->dashPattern = 0;
1023 /* notice: we're not fully done yet with this page- there might still be
1024 a few calls to drawLink() yet to come */
1027 static inline double sqr(double x) {return x*x;}
1029 #define STROKE_FILL 1
1030 #define STROKE_CLIP 2
1031 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1033 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1034 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1035 double miterLimit = state->getMiterLimit();
1036 double width = state->getTransformedLineWidth();
1039 double opaq = state->getStrokeOpacity();
1041 state->getFillRGB(&rgb);
1043 state->getStrokeRGB(&rgb);
1045 col.r = colToByte(rgb.r);
1046 col.g = colToByte(rgb.g);
1047 col.b = colToByte(rgb.b);
1048 col.a = (unsigned char)(opaq*255);
1050 gfx_capType capType = gfx_capRound;
1051 if(lineCap == 0) capType = gfx_capButt;
1052 else if(lineCap == 1) capType = gfx_capRound;
1053 else if(lineCap == 2) capType = gfx_capSquare;
1055 gfx_joinType joinType = gfx_joinRound;
1056 if(lineJoin == 0) joinType = gfx_joinMiter;
1057 else if(lineJoin == 1) joinType = gfx_joinRound;
1058 else if(lineJoin == 2) joinType = gfx_joinBevel;
1060 gfxline_t*line2 = 0;
1062 if(this->dashLength && this->dashPattern) {
1063 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1066 /* try to find out how much the transformation matrix would
1067 stretch the dashes, and factor that into the dash lengths.
1068 This is not the entirely correct approach- it would be
1069 better to first convert the path to an unscaled version,
1070 then apply dashing, and then transform the path using
1071 the current transformation matrix. However there are few
1072 PDFs which actually stretch a dashed path in a non-orthonormal
1074 double tx1, ty1, tx2, ty2;
1075 this->transformXY(state, 0, 0, &tx1, &ty1);
1076 this->transformXY(state, 1, 1, &tx2, &ty2);
1077 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1079 msg("<trace> %d dashes", this->dashLength);
1080 msg("<trace> | phase: %f", this->dashStart);
1081 for(t=0;t<this->dashLength;t++) {
1082 dash[t] = (float)this->dashPattern[t] * f;
1083 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1085 dash[this->dashLength] = -1;
1086 if(getLogLevel() >= LOGLEVEL_TRACE) {
1090 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1093 msg("<trace> After dashing:");
1096 if(getLogLevel() >= LOGLEVEL_TRACE) {
1097 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1099 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1100 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1102 col.r,col.g,col.b,col.a
1107 if(flags&STROKE_FILL) {
1108 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1109 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1110 if(getLogLevel() >= LOGLEVEL_TRACE) {
1111 dump_outline(gfxline);
1114 msg("<warning> Empty polygon (resulting from stroked line)");
1116 if(flags&STROKE_CLIP) {
1117 device->startclip(device, gfxline);
1118 states[statepos].clipping++;
1120 device->fill(device, gfxline, &col);
1122 gfxline_free(gfxline);
1125 if(flags&STROKE_CLIP)
1126 msg("<error> Stroke&clip not supported at the same time");
1127 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1131 gfxline_free(line2);
1134 gfxcolor_t getFillColor(GfxState * state)
1137 double opaq = state->getFillOpacity();
1138 state->getFillRGB(&rgb);
1140 col.r = colToByte(rgb.r);
1141 col.g = colToByte(rgb.g);
1142 col.b = colToByte(rgb.b);
1143 col.a = (unsigned char)(opaq*255);
1147 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1149 gfxcolor_t col = getFillColor(state);
1151 if(getLogLevel() >= LOGLEVEL_TRACE) {
1152 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1155 device->fill(device, line, &col);
1158 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1160 if(getLogLevel() >= LOGLEVEL_TRACE) {
1163 gfxbbox_t bbox = gfxline_getbbox(line);
1164 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1166 device->startclip(device, line);
1167 states[statepos].clipping++;
1170 void GFXOutputDev::clip(GfxState *state)
1172 GfxPath * path = state->getPath();
1173 msg("<trace> clip");
1174 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1175 if(config_optimize_polygons) {
1176 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1180 clipToGfxLine(state, line);
1184 void GFXOutputDev::eoClip(GfxState *state)
1186 GfxPath * path = state->getPath();
1187 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1188 clipToGfxLine(state, line);
1191 void GFXOutputDev::clipToStrokePath(GfxState *state)
1193 GfxPath * path = state->getPath();
1194 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1196 if(getLogLevel() >= LOGLEVEL_TRACE) {
1197 double width = state->getTransformedLineWidth();
1198 msg("<trace> cliptostrokepath width=%f", width);
1202 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1206 void GFXOutputDev::finish()
1208 if(outer_clip_box) {
1210 device->endclip(device);
1216 GFXOutputDev::~GFXOutputDev()
1221 free(this->pages); this->pages = 0;
1224 feature_t*f = this->featurewarnings;
1226 feature_t*next = f->next;
1228 free(f->string);f->string =0;
1234 this->featurewarnings = 0;
1236 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1238 GBool GFXOutputDev::upsideDown()
1242 GBool GFXOutputDev::useDrawChar()
1247 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1248 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1250 static char tmp_printstr[4096];
1251 char* makeStringPrintable(char*str)
1253 int len = strlen(str);
1260 for(t=0;t<len;t++) {
1265 tmp_printstr[t] = c;
1268 tmp_printstr[len++] = '.';
1269 tmp_printstr[len++] = '.';
1270 tmp_printstr[len++] = '.';
1272 tmp_printstr[len] = 0;
1273 return tmp_printstr;
1275 #define INTERNAL_FONT_SIZE 1024.0
1276 void GFXOutputDev::updateFontMatrix(GfxState*state)
1278 double* ctm = state->getCTM();
1279 double fontSize = state->getFontSize();
1280 double*textMat = state->getTextMat();
1282 /* taking the absolute value of horizScaling seems to be required for
1283 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1284 double hscale = fabs(state->getHorizScaling());
1286 // from xpdf-3.02/SplashOutputDev:updateFont
1287 double mm11 = textMat[0] * fontSize * hscale;
1288 double mm12 = textMat[1] * fontSize * hscale;
1289 double mm21 = textMat[2] * fontSize;
1290 double mm22 = textMat[3] * fontSize;
1292 // multiply with ctm, like state->getFontTransMat() does
1293 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1294 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1295 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1296 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1297 this->current_font_matrix.tx = 0;
1298 this->current_font_matrix.ty = 0;
1301 void GFXOutputDev::beginString(GfxState *state, GString *s)
1303 int render = state->getRender();
1304 if(current_text_stroke) {
1305 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1308 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1311 static gfxline_t* mkEmptyGfxShape(double x, double y)
1313 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1314 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1318 static char isValidUnicode(int c)
1320 if(c>=32 && c<0x2fffe)
1325 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1326 double dx, double dy,
1327 double originX, double originY,
1328 CharCode charid, int nBytes, Unicode *_u, int uLen)
1330 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1331 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1335 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1337 int render = state->getRender();
1338 gfxcolor_t col = getFillColor(state);
1340 // check for invisible text -- this is used by Acrobat Capture
1341 if (render == RENDER_INVISIBLE) {
1343 if(!config_extrafontdata)
1347 GfxFont*font = state->getFont();
1349 if(font->getType() == fontType3) {
1350 /* type 3 chars are passed as graphics */
1351 msg("<debug> type3 char at %f/%f", x, y);
1355 Unicode u = uLen?(_u[0]):0;
1356 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d font=%08x",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
1358 gfxmatrix_t m = this->current_font_matrix;
1359 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1360 m.tx += originX; m.ty += originY;
1362 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1363 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1365 msg("<debug> Drawing glyph %d as shape", charid);
1367 msg("<notice> Some texts will be rendered as shape");
1370 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1371 gfxline_t*tglyph = gfxline_clone(glyph);
1372 gfxline_transform(tglyph, &m);
1373 if((render&3) != RENDER_INVISIBLE) {
1374 gfxline_t*add = gfxline_clone(tglyph);
1375 current_text_stroke = gfxline_append(current_text_stroke, add);
1377 if(render&RENDER_CLIP) {
1378 gfxline_t*add = gfxline_clone(tglyph);
1379 current_text_clip = gfxline_append(current_text_clip, add);
1380 if(!current_text_clip) {
1381 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1384 gfxline_free(tglyph);
1388 void GFXOutputDev::endString(GfxState *state)
1390 int render = state->getRender();
1391 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1393 if(current_text_stroke) {
1394 /* fillstroke and stroke text rendering objects we can process right
1395 now (as there may be texts of other rendering modes in this
1396 text object)- clipping objects have to wait until endTextObject,
1398 device->setparameter(device, "mark","TXT");
1399 if((render&3) == RENDER_FILL) {
1400 fillGfxLine(state, current_text_stroke);
1401 gfxline_free(current_text_stroke);
1402 current_text_stroke = 0;
1403 } else if((render&3) == RENDER_FILLSTROKE) {
1404 fillGfxLine(state, current_text_stroke);
1405 strokeGfxline(state, current_text_stroke,0);
1406 gfxline_free(current_text_stroke);
1407 current_text_stroke = 0;
1408 } else if((render&3) == RENDER_STROKE) {
1409 strokeGfxline(state, current_text_stroke,0);
1410 gfxline_free(current_text_stroke);
1411 current_text_stroke = 0;
1413 device->setparameter(device, "mark","");
1417 void GFXOutputDev::endTextObject(GfxState *state)
1419 int render = state->getRender();
1420 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1422 if(current_text_clip) {
1423 device->setparameter(device, "mark","TXT");
1424 clipToGfxLine(state, current_text_clip);
1425 device->setparameter(device, "mark","");
1426 gfxline_free(current_text_clip);
1427 current_text_clip = 0;
1431 /* the logic seems to be as following:
1432 first, beginType3Char is called, with the charcode and the coordinates.
1433 if this function returns true, it already knew about the char and has now drawn it.
1434 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1435 called with some parameters.
1436 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1437 at the position first passed to beginType3Char). the char ends with endType3Char.
1439 The drawing operations between beginType3Char and endType3Char are somewhat different to
1440 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1441 color determines the color of a font)
1444 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1446 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1449 if(config_extrafontdata && current_fontinfo) {
1451 gfxmatrix_t m = this->current_font_matrix;
1452 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1453 m.m00*=INTERNAL_FONT_SIZE;
1454 m.m01*=INTERNAL_FONT_SIZE;
1455 m.m10*=INTERNAL_FONT_SIZE;
1456 m.m11*=INTERNAL_FONT_SIZE;
1458 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1459 msg("<error> Invalid charid %d for font", charid);
1462 gfxcolor_t col={0,0,0,0};
1463 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1464 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1468 /* the character itself is going to be passed using the draw functions */
1469 return gFalse; /* gTrue= is_in_cache? */
1472 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1474 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1477 void GFXOutputDev::endType3Char(GfxState *state)
1480 msg("<debug> endType3Char");
1483 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1485 this->currentpage = pageNum;
1487 int rot = doc->getPageRotate(1);
1488 gfxcolor_t white = {255,255,255,255};
1489 gfxcolor_t black = {255,0,0,0};
1491 gfxline_t clippath[5];
1493 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1494 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1495 Use CropBox, not MediaBox, as page size
1502 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1503 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1505 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1506 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1508 this->clipmovex = -(int)x1;
1509 this->clipmovey = -(int)y1;
1511 /* apply user clip box */
1512 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1513 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1514 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1515 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1516 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1517 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1520 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1522 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);
1524 msg("<verbose> page is rotated %d degrees", rot);
1526 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1527 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1528 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1529 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1530 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1531 device->startclip(device, clippath); outer_clip_box = 1;
1532 if(!config_transparent) {
1533 device->fill(device, clippath, &white);
1535 states[statepos].clipbbox.xmin = x1;
1536 states[statepos].clipbbox.ymin = x1;
1537 states[statepos].clipbbox.xmax = x2;
1538 states[statepos].clipbbox.ymax = y2;
1540 this->dashPattern = 0;
1541 this->dashLength = 0;
1542 this->dashStart = 0;
1546 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1548 double x1, y1, x2, y2;
1549 gfxline_t points[5];
1552 msg("<debug> drawlink");
1554 link->getRect(&x1, &y1, &x2, &y2);
1555 cvtUserToDev(x1, y1, &x, &y);
1556 points[0].type = gfx_moveTo;
1557 points[0].x = points[4].x = x + user_movex + clipmovex;
1558 points[0].y = points[4].y = y + user_movey + clipmovey;
1559 points[0].next = &points[1];
1560 cvtUserToDev(x2, y1, &x, &y);
1561 points[1].type = gfx_lineTo;
1562 points[1].x = x + user_movex + clipmovex;
1563 points[1].y = y + user_movey + clipmovey;
1564 points[1].next = &points[2];
1565 cvtUserToDev(x2, y2, &x, &y);
1566 points[2].type = gfx_lineTo;
1567 points[2].x = x + user_movex + clipmovex;
1568 points[2].y = y + user_movey + clipmovey;
1569 points[2].next = &points[3];
1570 cvtUserToDev(x1, y2, &x, &y);
1571 points[3].type = gfx_lineTo;
1572 points[3].x = x + user_movex + clipmovex;
1573 points[3].y = y + user_movey + clipmovey;
1574 points[3].next = &points[4];
1575 cvtUserToDev(x1, y1, &x, &y);
1576 points[4].type = gfx_lineTo;
1577 points[4].x = x + user_movex + clipmovex;
1578 points[4].y = y + user_movey + clipmovey;
1581 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1582 points[0].x, points[0].y,
1583 points[1].x, points[1].y,
1584 points[2].x, points[2].y,
1585 points[3].x, points[3].y);
1587 if(getLogLevel() >= LOGLEVEL_TRACE) {
1588 dump_outline(points);
1591 LinkAction*action=link->getAction();
1594 const char*type = "-?-";
1597 msg("<trace> drawlink action=%d", action->getKind());
1598 switch(action->getKind())
1602 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1603 LinkDest *dest=NULL;
1604 if (ha->getDest()==NULL)
1605 dest=catalog->findDest(ha->getNamedDest());
1606 else dest=ha->getDest();
1608 if (dest->isPageRef()){
1609 Ref pageref=dest->getPageRef();
1610 page=catalog->findPage(pageref.num,pageref.gen);
1612 else page=dest->getPageNum();
1613 sprintf(buf, "%d", page);
1620 LinkGoToR*l = (LinkGoToR*)action;
1621 GString*g = l->getFileName();
1623 s = strdup(g->getCString());
1625 /* if the GoToR link has no filename, then
1626 try to find a refernce in the *local*
1628 GString*g = l->getNamedDest();
1630 s = strdup(g->getCString());
1636 LinkNamed*l = (LinkNamed*)action;
1637 GString*name = l->getName();
1639 s = strdup(name->lowerCase()->getCString());
1640 named = name->getCString();
1643 if(strstr(s, "next") || strstr(s, "forward"))
1645 page = currentpage + 1;
1647 else if(strstr(s, "prev") || strstr(s, "back"))
1649 page = currentpage - 1;
1651 else if(strstr(s, "last") || strstr(s, "end"))
1653 if(pages && pagepos>0)
1654 page = pages[pagepos-1];
1656 else if(strstr(s, "first") || strstr(s, "top"))
1664 case actionLaunch: {
1666 LinkLaunch*l = (LinkLaunch*)action;
1667 GString * str = new GString(l->getFileName());
1668 GString * params = l->getParams();
1670 str->append(params);
1671 s = strdup(str->getCString());
1678 LinkURI*l = (LinkURI*)action;
1679 GString*g = l->getURI();
1681 url = g->getCString();
1686 case actionUnknown: {
1688 LinkUnknown*l = (LinkUnknown*)action;
1693 msg("<error> Unknown link type!");
1698 if(!s) s = strdup("-?-");
1700 msg("<trace> drawlink s=%s", s);
1702 if(!linkinfo && (page || s))
1704 msg("<notice> File contains links");
1712 for(t=1;t<=pagepos;t++) {
1713 if(pages[t]==page) {
1722 sprintf(buf, "page%d", lpage);
1723 device->drawlink(device, points, buf);
1727 device->drawlink(device, points, s);
1730 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1734 void GFXOutputDev::saveState(GfxState *state) {
1735 dbg("saveState %08x", state); dbgindent+=2;
1737 msg("<trace> saveState %08x", state);
1740 msg("<fatal> Too many nested states in pdf.");
1744 states[statepos].state = state;
1745 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1746 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1747 states[statepos].clipping = 0;
1748 states[statepos].olddevice = 0;
1749 states[statepos].clipbbox = states[statepos-1].clipbbox;
1752 void GFXOutputDev::restoreState(GfxState *state) {
1753 dbgindent-=2; dbg("restoreState %08x", state);
1756 msg("<fatal> Invalid restoreState");
1759 msg("<trace> restoreState %08x%s%s", state,
1760 states[statepos].softmask?" (end softmask)":"",
1761 states[statepos].clipping?" (end clipping)":"");
1762 if(states[statepos].softmask) {
1763 clearSoftMask(state);
1767 while(states[statepos].clipping) {
1768 device->endclip(device);
1769 states[statepos].clipping--;
1771 if(states[statepos].state!=state) {
1772 msg("<fatal> bad state nesting");
1775 states[statepos].state=0;
1779 void GFXOutputDev::updateLineDash(GfxState *state)
1781 state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1782 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1783 if(!this->dashLength) {
1784 this->dashPattern = 0;
1788 void GFXOutputDev::updateLineWidth(GfxState *state)
1790 double width = state->getTransformedLineWidth();
1793 void GFXOutputDev::updateLineCap(GfxState *state)
1795 int c = state->getLineCap();
1798 void GFXOutputDev::updateLineJoin(GfxState *state)
1800 int j = state->getLineJoin();
1803 void GFXOutputDev::updateFillColor(GfxState *state)
1806 double opaq = state->getFillOpacity();
1807 state->getFillRGB(&rgb);
1809 void GFXOutputDev::updateFillOpacity(GfxState *state)
1812 double opaq = state->getFillOpacity();
1813 state->getFillRGB(&rgb);
1814 dbg("update fillopaq %f", opaq);
1816 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1818 double opaq = state->getFillOpacity();
1819 dbg("update strokeopaq %f", opaq);
1821 void GFXOutputDev::updateFillOverprint(GfxState *state)
1823 double opaq = state->getFillOverprint();
1824 dbg("update filloverprint %f", opaq);
1826 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1828 double opaq = state->getStrokeOverprint();
1829 dbg("update strokeoverprint %f", opaq);
1831 void GFXOutputDev::updateTransfer(GfxState *state)
1833 dbg("update transfer");
1837 void GFXOutputDev::updateStrokeColor(GfxState *state)
1840 double opaq = state->getStrokeOpacity();
1841 state->getStrokeRGB(&rgb);
1845 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1847 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1848 memset(font, 0, sizeof(gfxfont_t));
1850 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1851 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1855 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1857 //printf("%d glyphs\n", font->num_glyphs);
1858 font->num_glyphs = 0;
1859 for(t=0;t<src->num_glyphs;t++) {
1860 if(src->glyphs[t]) {
1861 SplashPath*path = src->glyphs[t]->path;
1862 int len = path?path->getLength():0;
1863 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1864 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1865 src->glyphs[t]->glyphid = font->num_glyphs;
1866 glyph->unicode = src->glyphs[t]->unicode;
1867 if(glyph->unicode >= font->max_unicode)
1868 font->max_unicode = glyph->unicode+1;
1870 gfxdrawer_target_gfxline(&drawer);
1874 for(s=0;s<len;s++) {
1877 path->getPoint(s, &x, &y, &f);
1880 if(f&splashPathFirst) {
1881 drawer.moveTo(&drawer, x*scale, y*scale);
1883 if(f&splashPathCurve) {
1885 path->getPoint(++s, &x2, &y2, &f);
1886 if(f&splashPathCurve) {
1888 path->getPoint(++s, &x3, &y3, &f);
1889 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1891 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1894 drawer.lineTo(&drawer, x*scale, y*scale);
1896 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1897 // (f&splashPathFirst)?"first":"",
1898 // (f&splashPathLast)?"last":"");
1900 glyph->line = (gfxline_t*)drawer.result(&drawer);
1901 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1905 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1906 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1907 for(t=0;t<font->num_glyphs;t++) {
1908 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1909 font->unicode2glyph[font->glyphs[t].unicode] = t;
1913 msg("<trace> %d glyphs.", t, font->num_glyphs);
1917 void GFXOutputDev::updateFont(GfxState *state)
1919 GfxFont* gfxFont = state->getFont();
1923 char*id = getFontID(gfxFont);
1924 msg("<verbose> Updating font to %s", id);
1925 if(gfxFont->getType() == fontType3) {
1926 infofeature("Type3 fonts");
1927 if(!config_extrafontdata) {
1932 msg("<error> Internal Error: FontID is null");
1936 this->current_fontinfo = this->info->getFont(id);
1937 if(!this->current_fontinfo) {
1938 msg("<error> Internal Error: no fontinfo for font %s", id);
1941 if(!this->current_fontinfo->seen) {
1942 dumpFontInfo("<verbose>", gfxFont);
1945 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1947 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1948 font->id = strdup(id);
1949 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1951 device->addfont(device, font);
1953 current_gfxfont = font;
1956 updateFontMatrix(state);
1959 #define SQR(x) ((x)*(x))
1961 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1963 if((newwidth<2 || newheight<2) ||
1964 (width<=newwidth || height<=newheight))
1966 unsigned char*newdata;
1968 newdata= (unsigned char*)malloc(newwidth*newheight);
1969 double fx = ((double)width)/newwidth;
1970 double fy = ((double)height)/newheight;
1972 int blocksize = (int)(8192/(fx*fy));
1973 int r = 8192*256/palettesize;
1974 for(x=0;x<newwidth;x++) {
1975 double ex = px + fx;
1976 int fromx = (int)px;
1978 int xweight1 = (int)((1-(px-fromx))*256);
1979 int xweight2 = (int)((ex-tox)*256);
1981 for(y=0;y<newheight;y++) {
1982 double ey = py + fy;
1983 int fromy = (int)py;
1985 int yweight1 = (int)((1-(py-fromy))*256);
1986 int yweight2 = (int)((ey-toy)*256);
1993 for(xx=fromx;xx<=tox;xx++)
1994 for(yy=fromy;yy<=toy;yy++) {
1995 int b = 1-data[width*yy+xx];
1997 if(xx==fromx) weight = (weight*xweight1)/256;
1998 if(xx==tox) weight = (weight*xweight2)/256;
1999 if(yy==fromy) weight = (weight*yweight1)/256;
2000 if(yy==toy) weight = (weight*yweight2)/256;
2003 //if(a) a=(palettesize-1)*r/blocksize;
2004 newdata[y*newwidth+x] = (a*blocksize)/r;
2012 #define IMAGE_TYPE_JPEG 0
2013 #define IMAGE_TYPE_LOSSLESS 1
2015 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2016 double x1,double y1,
2017 double x2,double y2,
2018 double x3,double y3,
2019 double x4,double y4, int type)
2021 gfxcolor_t*newpic=0;
2023 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2024 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2026 gfxline_t p1,p2,p3,p4,p5;
2027 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2028 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2029 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2030 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2031 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2033 {p1.x = (int)(p1.x*20)/20.0;
2034 p1.y = (int)(p1.y*20)/20.0;
2035 p2.x = (int)(p2.x*20)/20.0;
2036 p2.y = (int)(p2.y*20)/20.0;
2037 p3.x = (int)(p3.x*20)/20.0;
2038 p3.y = (int)(p3.y*20)/20.0;
2039 p4.x = (int)(p4.x*20)/20.0;
2040 p4.y = (int)(p4.y*20)/20.0;
2041 p5.x = (int)(p5.x*20)/20.0;
2042 p5.y = (int)(p5.y*20)/20.0;
2046 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2047 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2052 img.data = (gfxcolor_t*)data;
2056 if(type == IMAGE_TYPE_JPEG)
2057 /* TODO: pass image_dpi to device instead */
2058 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2061 dev->fillbitmap(dev, &p1, &img, &m, 0);
2064 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2065 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2067 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2070 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2071 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2073 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2077 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2078 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2079 GBool inlineImg, int mask, int*maskColors,
2080 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2082 double x1,y1,x2,y2,x3,y3,x4,y4;
2083 ImageStream *imgStr;
2088 unsigned char* maskbitmap = 0;
2091 ncomps = colorMap->getNumPixelComps();
2092 bits = colorMap->getBits();
2097 unsigned char buf[8];
2098 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2100 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2101 imgMaskStr->reset();
2102 unsigned char pal[256];
2103 int n = 1 << colorMap->getBits();
2108 maskColorMap->getGray(pixBuf, &gray);
2109 pal[t] = colToByte(gray);
2111 for (y = 0; y < maskHeight; y++) {
2112 for (x = 0; x < maskWidth; x++) {
2113 imgMaskStr->getPixel(buf);
2114 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2119 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2120 imgMaskStr->reset();
2121 for (y = 0; y < maskHeight; y++) {
2122 for (x = 0; x < maskWidth; x++) {
2123 imgMaskStr->getPixel(buf);
2125 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2133 imgStr = new ImageStream(str, width, ncomps,bits);
2136 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2138 msg("<verbose> Ignoring %d by %d image", width, height);
2139 unsigned char buf[8];
2141 for (y = 0; y < height; ++y)
2142 for (x = 0; x < width; ++x) {
2143 imgStr->getPixel(buf);
2151 this->transformXY(state, 0, 1, &x1, &y1);
2152 this->transformXY(state, 0, 0, &x2, &y2);
2153 this->transformXY(state, 1, 0, &x3, &y3);
2154 this->transformXY(state, 1, 1, &x4, &y4);
2157 /* as type 3 bitmaps are antialized, we need to place them
2158 at integer coordinates, otherwise flash player's antializing
2159 will kick in and make everything blurry */
2160 x1 = (int)(x1);y1 = (int)(y1);
2161 x2 = (int)(x2);y2 = (int)(y2);
2162 x3 = (int)(x3);y3 = (int)(y3);
2163 x4 = (int)(x4);y4 = (int)(y4);
2166 if(!pbminfo && !(str->getKind()==strDCT)) {
2168 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2172 msg("<verbose> drawing %d by %d masked picture", width, height);
2174 if(!jpeginfo && (str->getKind()==strDCT)) {
2175 msg("<notice> File contains jpeg pictures");
2180 unsigned char buf[8];
2182 unsigned char*pic = new unsigned char[width*height];
2183 gfxcolor_t pal[256];
2185 state->getFillRGB(&rgb);
2187 memset(pal,255,sizeof(pal));
2188 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2189 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2190 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2191 pal[0].a = 255; pal[1].a = 0;
2194 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2195 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2196 for (y = 0; y < height; ++y)
2197 for (x = 0; x < width; ++x)
2199 imgStr->getPixel(buf);
2202 pic[width*y+x] = buf[0];
2205 /* the size of the drawn image is added to the identifier
2206 as the same image may require different bitmaps if displayed
2207 at different sizes (due to antialiasing): */
2210 unsigned char*pic2 = 0;
2213 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2222 height = realheight;
2226 /* make a black/white palette */
2228 float r = 255./(float)(numpalette-1);
2230 for(t=0;t<numpalette;t++) {
2231 pal[t].r = colToByte(rgb.r);
2232 pal[t].g = colToByte(rgb.g);
2233 pal[t].b = colToByte(rgb.b);
2234 pal[t].a = (unsigned char)(t*r);
2239 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2240 for (y = 0; y < height; ++y) {
2241 for (x = 0; x < width; ++x) {
2242 pic2[width*y+x] = pal[pic[y*width+x]];
2245 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2249 if(maskbitmap) free(maskbitmap);
2255 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2256 gfxcolor_t*pic=new gfxcolor_t[width*height];
2257 for (y = 0; y < height; ++y) {
2258 for (x = 0; x < width; ++x) {
2259 imgStr->getPixel(pixBuf);
2260 colorMap->getRGB(pixBuf, &rgb);
2261 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2262 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2263 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2264 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2266 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2270 if(str->getKind()==strDCT)
2271 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2273 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2276 if(maskbitmap) free(maskbitmap);
2279 gfxcolor_t*pic=new gfxcolor_t[width*height];
2280 gfxcolor_t pal[256];
2281 int n = 1 << colorMap->getBits();
2283 for(t=0;t<256;t++) {
2285 colorMap->getRGB(pixBuf, &rgb);
2287 {/*if(maskColors && *maskColors==t) {
2288 msg("<notice> Color %d is transparent", t);
2289 if (imgData->maskColors) {
2291 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2292 if (pix[i] < imgData->maskColors[2*i] ||
2293 pix[i] > imgData->maskColors[2*i+1]) {
2308 pal[t].r = (unsigned char)(colToByte(rgb.r));
2309 pal[t].g = (unsigned char)(colToByte(rgb.g));
2310 pal[t].b = (unsigned char)(colToByte(rgb.b));
2311 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2314 for (y = 0; y < height; ++y) {
2315 for (x = 0; x < width; ++x) {
2316 imgStr->getPixel(pixBuf);
2317 pic[width*y+x] = pal[pixBuf[0]];
2321 if(maskWidth < width && maskHeight < height) {
2322 for(y = 0; y < height; y++) {
2323 for (x = 0; x < width; x++) {
2324 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2328 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2329 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2330 double dx = width / maskWidth;
2331 double dy = height / maskHeight;
2333 for(y = 0; y < maskHeight; y++) {
2335 for (x = 0; x < maskWidth; x++) {
2336 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2337 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2345 height = maskHeight;
2348 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2352 if(maskbitmap) free(maskbitmap);
2357 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2358 int width, int height, GBool invert,
2361 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2362 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2363 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2366 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2367 int width, int height, GfxImageColorMap *colorMap,
2368 int *maskColors, GBool inlineImg)
2370 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2371 colorMap?"colorMap":"no colorMap",
2372 maskColors?"maskColors":"no maskColors",
2374 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2375 colorMap?"colorMap":"no colorMap",
2376 maskColors?"maskColors":"no maskColors",
2379 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2380 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2381 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2384 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2385 int width, int height,
2386 GfxImageColorMap *colorMap,
2387 Stream *maskStr, int maskWidth, int maskHeight,
2390 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2391 colorMap?"colorMap":"no colorMap",
2392 maskWidth, maskHeight);
2393 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2394 colorMap?"colorMap":"no colorMap",
2395 maskWidth, maskHeight);
2397 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2398 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2399 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2402 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2403 int width, int height,
2404 GfxImageColorMap *colorMap,
2406 int maskWidth, int maskHeight,
2407 GfxImageColorMap *maskColorMap)
2409 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2410 colorMap?"colorMap":"no colorMap",
2411 maskWidth, maskHeight);
2412 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2413 colorMap?"colorMap":"no colorMap",
2414 maskWidth, maskHeight);
2416 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2417 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2418 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2421 void GFXOutputDev::stroke(GfxState *state)
2425 GfxPath * path = state->getPath();
2426 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2427 strokeGfxline(state, line, 0);
2431 void GFXOutputDev::fill(GfxState *state)
2433 gfxcolor_t col = getFillColor(state);
2434 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2436 GfxPath * path = state->getPath();
2437 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2438 if(config_optimize_polygons) {
2439 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2443 fillGfxLine(state, line);
2447 void GFXOutputDev::eoFill(GfxState *state)
2449 gfxcolor_t col = getFillColor(state);
2450 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2452 GfxPath * path = state->getPath();
2453 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2454 fillGfxLine(state, line);
2459 static const char* dirseparator()
2468 void addGlobalFont(const char*filename)
2470 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2471 memset(f, 0, sizeof(fontfile_t));
2472 f->filename = filename;
2473 int len = strlen(filename);
2474 char*r1 = strrchr(filename, '/');
2475 char*r2 = strrchr(filename, '\\');
2483 msg("<notice> Adding font \"%s\".", filename);
2484 if(global_fonts_next) {
2485 global_fonts_next->next = f;
2486 global_fonts_next = global_fonts_next->next;
2488 global_fonts_next = global_fonts = f;
2492 void addGlobalLanguageDir(const char*dir)
2494 msg("<notice> Adding %s to language pack directories", dir);
2497 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2498 strcpy(config_file, dir);
2499 strcat(config_file, dirseparator());
2500 strcat(config_file, "add-to-xpdfrc");
2502 fi = fopen(config_file, "rb");
2504 msg("<error> Could not open %s", config_file);
2507 globalParams->parseFile(new GString(config_file), fi);
2511 void addGlobalFontDir(const char*dirname)
2513 #ifdef HAVE_DIRENT_H
2514 msg("<notice> Adding %s to font directories", dirname);
2515 lastfontdir = strdup(dirname);
2516 DIR*dir = opendir(dirname);
2518 msg("<warning> Couldn't open directory %s", dirname);
2523 ent = readdir (dir);
2527 char*name = ent->d_name;
2533 if(!strncasecmp(&name[l-4], ".pfa", 4))
2535 if(!strncasecmp(&name[l-4], ".pfb", 4))
2537 if(!strncasecmp(&name[l-4], ".ttf", 4))
2540 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2541 strcpy(fontname, dirname);
2542 strcat(fontname, dirseparator());
2543 strcat(fontname, name);
2544 addGlobalFont(fontname);
2549 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2553 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2559 this->pagebuflen = 1024;
2560 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2561 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2563 while(pdfpage >= this->pagebuflen)
2565 int oldlen = this->pagebuflen;
2566 this->pagebuflen+=1024;
2567 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2568 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2571 this->pages[pdfpage] = outputpage;
2572 if(pdfpage>this->pagepos)
2573 this->pagepos = pdfpage;
2576 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2577 GfxColorSpace *blendingColorSpace,
2578 GBool isolated, GBool knockout,
2581 const char*colormodename = "";
2583 if(blendingColorSpace) {
2584 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2586 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);
2587 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);
2589 //states[statepos].createsoftmask |= forSoftMask;
2590 states[statepos].createsoftmask = forSoftMask;
2591 states[statepos].transparencygroup = !forSoftMask;
2592 states[statepos].isolated = isolated;
2594 states[statepos].olddevice = this->device;
2595 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2596 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2598 gfxdevice_record_init(this->device);
2600 /*if(!forSoftMask) { ////???
2601 state->setFillOpacity(0.0);
2606 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2609 gfxdevice_t*r = this->device;
2611 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2613 this->device = states[statepos].olddevice;
2615 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2616 /* if these errors occur more often, we should build a seperate
2617 transparency group stack, like xpdf/SplashOutputDev.cc does */
2618 restoreState(state);
2619 this->device = states[statepos].olddevice;
2621 states[statepos].olddevice = 0;
2623 gfxresult_t*recording = r->finish(r);
2625 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2626 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2628 if(states[statepos].createsoftmask) {
2629 states[statepos-1].softmaskrecording = recording;
2631 states[statepos-1].grouprecording = recording;
2634 states[statepos].createsoftmask = 0;
2635 states[statepos].transparencygroup = 0;
2639 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2641 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2642 "colordodge","colorburn","hardlight","softlight","difference",
2643 "exclusion","hue","saturation","color","luminosity"};
2645 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2646 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2648 if(state->getBlendMode() == gfxBlendNormal)
2649 infofeature("transparency groups");
2652 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2653 warnfeature(buffer, 0);
2656 gfxresult_t*grouprecording = states[statepos].grouprecording;
2658 int blendmode = state->getBlendMode();
2659 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2660 int alpha = (int)(state->getFillOpacity()*255);
2661 if(blendmode == gfxBlendMultiply && alpha>200)
2664 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2665 gfxdevice_ops_init(&ops, this->device, alpha);
2666 gfxresult_record_replay(grouprecording, &ops);
2669 grouprecording->destroy(grouprecording);
2671 states[statepos].grouprecording = 0;
2674 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2676 if(states[statepos].softmask) {
2677 /* shouldn't happen, but *does* happen */
2678 clearSoftMask(state);
2681 /* alpha = 1: retrieve mask values from alpha layer
2682 alpha = 0: retrieve mask values from luminance */
2684 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2685 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2686 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2687 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2689 infofeature("soft masks");
2691 warnfeature("soft masks from alpha channel",0);
2693 if(states[statepos].olddevice) {
2694 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2697 states[statepos].olddevice = this->device;
2698 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2699 gfxdevice_record_init(this->device);
2701 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2703 states[statepos].softmask = 1;
2704 states[statepos].softmask_alpha = alpha;
2707 static inline Guchar div255(int x) {
2708 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2711 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2713 if(c < min) c = min;
2714 if(c > max) c = max;
2718 void GFXOutputDev::clearSoftMask(GfxState *state)
2720 if(!states[statepos].softmask)
2722 states[statepos].softmask = 0;
2723 dbg("clearSoftMask statepos=%d", statepos);
2724 msg("<verbose> clearSoftMask statepos=%d", statepos);
2726 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2727 msg("<error> Error in softmask/tgroup ordering");
2731 gfxresult_t*mask = states[statepos].softmaskrecording;
2732 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2733 this->device = states[statepos].olddevice;
2735 /* get outline of all objects below the soft mask */
2736 gfxdevice_t uniondev;
2737 gfxdevice_union_init(&uniondev, 0);
2738 gfxresult_record_replay(below, &uniondev);
2739 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2740 uniondev.finish(&uniondev);
2741 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2742 gfxline_free(belowoutline);belowoutline=0;
2744 this->device->startclip(this->device, belowoutline);
2745 gfxresult_record_replay(below, this->device);
2746 gfxresult_record_replay(mask, this->device);
2747 this->device->endclip(this->device);
2750 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2751 if(width<=0 || height<=0)
2754 gfxdevice_t belowrender;
2755 gfxdevice_render_init(&belowrender);
2756 if(states[statepos+1].isolated) {
2757 belowrender.setparameter(&belowrender, "fillwhite", "1");
2759 belowrender.setparameter(&belowrender, "antialize", "2");
2760 belowrender.startpage(&belowrender, width, height);
2761 gfxresult_record_replay(below, &belowrender);
2762 belowrender.endpage(&belowrender);
2763 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2764 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2765 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2767 gfxdevice_t maskrender;
2768 gfxdevice_render_init(&maskrender);
2769 maskrender.startpage(&maskrender, width, height);
2770 gfxresult_record_replay(mask, &maskrender);
2771 maskrender.endpage(&maskrender);
2772 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2773 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2775 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2776 msg("<fatal> Internal error in mask drawing");
2781 for(y=0;y<height;y++) {
2782 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2783 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2784 for(x=0;x<width;x++) {
2786 if(states[statepos].softmask_alpha) {
2789 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2792 l2->a = div255(alpha*l2->a);
2794 /* DON'T premultiply alpha- this is done by fillbitmap,
2795 depending on the output device */
2796 //l2->r = div255(alpha*l2->r);
2797 //l2->g = div255(alpha*l2->g);
2798 //l2->b = div255(alpha*l2->b);
2804 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2807 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2808 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2810 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2812 mask->destroy(mask);
2813 below->destroy(below);
2814 maskresult->destroy(maskresult);
2815 belowresult->destroy(belowresult);
2816 states[statepos].softmaskrecording = 0;
2821 // public: ~MemCheck()
2823 // delete globalParams;globalParams=0;
2824 // Object::memCheck(stderr);
2825 // gMemReport(stderr);