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 = 0;
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, y, &m.tx, &m.ty);
1361 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1362 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1364 msg("<debug> Drawing glyph %d as shape", charid);
1366 msg("<notice> Some texts will be rendered as shape");
1369 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1370 gfxline_t*tglyph = gfxline_clone(glyph);
1371 gfxline_transform(tglyph, &m);
1372 if((render&3) != RENDER_INVISIBLE) {
1373 gfxline_t*add = gfxline_clone(tglyph);
1374 current_text_stroke = gfxline_append(current_text_stroke, add);
1376 if(render&RENDER_CLIP) {
1377 gfxline_t*add = gfxline_clone(tglyph);
1378 current_text_clip = gfxline_append(current_text_clip, add);
1379 if(!current_text_clip) {
1380 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1383 gfxline_free(tglyph);
1387 void GFXOutputDev::endString(GfxState *state)
1389 int render = state->getRender();
1390 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1392 if(current_text_stroke) {
1393 /* fillstroke and stroke text rendering objects we can process right
1394 now (as there may be texts of other rendering modes in this
1395 text object)- clipping objects have to wait until endTextObject,
1397 device->setparameter(device, "mark","TXT");
1398 if((render&3) == RENDER_FILL) {
1399 fillGfxLine(state, current_text_stroke);
1400 gfxline_free(current_text_stroke);
1401 current_text_stroke = 0;
1402 } else if((render&3) == RENDER_FILLSTROKE) {
1403 fillGfxLine(state, current_text_stroke);
1404 strokeGfxline(state, current_text_stroke,0);
1405 gfxline_free(current_text_stroke);
1406 current_text_stroke = 0;
1407 } else if((render&3) == RENDER_STROKE) {
1408 strokeGfxline(state, current_text_stroke,0);
1409 gfxline_free(current_text_stroke);
1410 current_text_stroke = 0;
1412 device->setparameter(device, "mark","");
1416 void GFXOutputDev::endTextObject(GfxState *state)
1418 int render = state->getRender();
1419 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1421 if(current_text_clip) {
1422 device->setparameter(device, "mark","TXT");
1423 clipToGfxLine(state, current_text_clip);
1424 device->setparameter(device, "mark","");
1425 gfxline_free(current_text_clip);
1426 current_text_clip = 0;
1430 /* the logic seems to be as following:
1431 first, beginType3Char is called, with the charcode and the coordinates.
1432 if this function returns true, it already knew about the char and has now drawn it.
1433 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1434 called with some parameters.
1435 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1436 at the position first passed to beginType3Char). the char ends with endType3Char.
1438 The drawing operations between beginType3Char and endType3Char are somewhat different to
1439 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1440 color determines the color of a font)
1443 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1445 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1448 if(config_extrafontdata && current_fontinfo) {
1450 gfxmatrix_t m = this->current_font_matrix;
1451 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1452 m.m00*=INTERNAL_FONT_SIZE;
1453 m.m01*=INTERNAL_FONT_SIZE;
1454 m.m10*=INTERNAL_FONT_SIZE;
1455 m.m11*=INTERNAL_FONT_SIZE;
1457 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1458 msg("<error> Invalid charid %d for font", charid);
1461 gfxcolor_t col={0,0,0,0};
1462 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1463 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1467 /* the character itself is going to be passed using the draw functions */
1468 return gFalse; /* gTrue= is_in_cache? */
1471 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1473 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1476 void GFXOutputDev::endType3Char(GfxState *state)
1479 msg("<debug> endType3Char");
1482 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1484 this->currentpage = pageNum;
1486 int rot = doc->getPageRotate(1);
1487 gfxcolor_t white = {255,255,255,255};
1488 gfxcolor_t black = {255,0,0,0};
1490 gfxline_t clippath[5];
1492 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1493 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1494 Use CropBox, not MediaBox, as page size
1501 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1502 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1504 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1505 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1507 this->clipmovex = -(int)x1;
1508 this->clipmovey = -(int)y1;
1510 /* apply user clip box */
1511 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1512 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1513 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1514 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1515 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1516 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1519 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1521 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);
1523 msg("<verbose> page is rotated %d degrees", rot);
1525 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1526 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1527 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1528 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1529 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1530 device->startclip(device, clippath); outer_clip_box = 1;
1531 if(!config_transparent) {
1532 device->fill(device, clippath, &white);
1534 states[statepos].clipbbox.xmin = x1;
1535 states[statepos].clipbbox.ymin = x1;
1536 states[statepos].clipbbox.xmax = x2;
1537 states[statepos].clipbbox.ymax = y2;
1539 this->dashPattern = 0;
1540 this->dashLength = 0;
1541 this->dashStart = 0;
1545 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1547 double x1, y1, x2, y2;
1548 gfxline_t points[5];
1551 msg("<debug> drawlink");
1553 link->getRect(&x1, &y1, &x2, &y2);
1554 cvtUserToDev(x1, y1, &x, &y);
1555 points[0].type = gfx_moveTo;
1556 points[0].x = points[4].x = x + user_movex + clipmovex;
1557 points[0].y = points[4].y = y + user_movey + clipmovey;
1558 points[0].next = &points[1];
1559 cvtUserToDev(x2, y1, &x, &y);
1560 points[1].type = gfx_lineTo;
1561 points[1].x = x + user_movex + clipmovex;
1562 points[1].y = y + user_movey + clipmovey;
1563 points[1].next = &points[2];
1564 cvtUserToDev(x2, y2, &x, &y);
1565 points[2].type = gfx_lineTo;
1566 points[2].x = x + user_movex + clipmovex;
1567 points[2].y = y + user_movey + clipmovey;
1568 points[2].next = &points[3];
1569 cvtUserToDev(x1, y2, &x, &y);
1570 points[3].type = gfx_lineTo;
1571 points[3].x = x + user_movex + clipmovex;
1572 points[3].y = y + user_movey + clipmovey;
1573 points[3].next = &points[4];
1574 cvtUserToDev(x1, y1, &x, &y);
1575 points[4].type = gfx_lineTo;
1576 points[4].x = x + user_movex + clipmovex;
1577 points[4].y = y + user_movey + clipmovey;
1580 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1581 points[0].x, points[0].y,
1582 points[1].x, points[1].y,
1583 points[2].x, points[2].y,
1584 points[3].x, points[3].y);
1586 if(getLogLevel() >= LOGLEVEL_TRACE) {
1587 dump_outline(points);
1590 LinkAction*action=link->getAction();
1593 const char*type = "-?-";
1596 msg("<trace> drawlink action=%d", action->getKind());
1597 switch(action->getKind())
1601 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1602 LinkDest *dest=NULL;
1603 if (ha->getDest()==NULL)
1604 dest=catalog->findDest(ha->getNamedDest());
1605 else dest=ha->getDest();
1607 if (dest->isPageRef()){
1608 Ref pageref=dest->getPageRef();
1609 page=catalog->findPage(pageref.num,pageref.gen);
1611 else page=dest->getPageNum();
1612 sprintf(buf, "%d", page);
1619 LinkGoToR*l = (LinkGoToR*)action;
1620 GString*g = l->getFileName();
1622 s = strdup(g->getCString());
1624 /* if the GoToR link has no filename, then
1625 try to find a refernce in the *local*
1627 GString*g = l->getNamedDest();
1629 s = strdup(g->getCString());
1635 LinkNamed*l = (LinkNamed*)action;
1636 GString*name = l->getName();
1638 s = strdup(name->lowerCase()->getCString());
1639 named = name->getCString();
1642 if(strstr(s, "next") || strstr(s, "forward"))
1644 page = currentpage + 1;
1646 else if(strstr(s, "prev") || strstr(s, "back"))
1648 page = currentpage - 1;
1650 else if(strstr(s, "last") || strstr(s, "end"))
1652 if(pages && pagepos>0)
1653 page = pages[pagepos-1];
1655 else if(strstr(s, "first") || strstr(s, "top"))
1663 case actionLaunch: {
1665 LinkLaunch*l = (LinkLaunch*)action;
1666 GString * str = new GString(l->getFileName());
1667 GString * params = l->getParams();
1669 str->append(params);
1670 s = strdup(str->getCString());
1677 LinkURI*l = (LinkURI*)action;
1678 GString*g = l->getURI();
1680 url = g->getCString();
1685 case actionUnknown: {
1687 LinkUnknown*l = (LinkUnknown*)action;
1692 msg("<error> Unknown link type!");
1697 if(!s) s = strdup("-?-");
1699 msg("<trace> drawlink s=%s", s);
1701 if(!linkinfo && (page || s))
1703 msg("<notice> File contains links");
1711 for(t=1;t<=pagepos;t++) {
1712 if(pages[t]==page) {
1721 sprintf(buf, "page%d", lpage);
1722 device->drawlink(device, points, buf);
1726 device->drawlink(device, points, s);
1729 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1733 void GFXOutputDev::saveState(GfxState *state) {
1734 dbg("saveState"); dbgindent+=2;
1736 msg("<trace> saveState");
1739 msg("<error> Too many nested states in pdf.");
1743 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1744 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1745 states[statepos].clipping = 0;
1746 states[statepos].clipbbox = states[statepos-1].clipbbox;
1749 void GFXOutputDev::restoreState(GfxState *state) {
1750 dbgindent-=2; dbg("restoreState");
1753 msg("<error> Invalid restoreState");
1756 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1757 states[statepos].clipping?" (end clipping)":"");
1758 if(states[statepos].softmask) {
1759 clearSoftMask(state);
1762 while(states[statepos].clipping) {
1763 device->endclip(device);
1764 states[statepos].clipping--;
1769 void GFXOutputDev::updateLineDash(GfxState *state)
1771 state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1772 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1773 if(!this->dashLength) {
1774 this->dashPattern = 0;
1778 void GFXOutputDev::updateLineWidth(GfxState *state)
1780 double width = state->getTransformedLineWidth();
1783 void GFXOutputDev::updateLineCap(GfxState *state)
1785 int c = state->getLineCap();
1788 void GFXOutputDev::updateLineJoin(GfxState *state)
1790 int j = state->getLineJoin();
1793 void GFXOutputDev::updateFillColor(GfxState *state)
1796 double opaq = state->getFillOpacity();
1797 state->getFillRGB(&rgb);
1799 void GFXOutputDev::updateFillOpacity(GfxState *state)
1802 double opaq = state->getFillOpacity();
1803 state->getFillRGB(&rgb);
1804 dbg("update fillopaq %f", opaq);
1806 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1808 double opaq = state->getFillOpacity();
1809 dbg("update strokeopaq %f", opaq);
1811 void GFXOutputDev::updateFillOverprint(GfxState *state)
1813 double opaq = state->getFillOverprint();
1814 dbg("update filloverprint %f", opaq);
1816 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1818 double opaq = state->getStrokeOverprint();
1819 dbg("update strokeoverprint %f", opaq);
1821 void GFXOutputDev::updateTransfer(GfxState *state)
1823 dbg("update transfer");
1827 void GFXOutputDev::updateStrokeColor(GfxState *state)
1830 double opaq = state->getStrokeOpacity();
1831 state->getStrokeRGB(&rgb);
1835 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1837 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1838 memset(font, 0, sizeof(gfxfont_t));
1840 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1841 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1845 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1847 //printf("%d glyphs\n", font->num_glyphs);
1848 font->num_glyphs = 0;
1849 for(t=0;t<src->num_glyphs;t++) {
1850 if(src->glyphs[t]) {
1851 SplashPath*path = src->glyphs[t]->path;
1852 int len = path?path->getLength():0;
1853 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1854 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1855 src->glyphs[t]->glyphid = font->num_glyphs;
1856 glyph->unicode = src->glyphs[t]->unicode;
1857 if(glyph->unicode >= font->max_unicode)
1858 font->max_unicode = glyph->unicode+1;
1860 gfxdrawer_target_gfxline(&drawer);
1864 for(s=0;s<len;s++) {
1867 path->getPoint(s, &x, &y, &f);
1870 if(f&splashPathFirst) {
1871 drawer.moveTo(&drawer, x*scale, y*scale);
1873 if(f&splashPathCurve) {
1875 path->getPoint(++s, &x2, &y2, &f);
1876 if(f&splashPathCurve) {
1878 path->getPoint(++s, &x3, &y3, &f);
1879 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1881 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1884 drawer.lineTo(&drawer, x*scale, y*scale);
1886 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1887 // (f&splashPathFirst)?"first":"",
1888 // (f&splashPathLast)?"last":"");
1890 glyph->line = (gfxline_t*)drawer.result(&drawer);
1891 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1895 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1896 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1897 for(t=0;t<font->num_glyphs;t++) {
1898 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1899 font->unicode2glyph[font->glyphs[t].unicode] = t;
1903 msg("<trace> %d glyphs.", t, font->num_glyphs);
1907 void GFXOutputDev::updateFont(GfxState *state)
1909 GfxFont* gfxFont = state->getFont();
1913 char*id = getFontID(gfxFont);
1914 msg("<verbose> Updating font to %s", id);
1915 if(gfxFont->getType() == fontType3) {
1916 infofeature("Type3 fonts");
1917 if(!config_extrafontdata) {
1922 msg("<error> Internal Error: FontID is null");
1926 this->current_fontinfo = this->info->getFont(id);
1927 if(!this->current_fontinfo) {
1928 msg("<error> Internal Error: no fontinfo for font %s", id);
1931 if(!this->current_fontinfo->seen) {
1932 dumpFontInfo("<verbose>", gfxFont);
1935 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1937 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1938 font->id = strdup(id);
1939 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1941 device->addfont(device, font);
1943 current_gfxfont = font;
1946 updateFontMatrix(state);
1949 #define SQR(x) ((x)*(x))
1951 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1953 if((newwidth<2 || newheight<2) ||
1954 (width<=newwidth || height<=newheight))
1956 unsigned char*newdata;
1958 newdata= (unsigned char*)malloc(newwidth*newheight);
1959 double fx = ((double)width)/newwidth;
1960 double fy = ((double)height)/newheight;
1962 int blocksize = (int)(8192/(fx*fy));
1963 int r = 8192*256/palettesize;
1964 for(x=0;x<newwidth;x++) {
1965 double ex = px + fx;
1966 int fromx = (int)px;
1968 int xweight1 = (int)((1-(px-fromx))*256);
1969 int xweight2 = (int)((ex-tox)*256);
1971 for(y=0;y<newheight;y++) {
1972 double ey = py + fy;
1973 int fromy = (int)py;
1975 int yweight1 = (int)((1-(py-fromy))*256);
1976 int yweight2 = (int)((ey-toy)*256);
1983 for(xx=fromx;xx<=tox;xx++)
1984 for(yy=fromy;yy<=toy;yy++) {
1985 int b = 1-data[width*yy+xx];
1987 if(xx==fromx) weight = (weight*xweight1)/256;
1988 if(xx==tox) weight = (weight*xweight2)/256;
1989 if(yy==fromy) weight = (weight*yweight1)/256;
1990 if(yy==toy) weight = (weight*yweight2)/256;
1993 //if(a) a=(palettesize-1)*r/blocksize;
1994 newdata[y*newwidth+x] = (a*blocksize)/r;
2002 #define IMAGE_TYPE_JPEG 0
2003 #define IMAGE_TYPE_LOSSLESS 1
2005 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2006 double x1,double y1,
2007 double x2,double y2,
2008 double x3,double y3,
2009 double x4,double y4, int type)
2011 gfxcolor_t*newpic=0;
2013 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2014 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2016 gfxline_t p1,p2,p3,p4,p5;
2017 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2018 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2019 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2020 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2021 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2023 {p1.x = (int)(p1.x*20)/20.0;
2024 p1.y = (int)(p1.y*20)/20.0;
2025 p2.x = (int)(p2.x*20)/20.0;
2026 p2.y = (int)(p2.y*20)/20.0;
2027 p3.x = (int)(p3.x*20)/20.0;
2028 p3.y = (int)(p3.y*20)/20.0;
2029 p4.x = (int)(p4.x*20)/20.0;
2030 p4.y = (int)(p4.y*20)/20.0;
2031 p5.x = (int)(p5.x*20)/20.0;
2032 p5.y = (int)(p5.y*20)/20.0;
2036 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2037 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2042 img.data = (gfxcolor_t*)data;
2046 if(type == IMAGE_TYPE_JPEG)
2047 /* TODO: pass image_dpi to device instead */
2048 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2051 dev->fillbitmap(dev, &p1, &img, &m, 0);
2054 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2055 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2057 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2060 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2061 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2063 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2067 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2068 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2069 GBool inlineImg, int mask, int*maskColors,
2070 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2072 double x1,y1,x2,y2,x3,y3,x4,y4;
2073 ImageStream *imgStr;
2078 unsigned char* maskbitmap = 0;
2081 ncomps = colorMap->getNumPixelComps();
2082 bits = colorMap->getBits();
2087 unsigned char buf[8];
2088 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2090 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2091 imgMaskStr->reset();
2092 unsigned char pal[256];
2093 int n = 1 << colorMap->getBits();
2098 maskColorMap->getGray(pixBuf, &gray);
2099 pal[t] = colToByte(gray);
2101 for (y = 0; y < maskHeight; y++) {
2102 for (x = 0; x < maskWidth; x++) {
2103 imgMaskStr->getPixel(buf);
2104 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2109 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2110 imgMaskStr->reset();
2111 for (y = 0; y < maskHeight; y++) {
2112 for (x = 0; x < maskWidth; x++) {
2113 imgMaskStr->getPixel(buf);
2115 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2123 imgStr = new ImageStream(str, width, ncomps,bits);
2126 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2128 msg("<verbose> Ignoring %d by %d image", width, height);
2129 unsigned char buf[8];
2131 for (y = 0; y < height; ++y)
2132 for (x = 0; x < width; ++x) {
2133 imgStr->getPixel(buf);
2141 this->transformXY(state, 0, 1, &x1, &y1);
2142 this->transformXY(state, 0, 0, &x2, &y2);
2143 this->transformXY(state, 1, 0, &x3, &y3);
2144 this->transformXY(state, 1, 1, &x4, &y4);
2147 /* as type 3 bitmaps are antialized, we need to place them
2148 at integer coordinates, otherwise flash player's antializing
2149 will kick in and make everything blurry */
2150 x1 = (int)(x1);y1 = (int)(y1);
2151 x2 = (int)(x2);y2 = (int)(y2);
2152 x3 = (int)(x3);y3 = (int)(y3);
2153 x4 = (int)(x4);y4 = (int)(y4);
2156 if(!pbminfo && !(str->getKind()==strDCT)) {
2158 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2162 msg("<verbose> drawing %d by %d masked picture", width, height);
2164 if(!jpeginfo && (str->getKind()==strDCT)) {
2165 msg("<notice> File contains jpeg pictures");
2170 unsigned char buf[8];
2172 unsigned char*pic = new unsigned char[width*height];
2173 gfxcolor_t pal[256];
2175 state->getFillRGB(&rgb);
2177 memset(pal,255,sizeof(pal));
2178 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2179 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2180 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2181 pal[0].a = 255; pal[1].a = 0;
2184 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2185 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2186 for (y = 0; y < height; ++y)
2187 for (x = 0; x < width; ++x)
2189 imgStr->getPixel(buf);
2192 pic[width*y+x] = buf[0];
2195 /* the size of the drawn image is added to the identifier
2196 as the same image may require different bitmaps if displayed
2197 at different sizes (due to antialiasing): */
2200 unsigned char*pic2 = 0;
2203 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2212 height = realheight;
2216 /* make a black/white palette */
2218 float r = 255./(float)(numpalette-1);
2220 for(t=0;t<numpalette;t++) {
2221 pal[t].r = colToByte(rgb.r);
2222 pal[t].g = colToByte(rgb.g);
2223 pal[t].b = colToByte(rgb.b);
2224 pal[t].a = (unsigned char)(t*r);
2229 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2230 for (y = 0; y < height; ++y) {
2231 for (x = 0; x < width; ++x) {
2232 pic2[width*y+x] = pal[pic[y*width+x]];
2235 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2239 if(maskbitmap) free(maskbitmap);
2245 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2246 gfxcolor_t*pic=new gfxcolor_t[width*height];
2247 for (y = 0; y < height; ++y) {
2248 for (x = 0; x < width; ++x) {
2249 imgStr->getPixel(pixBuf);
2250 colorMap->getRGB(pixBuf, &rgb);
2251 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2252 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2253 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2254 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2256 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2260 if(str->getKind()==strDCT)
2261 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2263 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2266 if(maskbitmap) free(maskbitmap);
2269 gfxcolor_t*pic=new gfxcolor_t[width*height];
2270 gfxcolor_t pal[256];
2271 int n = 1 << colorMap->getBits();
2273 for(t=0;t<256;t++) {
2275 colorMap->getRGB(pixBuf, &rgb);
2277 {/*if(maskColors && *maskColors==t) {
2278 msg("<notice> Color %d is transparent", t);
2279 if (imgData->maskColors) {
2281 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2282 if (pix[i] < imgData->maskColors[2*i] ||
2283 pix[i] > imgData->maskColors[2*i+1]) {
2298 pal[t].r = (unsigned char)(colToByte(rgb.r));
2299 pal[t].g = (unsigned char)(colToByte(rgb.g));
2300 pal[t].b = (unsigned char)(colToByte(rgb.b));
2301 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2304 for (y = 0; y < height; ++y) {
2305 for (x = 0; x < width; ++x) {
2306 imgStr->getPixel(pixBuf);
2307 pic[width*y+x] = pal[pixBuf[0]];
2311 if(maskWidth < width && maskHeight < height) {
2312 for(y = 0; y < height; y++) {
2313 for (x = 0; x < width; x++) {
2314 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2318 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2319 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2320 double dx = width / maskWidth;
2321 double dy = height / maskHeight;
2323 for(y = 0; y < maskHeight; y++) {
2325 for (x = 0; x < maskWidth; x++) {
2326 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2327 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2335 height = maskHeight;
2338 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2342 if(maskbitmap) free(maskbitmap);
2347 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2348 int width, int height, GBool invert,
2351 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2352 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2353 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2356 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2357 int width, int height, GfxImageColorMap *colorMap,
2358 int *maskColors, GBool inlineImg)
2360 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2361 colorMap?"colorMap":"no colorMap",
2362 maskColors?"maskColors":"no maskColors",
2364 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2365 colorMap?"colorMap":"no colorMap",
2366 maskColors?"maskColors":"no maskColors",
2369 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2370 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2371 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2374 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2375 int width, int height,
2376 GfxImageColorMap *colorMap,
2377 Stream *maskStr, int maskWidth, int maskHeight,
2380 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2381 colorMap?"colorMap":"no colorMap",
2382 maskWidth, maskHeight);
2383 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2384 colorMap?"colorMap":"no colorMap",
2385 maskWidth, maskHeight);
2387 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2388 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2389 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2392 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2393 int width, int height,
2394 GfxImageColorMap *colorMap,
2396 int maskWidth, int maskHeight,
2397 GfxImageColorMap *maskColorMap)
2399 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2400 colorMap?"colorMap":"no colorMap",
2401 maskWidth, maskHeight);
2402 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2403 colorMap?"colorMap":"no colorMap",
2404 maskWidth, maskHeight);
2406 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2407 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2408 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2411 void GFXOutputDev::stroke(GfxState *state)
2415 GfxPath * path = state->getPath();
2416 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2417 strokeGfxline(state, line, 0);
2421 void GFXOutputDev::fill(GfxState *state)
2423 gfxcolor_t col = getFillColor(state);
2424 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2426 GfxPath * path = state->getPath();
2427 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2428 if(config_optimize_polygons) {
2429 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2433 fillGfxLine(state, line);
2437 void GFXOutputDev::eoFill(GfxState *state)
2439 gfxcolor_t col = getFillColor(state);
2440 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2442 GfxPath * path = state->getPath();
2443 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2444 fillGfxLine(state, line);
2449 static const char* dirseparator()
2458 void addGlobalFont(const char*filename)
2460 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2461 memset(f, 0, sizeof(fontfile_t));
2462 f->filename = filename;
2463 int len = strlen(filename);
2464 char*r1 = strrchr(filename, '/');
2465 char*r2 = strrchr(filename, '\\');
2473 msg("<notice> Adding font \"%s\".", filename);
2474 if(global_fonts_next) {
2475 global_fonts_next->next = f;
2476 global_fonts_next = global_fonts_next->next;
2478 global_fonts_next = global_fonts = f;
2482 void addGlobalLanguageDir(const char*dir)
2484 msg("<notice> Adding %s to language pack directories", dir);
2487 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2488 strcpy(config_file, dir);
2489 strcat(config_file, dirseparator());
2490 strcat(config_file, "add-to-xpdfrc");
2492 fi = fopen(config_file, "rb");
2494 msg("<error> Could not open %s", config_file);
2497 globalParams->parseFile(new GString(config_file), fi);
2501 void addGlobalFontDir(const char*dirname)
2503 #ifdef HAVE_DIRENT_H
2504 msg("<notice> Adding %s to font directories", dirname);
2505 lastfontdir = strdup(dirname);
2506 DIR*dir = opendir(dirname);
2508 msg("<warning> Couldn't open directory %s", dirname);
2513 ent = readdir (dir);
2517 char*name = ent->d_name;
2523 if(!strncasecmp(&name[l-4], ".pfa", 4))
2525 if(!strncasecmp(&name[l-4], ".pfb", 4))
2527 if(!strncasecmp(&name[l-4], ".ttf", 4))
2530 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2531 strcpy(fontname, dirname);
2532 strcat(fontname, dirseparator());
2533 strcat(fontname, name);
2534 addGlobalFont(fontname);
2539 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2543 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2549 this->pagebuflen = 1024;
2550 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2551 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2553 while(pdfpage >= this->pagebuflen)
2555 int oldlen = this->pagebuflen;
2556 this->pagebuflen+=1024;
2557 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2558 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2561 this->pages[pdfpage] = outputpage;
2562 if(pdfpage>this->pagepos)
2563 this->pagepos = pdfpage;
2566 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2567 GfxColorSpace *blendingColorSpace,
2568 GBool isolated, GBool knockout,
2571 const char*colormodename = "";
2573 if(blendingColorSpace) {
2574 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2576 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);
2577 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);
2579 //states[statepos].createsoftmask |= forSoftMask;
2580 states[statepos].createsoftmask = forSoftMask;
2581 states[statepos].transparencygroup = !forSoftMask;
2582 states[statepos].isolated = isolated;
2584 states[statepos].olddevice = this->device;
2585 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2587 gfxdevice_record_init(this->device);
2589 /*if(!forSoftMask) { ////???
2590 state->setFillOpacity(0.0);
2595 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2598 gfxdevice_t*r = this->device;
2600 this->device = states[statepos].olddevice;
2602 gfxresult_t*recording = r->finish(r);
2604 dbg("endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2605 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2607 if(states[statepos].createsoftmask) {
2608 states[statepos-1].softmaskrecording = recording;
2610 states[statepos-1].grouprecording = recording;
2613 states[statepos].createsoftmask = 0;
2614 states[statepos].transparencygroup = 0;
2618 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2620 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2621 "colordodge","colorburn","hardlight","softlight","difference",
2622 "exclusion","hue","saturation","color","luminosity"};
2624 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2625 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2627 if(state->getBlendMode() == gfxBlendNormal)
2628 infofeature("transparency groups");
2631 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2632 warnfeature(buffer, 0);
2635 gfxresult_t*grouprecording = states[statepos].grouprecording;
2637 int blendmode = state->getBlendMode();
2638 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2639 int alpha = (int)(state->getFillOpacity()*255);
2640 if(blendmode == gfxBlendMultiply && alpha>200)
2643 gfxdevice_ops_init(&ops, this->device, alpha);
2644 gfxresult_record_replay(grouprecording, &ops);
2647 grouprecording->destroy(grouprecording);
2649 states[statepos].grouprecording = 0;
2652 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2654 if(states[statepos].softmask) {
2655 /* shouldn't happen, but *does* happen */
2656 clearSoftMask(state);
2659 /* alpha = 1: retrieve mask values from alpha layer
2660 alpha = 0: retrieve mask values from luminance */
2662 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2663 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2664 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2665 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2667 infofeature("soft masks");
2669 warnfeature("soft masks from alpha channel",0);
2671 states[statepos].olddevice = this->device;
2672 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2673 gfxdevice_record_init(this->device);
2675 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2677 states[statepos].softmask = 1;
2678 states[statepos].softmask_alpha = alpha;
2681 static inline Guchar div255(int x) {
2682 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2685 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2687 if(c < min) c = min;
2688 if(c > max) c = max;
2692 void GFXOutputDev::clearSoftMask(GfxState *state)
2694 if(!states[statepos].softmask)
2696 states[statepos].softmask = 0;
2697 dbg("clearSoftMask statepos=%d", statepos);
2698 msg("<verbose> clearSoftMask statepos=%d", statepos);
2700 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2701 msg("<error> Error in softmask/tgroup ordering");
2705 gfxresult_t*mask = states[statepos].softmaskrecording;
2706 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2707 this->device = states[statepos].olddevice;
2709 /* get outline of all objects below the soft mask */
2710 gfxdevice_t uniondev;
2711 gfxdevice_union_init(&uniondev, 0);
2712 gfxresult_record_replay(below, &uniondev);
2713 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2714 uniondev.finish(&uniondev);
2715 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2716 gfxline_free(belowoutline);belowoutline=0;
2718 this->device->startclip(this->device, belowoutline);
2719 gfxresult_record_replay(below, this->device);
2720 gfxresult_record_replay(mask, this->device);
2721 this->device->endclip(this->device);
2724 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2725 if(width<=0 || height<=0)
2728 gfxdevice_t belowrender;
2729 gfxdevice_render_init(&belowrender);
2730 if(states[statepos+1].isolated) {
2731 belowrender.setparameter(&belowrender, "fillwhite", "1");
2733 belowrender.setparameter(&belowrender, "antialize", "2");
2734 belowrender.startpage(&belowrender, width, height);
2735 gfxresult_record_replay(below, &belowrender);
2736 belowrender.endpage(&belowrender);
2737 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2738 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2739 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2741 gfxdevice_t maskrender;
2742 gfxdevice_render_init(&maskrender);
2743 maskrender.startpage(&maskrender, width, height);
2744 gfxresult_record_replay(mask, &maskrender);
2745 maskrender.endpage(&maskrender);
2746 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2747 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2749 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2750 msg("<fatal> Internal error in mask drawing");
2755 for(y=0;y<height;y++) {
2756 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2757 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2758 for(x=0;x<width;x++) {
2760 if(states[statepos].softmask_alpha) {
2763 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2766 l2->a = div255(alpha*l2->a);
2768 /* DON'T premultiply alpha- this is done by fillbitmap,
2769 depending on the output device */
2770 //l2->r = div255(alpha*l2->r);
2771 //l2->g = div255(alpha*l2->g);
2772 //l2->b = div255(alpha*l2->b);
2778 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2781 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2782 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2784 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2786 mask->destroy(mask);
2787 below->destroy(below);
2788 maskresult->destroy(maskresult);
2789 belowresult->destroy(belowresult);
2790 states[statepos].softmaskrecording = 0;
2795 // public: ~MemCheck()
2797 // delete globalParams;globalParams=0;
2798 // Object::memCheck(stderr);
2799 // gMemReport(stderr);