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<1 || newheight<1) ||
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 /* the code in this function is *old*. It's not pretty, but it works. */
2084 double x1,y1,x2,y2,x3,y3,x4,y4;
2085 ImageStream *imgStr;
2090 unsigned char* maskbitmap = 0;
2093 ncomps = colorMap->getNumPixelComps();
2094 bits = colorMap->getBits();
2099 unsigned char buf[8];
2100 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2102 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2103 imgMaskStr->reset();
2104 unsigned char pal[256];
2105 int n = 1 << colorMap->getBits();
2110 maskColorMap->getGray(pixBuf, &gray);
2111 pal[t] = colToByte(gray);
2113 for (y = 0; y < maskHeight; y++) {
2114 for (x = 0; x < maskWidth; x++) {
2115 imgMaskStr->getPixel(buf);
2116 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2121 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2122 imgMaskStr->reset();
2123 for (y = 0; y < maskHeight; y++) {
2124 for (x = 0; x < maskWidth; x++) {
2125 imgMaskStr->getPixel(buf);
2127 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2135 imgStr = new ImageStream(str, width, ncomps,bits);
2138 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2140 msg("<verbose> Ignoring %d by %d image", width, height);
2141 unsigned char buf[8];
2143 for (y = 0; y < height; ++y)
2144 for (x = 0; x < width; ++x) {
2145 imgStr->getPixel(buf);
2153 this->transformXY(state, 0, 1, &x1, &y1);
2154 this->transformXY(state, 0, 0, &x2, &y2);
2155 this->transformXY(state, 1, 0, &x3, &y3);
2156 this->transformXY(state, 1, 1, &x4, &y4);
2159 /* as type 3 bitmaps are antialized, we need to place them
2160 at integer coordinates, otherwise flash player's antializing
2161 will kick in and make everything blurry */
2162 x1 = (int)(x1);y1 = (int)(y1);
2163 x2 = (int)(x2);y2 = (int)(y2);
2164 x3 = (int)(x3);y3 = (int)(y3);
2165 x4 = (int)(x4);y4 = (int)(y4);
2168 if(!pbminfo && !(str->getKind()==strDCT)) {
2170 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2174 msg("<verbose> drawing %d by %d masked picture", width, height);
2176 if(!jpeginfo && (str->getKind()==strDCT)) {
2177 msg("<notice> File contains jpeg pictures");
2182 unsigned char buf[8];
2184 unsigned char*pic = new unsigned char[width*height];
2185 gfxcolor_t pal[256];
2187 state->getFillRGB(&rgb);
2189 memset(pal,255,sizeof(pal));
2190 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2191 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2192 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2193 pal[0].a = 255; pal[1].a = 0;
2196 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2197 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2198 for (y = 0; y < height; ++y)
2199 for (x = 0; x < width; ++x)
2201 imgStr->getPixel(buf);
2204 pic[width*y+x] = buf[0];
2208 unsigned char*pic2 = 0;
2211 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2220 height = realheight;
2224 /* make a black/white palette */
2226 float r = 255./(float)(numpalette-1);
2228 for(t=0;t<numpalette;t++) {
2229 pal[t].r = colToByte(rgb.r);
2230 pal[t].g = colToByte(rgb.g);
2231 pal[t].b = colToByte(rgb.b);
2232 pal[t].a = (unsigned char)(t*r);
2237 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2238 for (y = 0; y < height; ++y) {
2239 for (x = 0; x < width; ++x) {
2240 pic2[width*y+x] = pal[pic[y*width+x]];
2243 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2247 if(maskbitmap) free(maskbitmap);
2253 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2254 gfxcolor_t*pic=new gfxcolor_t[width*height];
2255 for (y = 0; y < height; ++y) {
2256 for (x = 0; x < width; ++x) {
2257 imgStr->getPixel(pixBuf);
2258 colorMap->getRGB(pixBuf, &rgb);
2259 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2260 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2261 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2262 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2264 int x1 = x*maskWidth/width;
2265 int y1 = y*maskHeight/height;
2266 int x2 = (x+1)*maskWidth/width;
2267 int y2 = (y+1)*maskHeight/height;
2269 unsigned int alpha=0;
2270 unsigned int count=0;
2271 for(xx=x1;xx<x2;xx++)
2272 for(yy=y1;yy<y2;yy++) {
2273 alpha += maskbitmap[yy*maskWidth+xx];
2277 pic[width*y+x].a = alpha / count;
2279 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2284 if(str->getKind()==strDCT)
2285 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2287 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2290 if(maskbitmap) free(maskbitmap);
2293 gfxcolor_t*pic=new gfxcolor_t[width*height];
2294 gfxcolor_t pal[256];
2295 int n = 1 << colorMap->getBits();
2297 for(t=0;t<256;t++) {
2299 colorMap->getRGB(pixBuf, &rgb);
2301 {/*if(maskColors && *maskColors==t) {
2302 msg("<notice> Color %d is transparent", t);
2303 if (imgData->maskColors) {
2305 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2306 if (pix[i] < imgData->maskColors[2*i] ||
2307 pix[i] > imgData->maskColors[2*i+1]) {
2322 pal[t].r = (unsigned char)(colToByte(rgb.r));
2323 pal[t].g = (unsigned char)(colToByte(rgb.g));
2324 pal[t].b = (unsigned char)(colToByte(rgb.b));
2325 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2328 for (y = 0; y < height; ++y) {
2329 for (x = 0; x < width; ++x) {
2330 imgStr->getPixel(pixBuf);
2331 pic[width*y+x] = pal[pixBuf[0]];
2335 if(maskWidth < width && maskHeight < height) {
2336 for(y = 0; y < height; y++) {
2337 for (x = 0; x < width; x++) {
2338 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2342 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2343 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2344 double dx = width / maskWidth;
2345 double dy = height / maskHeight;
2347 for(y = 0; y < maskHeight; y++) {
2349 for (x = 0; x < maskWidth; x++) {
2350 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2351 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2359 height = maskHeight;
2362 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2366 if(maskbitmap) free(maskbitmap);
2371 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2372 int width, int height, GBool invert,
2375 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2376 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2377 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2380 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2381 int width, int height, GfxImageColorMap *colorMap,
2382 int *maskColors, GBool inlineImg)
2384 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2385 colorMap?"colorMap":"no colorMap",
2386 maskColors?"maskColors":"no maskColors",
2388 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2389 colorMap?"colorMap":"no colorMap",
2390 maskColors?"maskColors":"no maskColors",
2393 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2394 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2395 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2398 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2399 int width, int height,
2400 GfxImageColorMap *colorMap,
2401 Stream *maskStr, int maskWidth, int maskHeight,
2404 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2405 colorMap?"colorMap":"no colorMap",
2406 maskWidth, maskHeight);
2407 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2408 colorMap?"colorMap":"no colorMap",
2409 maskWidth, maskHeight);
2411 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2412 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2413 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2416 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2417 int width, int height,
2418 GfxImageColorMap *colorMap,
2420 int maskWidth, int maskHeight,
2421 GfxImageColorMap *maskColorMap)
2423 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2424 colorMap?"colorMap":"no colorMap",
2425 maskWidth, maskHeight);
2426 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2427 colorMap?"colorMap":"no colorMap",
2428 maskWidth, maskHeight);
2430 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2431 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2432 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2435 void GFXOutputDev::stroke(GfxState *state)
2439 GfxPath * path = state->getPath();
2440 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2441 strokeGfxline(state, line, 0);
2445 void GFXOutputDev::fill(GfxState *state)
2447 gfxcolor_t col = getFillColor(state);
2448 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2450 GfxPath * path = state->getPath();
2451 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2452 if(config_optimize_polygons) {
2453 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2457 fillGfxLine(state, line);
2461 void GFXOutputDev::eoFill(GfxState *state)
2463 gfxcolor_t col = getFillColor(state);
2464 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2466 GfxPath * path = state->getPath();
2467 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2468 fillGfxLine(state, line);
2473 static const char* dirseparator()
2482 void addGlobalFont(const char*filename)
2484 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2485 memset(f, 0, sizeof(fontfile_t));
2486 f->filename = filename;
2487 int len = strlen(filename);
2488 char*r1 = strrchr(filename, '/');
2489 char*r2 = strrchr(filename, '\\');
2497 msg("<notice> Adding font \"%s\".", filename);
2498 if(global_fonts_next) {
2499 global_fonts_next->next = f;
2500 global_fonts_next = global_fonts_next->next;
2502 global_fonts_next = global_fonts = f;
2506 void addGlobalLanguageDir(const char*dir)
2508 msg("<notice> Adding %s to language pack directories", dir);
2511 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2512 strcpy(config_file, dir);
2513 strcat(config_file, dirseparator());
2514 strcat(config_file, "add-to-xpdfrc");
2516 fi = fopen(config_file, "rb");
2518 msg("<error> Could not open %s", config_file);
2521 globalParams->parseFile(new GString(config_file), fi);
2525 void addGlobalFontDir(const char*dirname)
2527 #ifdef HAVE_DIRENT_H
2528 msg("<notice> Adding %s to font directories", dirname);
2529 lastfontdir = strdup(dirname);
2530 DIR*dir = opendir(dirname);
2532 msg("<warning> Couldn't open directory %s", dirname);
2537 ent = readdir (dir);
2541 char*name = ent->d_name;
2547 if(!strncasecmp(&name[l-4], ".pfa", 4))
2549 if(!strncasecmp(&name[l-4], ".pfb", 4))
2551 if(!strncasecmp(&name[l-4], ".ttf", 4))
2554 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2555 strcpy(fontname, dirname);
2556 strcat(fontname, dirseparator());
2557 strcat(fontname, name);
2558 addGlobalFont(fontname);
2563 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2567 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2573 this->pagebuflen = 1024;
2574 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2575 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2577 while(pdfpage >= this->pagebuflen)
2579 int oldlen = this->pagebuflen;
2580 this->pagebuflen+=1024;
2581 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2582 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2585 this->pages[pdfpage] = outputpage;
2586 if(pdfpage>this->pagepos)
2587 this->pagepos = pdfpage;
2590 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2591 GfxColorSpace *blendingColorSpace,
2592 GBool isolated, GBool knockout,
2595 const char*colormodename = "";
2597 if(blendingColorSpace) {
2598 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2600 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);
2601 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);
2603 //states[statepos].createsoftmask |= forSoftMask;
2604 states[statepos].createsoftmask = forSoftMask;
2605 states[statepos].transparencygroup = !forSoftMask;
2606 states[statepos].isolated = isolated;
2608 states[statepos].olddevice = this->device;
2609 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2610 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2612 gfxdevice_record_init(this->device);
2614 /*if(!forSoftMask) { ////???
2615 state->setFillOpacity(0.0);
2620 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2623 gfxdevice_t*r = this->device;
2625 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2627 this->device = states[statepos].olddevice;
2629 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2630 /* if these errors occur more often, we should build a seperate
2631 transparency group stack, like xpdf/SplashOutputDev.cc does */
2632 restoreState(state);
2633 this->device = states[statepos].olddevice;
2635 states[statepos].olddevice = 0;
2637 gfxresult_t*recording = r->finish(r);
2639 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2640 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2642 if(states[statepos].createsoftmask) {
2643 states[statepos-1].softmaskrecording = recording;
2645 states[statepos-1].grouprecording = recording;
2648 states[statepos].createsoftmask = 0;
2649 states[statepos].transparencygroup = 0;
2653 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2655 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2656 "colordodge","colorburn","hardlight","softlight","difference",
2657 "exclusion","hue","saturation","color","luminosity"};
2659 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2660 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2662 if(state->getBlendMode() == gfxBlendNormal)
2663 infofeature("transparency groups");
2666 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2667 warnfeature(buffer, 0);
2670 gfxresult_t*grouprecording = states[statepos].grouprecording;
2672 int blendmode = state->getBlendMode();
2673 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2674 int alpha = (int)(state->getFillOpacity()*255);
2675 if(blendmode == gfxBlendMultiply && alpha>200)
2678 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2679 gfxdevice_ops_init(&ops, this->device, alpha);
2680 gfxresult_record_replay(grouprecording, &ops);
2683 grouprecording->destroy(grouprecording);
2685 states[statepos].grouprecording = 0;
2688 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2690 if(states[statepos].softmask) {
2691 /* shouldn't happen, but *does* happen */
2692 clearSoftMask(state);
2695 /* alpha = 1: retrieve mask values from alpha layer
2696 alpha = 0: retrieve mask values from luminance */
2698 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2699 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2700 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2701 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2703 infofeature("soft masks");
2705 warnfeature("soft masks from alpha channel",0);
2707 if(states[statepos].olddevice) {
2708 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2711 states[statepos].olddevice = this->device;
2712 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2713 gfxdevice_record_init(this->device);
2715 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2717 states[statepos].softmask = 1;
2718 states[statepos].softmask_alpha = alpha;
2721 static inline Guchar div255(int x) {
2722 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2725 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2727 if(c < min) c = min;
2728 if(c > max) c = max;
2732 void GFXOutputDev::clearSoftMask(GfxState *state)
2734 if(!states[statepos].softmask)
2736 states[statepos].softmask = 0;
2737 dbg("clearSoftMask statepos=%d", statepos);
2738 msg("<verbose> clearSoftMask statepos=%d", statepos);
2740 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2741 msg("<error> Error in softmask/tgroup ordering");
2745 gfxresult_t*mask = states[statepos].softmaskrecording;
2746 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2747 this->device = states[statepos].olddevice;
2749 /* get outline of all objects below the soft mask */
2750 gfxdevice_t uniondev;
2751 gfxdevice_union_init(&uniondev, 0);
2752 gfxresult_record_replay(below, &uniondev);
2753 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2754 uniondev.finish(&uniondev);
2755 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2756 gfxline_free(belowoutline);belowoutline=0;
2758 this->device->startclip(this->device, belowoutline);
2759 gfxresult_record_replay(below, this->device);
2760 gfxresult_record_replay(mask, this->device);
2761 this->device->endclip(this->device);
2764 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2765 if(width<=0 || height<=0)
2768 gfxdevice_t belowrender;
2769 gfxdevice_render_init(&belowrender);
2770 if(states[statepos+1].isolated) {
2771 belowrender.setparameter(&belowrender, "fillwhite", "1");
2773 belowrender.setparameter(&belowrender, "antialize", "2");
2774 belowrender.startpage(&belowrender, width, height);
2775 gfxresult_record_replay(below, &belowrender);
2776 belowrender.endpage(&belowrender);
2777 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2778 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2779 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2781 gfxdevice_t maskrender;
2782 gfxdevice_render_init(&maskrender);
2783 maskrender.startpage(&maskrender, width, height);
2784 gfxresult_record_replay(mask, &maskrender);
2785 maskrender.endpage(&maskrender);
2786 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2787 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2789 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2790 msg("<fatal> Internal error in mask drawing");
2795 for(y=0;y<height;y++) {
2796 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2797 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2798 for(x=0;x<width;x++) {
2800 if(states[statepos].softmask_alpha) {
2803 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2806 l2->a = div255(alpha*l2->a);
2808 /* DON'T premultiply alpha- this is done by fillbitmap,
2809 depending on the output device */
2810 //l2->r = div255(alpha*l2->r);
2811 //l2->g = div255(alpha*l2->g);
2812 //l2->b = div255(alpha*l2->b);
2818 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2821 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2822 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2824 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2826 mask->destroy(mask);
2827 below->destroy(below);
2828 maskresult->destroy(maskresult);
2829 belowresult->destroy(belowresult);
2830 states[statepos].softmaskrecording = 0;
2835 // public: ~MemCheck()
2837 // delete globalParams;globalParams=0;
2838 // Object::memCheck(stderr);
2839 // gMemReport(stderr);