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_bigchar=0;
568 this->config_convertgradients=1;
569 this->config_break_on_warning=0;
570 this->config_remapunicode=0;
571 this->config_transparent=0;
572 this->config_extrafontdata = 0;
573 this->config_fontquality = 10;
574 this->config_optimize_polygons = 0;
575 this->config_multiply = 1;
577 this->gfxfontlist = gfxfontlist_create();
579 memset(states, 0, sizeof(states));
580 this->featurewarnings = 0;
583 void GFXOutputDev::setParameter(const char*key, const char*value)
585 if(!strcmp(key,"breakonwarning")) {
586 this->config_break_on_warning = atoi(value);
587 } else if(!strcmp(key,"remapunicode")) {
588 this->config_remapunicode = atoi(value);
589 } else if(!strcmp(key,"transparent")) {
590 this->config_transparent = atoi(value);
591 } else if(!strcmp(key,"extrafontdata")) {
592 this->config_extrafontdata = atoi(value);
593 } else if(!strcmp(key,"convertgradients")) {
594 this->config_convertgradients = atoi(value);
595 } else if(!strcmp(key,"multiply")) {
596 this->config_multiply = atoi(value);
597 if(this->config_multiply<1)
598 this->config_multiply=1;
599 } else if(!strcmp(key,"optimize_polygons")) {
600 this->config_optimize_polygons = atoi(value);
601 } else if(!strcmp(key,"bigchar")) {
602 this->config_bigchar = atoi(value);
603 } else if(!strcmp(key,"fontquality")) {
604 this->config_fontquality = atof(value);
605 if(this->config_fontquality<=1)
606 this->config_fontquality=1;
611 void GFXOutputDev::setDevice(gfxdevice_t*dev)
616 void GFXOutputDev::setMove(int x,int y)
618 this->user_movex = x;
619 this->user_movey = y;
622 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
624 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
625 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
627 this->user_clipx1 = x1;
628 this->user_clipy1 = y1;
629 this->user_clipx2 = x2;
630 this->user_clipy2 = y2;
633 static char*getFontName(GfxFont*font)
636 GString*gstr = font->getName();
637 char* fname = gstr==0?0:gstr->getCString();
641 sprintf(buf, "UFONT%d", r->num);
642 fontid = strdup(buf);
644 fontid = strdup(fname);
648 char* plus = strchr(fontid, '+');
649 if(plus && plus < &fontid[strlen(fontid)-1]) {
650 fontname = strdup(plus+1);
652 fontname = strdup(fontid);
658 static void dumpFontInfo(const char*loglevel, GfxFont*font);
659 static int lastdumps[1024];
660 static int lastdumppos = 0;
665 static void showFontError(GfxFont*font, int nr)
669 for(t=0;t<lastdumppos;t++)
670 if(lastdumps[t] == r->num)
674 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
675 lastdumps[lastdumppos++] = r->num;
677 msg("<warning> The following font caused problems:");
679 msg("<warning> The following font caused problems (substituting):");
681 msg("<warning> The following Type 3 Font will be rendered as graphics:");
682 dumpFontInfo("<warning>", font);
685 static void dumpFontInfo(const char*loglevel, GfxFont*font)
687 char* id = getFontID(font);
688 char* name = getFontName(font);
689 Ref* r=font->getID();
690 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
692 GString*gstr = font->getTag();
694 msg("%s| Tag: %s", loglevel, id);
696 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
698 GfxFontType type=font->getType();
700 case fontUnknownType:
701 msg("%s| Type: unknown",loglevel);
704 msg("%s| Type: 1",loglevel);
707 msg("%s| Type: 1C",loglevel);
710 msg("%s| Type: 3",loglevel);
713 msg("%s| Type: TrueType",loglevel);
716 msg("%s| Type: CIDType0",loglevel);
719 msg("%s| Type: CIDType0C",loglevel);
722 msg("%s| Type: CIDType2",loglevel);
727 GBool embedded = font->getEmbeddedFontID(&embRef);
729 if(font->getEmbeddedFontName()) {
730 embeddedName = font->getEmbeddedFontName()->getCString();
733 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
735 gstr = font->getExtFontFile();
737 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
739 // Get font descriptor flags.
740 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
741 if(font->isSerif()) msg("%s| is serif", loglevel);
742 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
743 if(font->isItalic()) msg("%s| is italic", loglevel);
744 if(font->isBold()) msg("%s| is bold", loglevel);
750 //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");}
751 //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");}
753 void dump_outline(gfxline_t*line)
756 if(line->type == gfx_moveTo) {
757 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
758 } else if(line->type == gfx_lineTo) {
759 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
760 } else if(line->type == gfx_splineTo) {
761 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
767 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
769 int num = path->getNumSubpaths();
772 double lastx=0,lasty=0,posx=0,posy=0;
775 msg("<warning> empty path");
779 gfxdrawer_target_gfxline(&draw);
781 for(t = 0; t < num; t++) {
782 GfxSubpath *subpath = path->getSubpath(t);
783 int subnum = subpath->getNumPoints();
784 double bx=0,by=0,cx=0,cy=0;
786 for(s=0;s<subnum;s++) {
789 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
792 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
793 draw.lineTo(&draw, lastx, lasty);
795 draw.moveTo(&draw, x,y);
800 } else if(subpath->getCurve(s) && cpos==0) {
804 } else if(subpath->getCurve(s) && cpos==1) {
812 draw.lineTo(&draw, x,y);
814 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
821 /* fix non-closed lines */
822 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
823 draw.lineTo(&draw, lastx, lasty);
825 gfxline_t*result = (gfxline_t*)draw.result(&draw);
827 gfxline_optimize(result);
832 GBool GFXOutputDev::useTilingPatternFill()
834 infofeature("tiled patterns");
835 // if(config_convertgradients)
839 GBool GFXOutputDev::useShadedFills()
841 infofeature("shaded fills");
842 if(config_convertgradients)
847 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
849 state->transform(x,y,nx,ny);
850 *nx += user_movex + clipmovex;
851 *ny += user_movey + clipmovey;
855 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
856 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
857 int paintType, Dict *resDict,
858 double *mat, double *bbox,
859 int x0, int y0, int x1, int y1,
860 double xStep, double yStep)
862 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
863 int paintType, Dict *resDict,
864 double *mat, double *bbox,
865 int x0, int y0, int x1, int y1,
866 double xStep, double yStep)
869 msg("<debug> tilingPatternFill");
870 infofeature("tiling pattern fills");
873 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
875 msg("<error> functionShadedFill not supported yet");
876 infofeature("function shaded fills");
879 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
883 colspace->getRGB(col, &rgb);
884 c.r = colToByte(rgb.r);
885 c.g = colToByte(rgb.g);
886 c.b = colToByte(rgb.b);
891 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
893 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
894 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
897 this->transformXY(state, x0,y0, &x0,&y0);
898 this->transformXY(state, x1,y1, &x1,&y1);
899 this->transformXY(state, x2,y2, &x2,&y2);
904 shading->getColor(0.0, &color0);
905 shading->getColor(0.5, &color1);
906 shading->getColor(1.0, &color2);
908 GfxColorSpace* colspace = shading->getColorSpace();
910 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
911 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
912 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
913 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
914 infofeature("radial shaded fills");
916 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
920 g[0].color = col2col(colspace, &color0);
921 g[1].color = col2col(colspace, &color1);
922 g[2].color = col2col(colspace, &color2);
927 gfxbbox_t b = states[statepos].clipbbox;
928 gfxline_t p1,p2,p3,p4,p5;
929 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
930 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
931 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
932 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
933 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
936 //m.m00 = (x3-x0); m.m10 = (x1-x0);
937 //m.m01 = (y3-y0); m.m11 = (y1-y0);
938 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
939 m.m00 = (x1-x0); m.m10 = (x2-x0);
940 m.m01 = (y1-y0); m.m11 = (y2-y0);
944 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
948 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
951 shading->getCoords(&x0,&y0,&x1,&y1);
952 this->transformXY(state, x0,y0,&x0,&y0);
953 this->transformXY(state, x1,y1,&x1,&y1);
958 shading->getColor(0.0, &color0);
959 shading->getColor(0.5, &color1);
960 shading->getColor(1.0, &color2);
962 GfxColorSpace* colspace = shading->getColorSpace();
964 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
965 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
966 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
967 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
969 infofeature("axial shaded fills");
971 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
975 g[0].color = col2col(colspace, &color0);
976 g[1].color = col2col(colspace, &color1);
977 g[2].color = col2col(colspace, &color2);
982 double xMin,yMin,xMax,yMax;
983 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
984 this->transformXY(state, xMin, yMin, &xMin, &yMin);
985 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
988 xMin = 1024; yMin = 1024;
990 gfxbbox_t b = states[statepos].clipbbox;
991 gfxline_t p1,p2,p3,p4,p5;
992 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
993 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
994 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
995 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
996 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
998 /* the gradient starts at (-1.0,0.0), so move (0,0) to
999 the middle of the two control points */
1001 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1002 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1003 m.tx = (x0 + x1)/2 - 0.5;
1004 m.ty = (y0 + y1)/2 - 0.5;
1006 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1012 GBool GFXOutputDev::useDrawForm()
1014 infofeature("forms");
1017 void GFXOutputDev::drawForm(Ref id)
1019 msg("<error> drawForm not implemented");
1021 GBool GFXOutputDev::needNonText()
1025 void GFXOutputDev::endPage()
1027 msg("<verbose> endPage (GfxOutputDev)");
1028 if(outer_clip_box) {
1029 device->endclip(device);
1032 this->dashPattern = 0;
1033 /* notice: we're not fully done yet with this page- there might still be
1034 a few calls to drawLink() yet to come */
1037 static inline double sqr(double x) {return x*x;}
1039 #define STROKE_FILL 1
1040 #define STROKE_CLIP 2
1041 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1043 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1044 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1045 double miterLimit = state->getMiterLimit();
1046 double width = state->getTransformedLineWidth();
1049 double opaq = state->getStrokeOpacity();
1051 state->getFillRGB(&rgb);
1053 state->getStrokeRGB(&rgb);
1055 col.r = colToByte(rgb.r);
1056 col.g = colToByte(rgb.g);
1057 col.b = colToByte(rgb.b);
1058 col.a = (unsigned char)(opaq*255);
1060 gfx_capType capType = gfx_capRound;
1061 if(lineCap == 0) capType = gfx_capButt;
1062 else if(lineCap == 1) capType = gfx_capRound;
1063 else if(lineCap == 2) capType = gfx_capSquare;
1065 gfx_joinType joinType = gfx_joinRound;
1066 if(lineJoin == 0) joinType = gfx_joinMiter;
1067 else if(lineJoin == 1) joinType = gfx_joinRound;
1068 else if(lineJoin == 2) joinType = gfx_joinBevel;
1070 gfxline_t*line2 = 0;
1072 if(this->dashLength && this->dashPattern) {
1073 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1076 /* try to find out how much the transformation matrix would
1077 stretch the dashes, and factor that into the dash lengths.
1078 This is not the entirely correct approach- it would be
1079 better to first convert the path to an unscaled version,
1080 then apply dashing, and then transform the path using
1081 the current transformation matrix. However there are few
1082 PDFs which actually stretch a dashed path in a non-orthonormal
1084 double tx1, ty1, tx2, ty2;
1085 this->transformXY(state, 0, 0, &tx1, &ty1);
1086 this->transformXY(state, 1, 1, &tx2, &ty2);
1087 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1089 msg("<trace> %d dashes", this->dashLength);
1090 msg("<trace> | phase: %f", this->dashStart);
1091 for(t=0;t<this->dashLength;t++) {
1092 dash[t] = (float)this->dashPattern[t] * f;
1095 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1097 dash[this->dashLength] = -1;
1098 if(getLogLevel() >= LOGLEVEL_TRACE) {
1102 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1105 msg("<trace> After dashing:");
1108 if(getLogLevel() >= LOGLEVEL_TRACE) {
1109 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1111 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1112 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1114 col.r,col.g,col.b,col.a
1119 if(flags&STROKE_FILL) {
1120 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1121 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1122 if(getLogLevel() >= LOGLEVEL_TRACE) {
1123 dump_outline(gfxline);
1126 msg("<warning> Empty polygon (resulting from stroked line)");
1128 if(flags&STROKE_CLIP) {
1129 device->startclip(device, gfxline);
1130 states[statepos].clipping++;
1132 device->fill(device, gfxline, &col);
1134 gfxline_free(gfxline);
1137 if(flags&STROKE_CLIP)
1138 msg("<error> Stroke&clip not supported at the same time");
1139 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1143 gfxline_free(line2);
1146 gfxcolor_t getFillColor(GfxState * state)
1149 double opaq = state->getFillOpacity();
1150 state->getFillRGB(&rgb);
1152 col.r = colToByte(rgb.r);
1153 col.g = colToByte(rgb.g);
1154 col.b = colToByte(rgb.b);
1155 col.a = (unsigned char)(opaq*255);
1159 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1161 gfxcolor_t col = getFillColor(state);
1163 if(getLogLevel() >= LOGLEVEL_TRACE) {
1164 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1167 device->fill(device, line, &col);
1170 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1172 if(getLogLevel() >= LOGLEVEL_TRACE) {
1175 gfxbbox_t bbox = gfxline_getbbox(line);
1176 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1178 device->startclip(device, line);
1179 states[statepos].clipping++;
1182 void GFXOutputDev::clip(GfxState *state)
1184 GfxPath * path = state->getPath();
1185 msg("<trace> clip");
1186 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1187 if(config_optimize_polygons) {
1188 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1192 clipToGfxLine(state, line);
1196 void GFXOutputDev::eoClip(GfxState *state)
1198 GfxPath * path = state->getPath();
1199 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1200 clipToGfxLine(state, line);
1203 void GFXOutputDev::clipToStrokePath(GfxState *state)
1205 GfxPath * path = state->getPath();
1206 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1208 if(getLogLevel() >= LOGLEVEL_TRACE) {
1209 double width = state->getTransformedLineWidth();
1210 msg("<trace> cliptostrokepath width=%f", width);
1214 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1218 void GFXOutputDev::finish()
1220 if(outer_clip_box) {
1222 device->endclip(device);
1228 GFXOutputDev::~GFXOutputDev()
1233 free(this->pages); this->pages = 0;
1235 if(this->dashPattern) {
1236 free(this->dashPattern);this->dashPattern = 0;
1239 feature_t*f = this->featurewarnings;
1241 feature_t*next = f->next;
1243 free(f->string);f->string =0;
1249 this->featurewarnings = 0;
1251 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1253 GBool GFXOutputDev::upsideDown()
1257 GBool GFXOutputDev::useDrawChar()
1262 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1263 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1265 static char tmp_printstr[4096];
1266 char* makeStringPrintable(char*str)
1268 int len = strlen(str);
1275 for(t=0;t<len;t++) {
1280 tmp_printstr[t] = c;
1283 tmp_printstr[len++] = '.';
1284 tmp_printstr[len++] = '.';
1285 tmp_printstr[len++] = '.';
1287 tmp_printstr[len] = 0;
1288 return tmp_printstr;
1290 #define INTERNAL_FONT_SIZE 1024.0
1291 void GFXOutputDev::updateFontMatrix(GfxState*state)
1293 double* ctm = state->getCTM();
1294 double fontSize = state->getFontSize();
1295 double*textMat = state->getTextMat();
1297 /* taking the absolute value of horizScaling seems to be required for
1298 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1299 double hscale = fabs(state->getHorizScaling());
1301 // from xpdf-3.02/SplashOutputDev:updateFont
1302 double mm11 = textMat[0] * fontSize * hscale;
1303 double mm12 = textMat[1] * fontSize * hscale;
1304 double mm21 = textMat[2] * fontSize;
1305 double mm22 = textMat[3] * fontSize;
1307 // multiply with ctm, like state->getFontTransMat() does
1308 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1309 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1310 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1311 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1312 this->current_font_matrix.tx = 0;
1313 this->current_font_matrix.ty = 0;
1316 void GFXOutputDev::beginString(GfxState *state, GString *s)
1318 int render = state->getRender();
1319 if(current_text_stroke) {
1320 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1323 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1326 static gfxline_t* mkEmptyGfxShape(double x, double y)
1328 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1329 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1333 static char isValidUnicode(int c)
1335 if(c>=32 && c<0x2fffe)
1340 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1341 double dx, double dy,
1342 double originX, double originY,
1343 CharCode charid, int nBytes, Unicode *_u, int uLen)
1345 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1346 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1350 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1352 int render = state->getRender();
1353 gfxcolor_t col = getFillColor(state);
1355 // check for invisible text -- this is used by Acrobat Capture
1356 if (render == RENDER_INVISIBLE) {
1358 if(!config_extrafontdata)
1362 GfxFont*font = state->getFont();
1364 if(font->getType() == fontType3) {
1365 /* type 3 chars are passed as graphics */
1366 msg("<debug> type3 char at %f/%f", x, y);
1370 Unicode u = uLen?(_u[0]):0;
1371 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);
1373 gfxmatrix_t m = this->current_font_matrix;
1374 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1375 m.tx += originX; m.ty += originY;
1377 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1378 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1380 msg("<debug> Drawing glyph %d as shape", charid);
1382 msg("<notice> Some texts will be rendered as shape");
1385 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1386 gfxline_t*tglyph = gfxline_clone(glyph);
1387 gfxline_transform(tglyph, &m);
1388 if((render&3) != RENDER_INVISIBLE) {
1389 gfxline_t*add = gfxline_clone(tglyph);
1390 current_text_stroke = gfxline_append(current_text_stroke, add);
1392 if(render&RENDER_CLIP) {
1393 gfxline_t*add = gfxline_clone(tglyph);
1394 current_text_clip = gfxline_append(current_text_clip, add);
1395 if(!current_text_clip) {
1396 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1399 gfxline_free(tglyph);
1403 void GFXOutputDev::endString(GfxState *state)
1405 int render = state->getRender();
1406 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1408 if(current_text_stroke) {
1409 /* fillstroke and stroke text rendering objects we can process right
1410 now (as there may be texts of other rendering modes in this
1411 text object)- clipping objects have to wait until endTextObject,
1413 device->setparameter(device, "mark","TXT");
1414 if((render&3) == RENDER_FILL) {
1415 fillGfxLine(state, current_text_stroke);
1416 gfxline_free(current_text_stroke);
1417 current_text_stroke = 0;
1418 } else if((render&3) == RENDER_FILLSTROKE) {
1419 fillGfxLine(state, current_text_stroke);
1420 strokeGfxline(state, current_text_stroke,0);
1421 gfxline_free(current_text_stroke);
1422 current_text_stroke = 0;
1423 } else if((render&3) == RENDER_STROKE) {
1424 strokeGfxline(state, current_text_stroke,0);
1425 gfxline_free(current_text_stroke);
1426 current_text_stroke = 0;
1428 device->setparameter(device, "mark","");
1432 void GFXOutputDev::endTextObject(GfxState *state)
1434 int render = state->getRender();
1435 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1437 if(current_text_clip) {
1438 device->setparameter(device, "mark","TXT");
1439 clipToGfxLine(state, current_text_clip);
1440 device->setparameter(device, "mark","");
1441 gfxline_free(current_text_clip);
1442 current_text_clip = 0;
1446 /* the logic seems to be as following:
1447 first, beginType3Char is called, with the charcode and the coordinates.
1448 if this function returns true, it already knew about the char and has now drawn it.
1449 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1450 called with some parameters.
1451 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1452 at the position first passed to beginType3Char). the char ends with endType3Char.
1454 The drawing operations between beginType3Char and endType3Char are somewhat different to
1455 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1456 color determines the color of a font)
1459 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1461 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1464 if(config_extrafontdata && current_fontinfo) {
1466 gfxmatrix_t m = this->current_font_matrix;
1467 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1468 m.m00*=INTERNAL_FONT_SIZE;
1469 m.m01*=INTERNAL_FONT_SIZE;
1470 m.m10*=INTERNAL_FONT_SIZE;
1471 m.m11*=INTERNAL_FONT_SIZE;
1473 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1474 msg("<error> Invalid charid %d for font", charid);
1477 gfxcolor_t col={0,0,0,0};
1478 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1479 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1483 /* the character itself is going to be passed using the draw functions */
1484 return gFalse; /* gTrue= is_in_cache? */
1487 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1489 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1492 void GFXOutputDev::endType3Char(GfxState *state)
1495 msg("<debug> endType3Char");
1498 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1500 this->currentpage = pageNum;
1502 int rot = doc->getPageRotate(1);
1503 gfxcolor_t white = {255,255,255,255};
1504 gfxcolor_t black = {255,0,0,0};
1506 gfxline_t clippath[5];
1508 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1509 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1510 Use CropBox, not MediaBox, as page size
1517 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1518 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1520 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1521 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1523 this->clipmovex = -(int)x1;
1524 this->clipmovey = -(int)y1;
1526 /* apply user clip box */
1527 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1528 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1529 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1530 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1531 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1532 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1535 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1537 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);
1539 msg("<verbose> page is rotated %d degrees", rot);
1541 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1542 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1543 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1544 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1545 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1546 device->startclip(device, clippath); outer_clip_box = 1;
1547 if(!config_transparent) {
1548 device->fill(device, clippath, &white);
1550 states[statepos].clipbbox.xmin = x1;
1551 states[statepos].clipbbox.ymin = x1;
1552 states[statepos].clipbbox.xmax = x2;
1553 states[statepos].clipbbox.ymax = y2;
1555 this->dashPattern = 0;
1556 this->dashLength = 0;
1557 this->dashStart = 0;
1561 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1563 double x1, y1, x2, y2;
1564 gfxline_t points[5];
1567 msg("<debug> drawlink");
1569 link->getRect(&x1, &y1, &x2, &y2);
1570 cvtUserToDev(x1, y1, &x, &y);
1571 points[0].type = gfx_moveTo;
1572 points[0].x = points[4].x = x + user_movex + clipmovex;
1573 points[0].y = points[4].y = y + user_movey + clipmovey;
1574 points[0].next = &points[1];
1575 cvtUserToDev(x2, y1, &x, &y);
1576 points[1].type = gfx_lineTo;
1577 points[1].x = x + user_movex + clipmovex;
1578 points[1].y = y + user_movey + clipmovey;
1579 points[1].next = &points[2];
1580 cvtUserToDev(x2, y2, &x, &y);
1581 points[2].type = gfx_lineTo;
1582 points[2].x = x + user_movex + clipmovex;
1583 points[2].y = y + user_movey + clipmovey;
1584 points[2].next = &points[3];
1585 cvtUserToDev(x1, y2, &x, &y);
1586 points[3].type = gfx_lineTo;
1587 points[3].x = x + user_movex + clipmovex;
1588 points[3].y = y + user_movey + clipmovey;
1589 points[3].next = &points[4];
1590 cvtUserToDev(x1, y1, &x, &y);
1591 points[4].type = gfx_lineTo;
1592 points[4].x = x + user_movex + clipmovex;
1593 points[4].y = y + user_movey + clipmovey;
1596 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1597 points[0].x, points[0].y,
1598 points[1].x, points[1].y,
1599 points[2].x, points[2].y,
1600 points[3].x, points[3].y);
1602 if(getLogLevel() >= LOGLEVEL_TRACE) {
1603 dump_outline(points);
1606 LinkAction*action=link->getAction();
1609 const char*type = "-?-";
1612 msg("<trace> drawlink action=%d", action->getKind());
1613 switch(action->getKind())
1617 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1618 LinkDest *dest=NULL;
1619 if (ha->getDest()==NULL)
1620 dest=catalog->findDest(ha->getNamedDest());
1621 else dest=ha->getDest();
1623 if (dest->isPageRef()){
1624 Ref pageref=dest->getPageRef();
1625 page=catalog->findPage(pageref.num,pageref.gen);
1627 else page=dest->getPageNum();
1628 sprintf(buf, "%d", page);
1635 LinkGoToR*l = (LinkGoToR*)action;
1636 GString*g = l->getFileName();
1638 s = strdup(g->getCString());
1640 /* if the GoToR link has no filename, then
1641 try to find a refernce in the *local*
1643 GString*g = l->getNamedDest();
1645 s = strdup(g->getCString());
1651 LinkNamed*l = (LinkNamed*)action;
1652 GString*name = l->getName();
1654 s = strdup(name->lowerCase()->getCString());
1655 named = name->getCString();
1658 if(strstr(s, "next") || strstr(s, "forward"))
1660 page = currentpage + 1;
1662 else if(strstr(s, "prev") || strstr(s, "back"))
1664 page = currentpage - 1;
1666 else if(strstr(s, "last") || strstr(s, "end"))
1668 if(pages && pagepos>0)
1669 page = pages[pagepos-1];
1671 else if(strstr(s, "first") || strstr(s, "top"))
1679 case actionLaunch: {
1681 LinkLaunch*l = (LinkLaunch*)action;
1682 GString * str = new GString(l->getFileName());
1683 GString * params = l->getParams();
1685 str->append(params);
1686 s = strdup(str->getCString());
1693 LinkURI*l = (LinkURI*)action;
1694 GString*g = l->getURI();
1696 url = g->getCString();
1701 case actionUnknown: {
1703 LinkUnknown*l = (LinkUnknown*)action;
1708 msg("<error> Unknown link type!");
1713 if(!s) s = strdup("-?-");
1715 msg("<trace> drawlink s=%s", s);
1717 if(!linkinfo && (page || s))
1719 msg("<notice> File contains links");
1727 for(t=1;t<=pagepos;t++) {
1728 if(pages[t]==page) {
1737 sprintf(buf, "page%d", lpage);
1738 device->drawlink(device, points, buf);
1742 device->drawlink(device, points, s);
1745 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1749 void GFXOutputDev::saveState(GfxState *state) {
1750 dbg("saveState %08x", state); dbgindent+=2;
1752 msg("<trace> saveState %08x", state);
1755 msg("<fatal> Too many nested states in pdf.");
1759 states[statepos].state = state;
1760 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1761 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1762 states[statepos].clipping = 0;
1763 states[statepos].olddevice = 0;
1764 states[statepos].clipbbox = states[statepos-1].clipbbox;
1767 void GFXOutputDev::restoreState(GfxState *state) {
1768 dbgindent-=2; dbg("restoreState %08x", state);
1771 msg("<fatal> Invalid restoreState");
1774 msg("<trace> restoreState %08x%s%s", state,
1775 states[statepos].softmask?" (end softmask)":"",
1776 states[statepos].clipping?" (end clipping)":"");
1777 if(states[statepos].softmask) {
1778 clearSoftMask(state);
1782 while(states[statepos].clipping) {
1783 device->endclip(device);
1784 states[statepos].clipping--;
1786 if(states[statepos].state!=state) {
1787 msg("<fatal> bad state nesting");
1790 states[statepos].state=0;
1794 void GFXOutputDev::updateLineDash(GfxState *state)
1796 if(this->dashPattern) {
1797 free(this->dashPattern);this->dashPattern = 0;
1799 double *pattern = 0;
1800 state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1801 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1802 if(!this->dashLength) {
1803 this->dashPattern = 0;
1805 double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1806 memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1807 this->dashPattern = p;
1811 void GFXOutputDev::updateLineWidth(GfxState *state)
1813 double width = state->getTransformedLineWidth();
1816 void GFXOutputDev::updateLineCap(GfxState *state)
1818 int c = state->getLineCap();
1821 void GFXOutputDev::updateLineJoin(GfxState *state)
1823 int j = state->getLineJoin();
1826 void GFXOutputDev::updateFillColor(GfxState *state)
1829 double opaq = state->getFillOpacity();
1830 state->getFillRGB(&rgb);
1832 void GFXOutputDev::updateFillOpacity(GfxState *state)
1835 double opaq = state->getFillOpacity();
1836 state->getFillRGB(&rgb);
1837 dbg("update fillopaq %f", opaq);
1839 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1841 double opaq = state->getFillOpacity();
1842 dbg("update strokeopaq %f", opaq);
1844 void GFXOutputDev::updateFillOverprint(GfxState *state)
1846 double opaq = state->getFillOverprint();
1847 dbg("update filloverprint %f", opaq);
1849 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1851 double opaq = state->getStrokeOverprint();
1852 dbg("update strokeoverprint %f", opaq);
1854 void GFXOutputDev::updateTransfer(GfxState *state)
1856 dbg("update transfer");
1860 void GFXOutputDev::updateStrokeColor(GfxState *state)
1863 double opaq = state->getStrokeOpacity();
1864 state->getStrokeRGB(&rgb);
1868 gfxfont_t* GFXOutputDev::createGfxFont(GfxFont*xpdffont, FontInfo*src)
1870 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1871 memset(font, 0, sizeof(gfxfont_t));
1873 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1874 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1878 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1880 //printf("%d glyphs\n", font->num_glyphs);
1881 font->num_glyphs = 0;
1882 font->ascent = fabs(src->descender);
1883 font->descent = fabs(src->ascender);
1885 for(t=0;t<src->num_glyphs;t++) {
1886 if(src->glyphs[t]) {
1887 SplashPath*path = src->glyphs[t]->path;
1888 int len = path?path->getLength():0;
1889 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1890 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1891 src->glyphs[t]->glyphid = font->num_glyphs;
1892 glyph->unicode = src->glyphs[t]->unicode;
1893 if(glyph->unicode >= font->max_unicode)
1894 font->max_unicode = glyph->unicode+1;
1896 gfxdrawer_target_gfxline(&drawer);
1900 for(s=0;s<len;s++) {
1903 path->getPoint(s, &x, &y, &f);
1906 if(f&splashPathFirst) {
1907 drawer.moveTo(&drawer, x*scale, y*scale);
1909 if(f&splashPathCurve) {
1911 path->getPoint(++s, &x2, &y2, &f);
1912 if(f&splashPathCurve) {
1914 path->getPoint(++s, &x3, &y3, &f);
1915 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1917 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1920 drawer.lineTo(&drawer, x*scale, y*scale);
1922 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1923 // (f&splashPathFirst)?"first":"",
1924 // (f&splashPathLast)?"last":"");
1927 glyph->line = (gfxline_t*)drawer.result(&drawer);
1928 if(src->glyphs[t]->advance>0) {
1929 glyph->advance = src->glyphs[t]->advance;
1931 msg("<warning> Approximating advance value for glyph %d", t);
1932 glyph->advance = xmax*scale;
1934 if(this->config_bigchar) {
1935 double max = src->glyphs[t]->advance_max;
1936 if(max>0 && max > glyph->advance) {
1937 glyph->advance = max;
1944 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1945 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1946 for(t=0;t<font->num_glyphs;t++) {
1947 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1948 font->unicode2glyph[font->glyphs[t].unicode] = t;
1952 msg("<trace> %d glyphs.", t, font->num_glyphs);
1956 void GFXOutputDev::updateFont(GfxState *state)
1958 GfxFont* gfxFont = state->getFont();
1962 char*id = getFontID(gfxFont);
1963 msg("<verbose> Updating font to %s", id);
1964 if(gfxFont->getType() == fontType3) {
1965 infofeature("Type3 fonts");
1966 if(!config_extrafontdata) {
1971 msg("<error> Internal Error: FontID is null");
1975 this->current_fontinfo = this->info->getFont(id);
1976 if(!this->current_fontinfo) {
1977 msg("<error> Internal Error: no fontinfo for font %s", id);
1980 if(!this->current_fontinfo->seen) {
1981 dumpFontInfo("<verbose>", gfxFont);
1984 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1986 font = this->createGfxFont(gfxFont, current_fontinfo);
1987 font->id = strdup(id);
1988 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1990 device->addfont(device, font);
1992 current_gfxfont = font;
1995 updateFontMatrix(state);
1998 #define SQR(x) ((x)*(x))
2000 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2002 if((newwidth<1 || newheight<1) ||
2003 (width<=newwidth || height<=newheight))
2005 unsigned char*newdata;
2007 newdata= (unsigned char*)malloc(newwidth*newheight);
2008 double fx = ((double)width)/newwidth;
2009 double fy = ((double)height)/newheight;
2011 int blocksize = (int)(8192/(fx*fy));
2012 int r = 8192*256/palettesize;
2013 for(x=0;x<newwidth;x++) {
2014 double ex = px + fx;
2015 int fromx = (int)px;
2017 int xweight1 = (int)((1-(px-fromx))*256);
2018 int xweight2 = (int)((ex-tox)*256);
2020 for(y=0;y<newheight;y++) {
2021 double ey = py + fy;
2022 int fromy = (int)py;
2024 int yweight1 = (int)((1-(py-fromy))*256);
2025 int yweight2 = (int)((ey-toy)*256);
2032 for(xx=fromx;xx<=tox;xx++)
2033 for(yy=fromy;yy<=toy;yy++) {
2034 int b = 1-data[width*yy+xx];
2036 if(xx==fromx) weight = (weight*xweight1)/256;
2037 if(xx==tox) weight = (weight*xweight2)/256;
2038 if(yy==fromy) weight = (weight*yweight1)/256;
2039 if(yy==toy) weight = (weight*yweight2)/256;
2042 //if(a) a=(palettesize-1)*r/blocksize;
2043 newdata[y*newwidth+x] = (a*blocksize)/r;
2051 #define IMAGE_TYPE_JPEG 0
2052 #define IMAGE_TYPE_LOSSLESS 1
2054 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2055 double x1,double y1,
2056 double x2,double y2,
2057 double x3,double y3,
2058 double x4,double y4, int type, int multiply)
2060 gfxcolor_t*newpic=0;
2062 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2063 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2065 gfxline_t p1,p2,p3,p4,p5;
2066 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2067 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2068 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2069 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2070 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2072 {p1.x = (int)(p1.x*20)/20.0;
2073 p1.y = (int)(p1.y*20)/20.0;
2074 p2.x = (int)(p2.x*20)/20.0;
2075 p2.y = (int)(p2.y*20)/20.0;
2076 p3.x = (int)(p3.x*20)/20.0;
2077 p3.y = (int)(p3.y*20)/20.0;
2078 p4.x = (int)(p4.x*20)/20.0;
2079 p4.y = (int)(p4.y*20)/20.0;
2080 p5.x = (int)(p5.x*20)/20.0;
2081 p5.y = (int)(p5.y*20)/20.0;
2085 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2086 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2088 m.tx = p1.x - 0.5*multiply;
2089 m.ty = p1.y - 0.5*multiply;
2092 img.data = (gfxcolor_t*)data;
2096 if(type == IMAGE_TYPE_JPEG)
2097 /* TODO: pass image_dpi to device instead */
2098 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2101 dev->fillbitmap(dev, &p1, &img, &m, 0);
2104 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2105 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2107 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2110 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2111 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2113 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2117 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2118 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2119 GBool inlineImg, int mask, int*maskColors,
2120 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2122 /* the code in this function is *old*. It's not pretty, but it works. */
2124 double x1,y1,x2,y2,x3,y3,x4,y4;
2125 ImageStream *imgStr;
2130 unsigned char* maskbitmap = 0;
2133 ncomps = colorMap->getNumPixelComps();
2134 bits = colorMap->getBits();
2139 unsigned char buf[8];
2140 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2142 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2143 imgMaskStr->reset();
2144 unsigned char pal[256];
2145 int n = 1 << colorMap->getBits();
2150 maskColorMap->getGray(pixBuf, &gray);
2151 pal[t] = colToByte(gray);
2153 for (y = 0; y < maskHeight; y++) {
2154 for (x = 0; x < maskWidth; x++) {
2155 imgMaskStr->getPixel(buf);
2156 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2161 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2162 imgMaskStr->reset();
2163 for (y = 0; y < maskHeight; y++) {
2164 for (x = 0; x < maskWidth; x++) {
2165 imgMaskStr->getPixel(buf);
2167 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2175 imgStr = new ImageStream(str, width, ncomps,bits);
2178 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2180 msg("<verbose> Ignoring %d by %d image", width, height);
2181 unsigned char buf[8];
2183 for (y = 0; y < height; ++y)
2184 for (x = 0; x < width; ++x) {
2185 imgStr->getPixel(buf);
2193 this->transformXY(state, 0, 1, &x1, &y1);
2194 this->transformXY(state, 0, 0, &x2, &y2);
2195 this->transformXY(state, 1, 0, &x3, &y3);
2196 this->transformXY(state, 1, 1, &x4, &y4);
2199 /* as type 3 bitmaps are antialized, we need to place them
2200 at integer coordinates, otherwise flash player's antializing
2201 will kick in and make everything blurry */
2202 x1 = (int)(x1);y1 = (int)(y1);
2203 x2 = (int)(x2);y2 = (int)(y2);
2204 x3 = (int)(x3);y3 = (int)(y3);
2205 x4 = (int)(x4);y4 = (int)(y4);
2208 if(!pbminfo && !(str->getKind()==strDCT)) {
2210 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2214 msg("<verbose> drawing %d by %d masked picture", width, height);
2216 if(!jpeginfo && (str->getKind()==strDCT)) {
2217 msg("<notice> File contains jpeg pictures");
2222 unsigned char buf[8];
2224 unsigned char*pic = new unsigned char[width*height];
2225 gfxcolor_t pal[256];
2227 state->getFillRGB(&rgb);
2229 memset(pal,255,sizeof(pal));
2230 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2231 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2232 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2233 pal[0].a = 255; pal[1].a = 0;
2236 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2237 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2238 for (y = 0; y < height; ++y)
2239 for (x = 0; x < width; ++x)
2241 imgStr->getPixel(buf);
2244 pic[width*y+x] = buf[0];
2248 unsigned char*pic2 = 0;
2251 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2260 height = realheight;
2264 /* make a black/white palette */
2266 float r = 255./(float)(numpalette-1);
2268 for(t=0;t<numpalette;t++) {
2269 pal[t].r = colToByte(rgb.r);
2270 pal[t].g = colToByte(rgb.g);
2271 pal[t].b = colToByte(rgb.b);
2272 pal[t].a = (unsigned char)(t*r);
2277 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2278 for (y = 0; y < height; ++y) {
2279 for (x = 0; x < width; ++x) {
2280 pic2[width*y+x] = pal[pic[y*width+x]];
2283 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2287 if(maskbitmap) free(maskbitmap);
2293 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2294 gfxcolor_t*pic=new gfxcolor_t[width*height];
2295 for (y = 0; y < height; ++y) {
2296 for (x = 0; x < width; ++x) {
2297 imgStr->getPixel(pixBuf);
2298 colorMap->getRGB(pixBuf, &rgb);
2299 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2300 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2301 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2302 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2304 int x1 = x*maskWidth/width;
2305 int y1 = y*maskHeight/height;
2306 int x2 = (x+1)*maskWidth/width;
2307 int y2 = (y+1)*maskHeight/height;
2309 unsigned int alpha=0;
2310 unsigned int count=0;
2311 for(xx=x1;xx<x2;xx++)
2312 for(yy=y1;yy<y2;yy++) {
2313 alpha += maskbitmap[yy*maskWidth+xx];
2317 pic[width*y+x].a = alpha / count;
2319 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2324 if(str->getKind()==strDCT)
2325 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2327 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2330 if(maskbitmap) free(maskbitmap);
2333 gfxcolor_t*pic=new gfxcolor_t[width*height];
2334 gfxcolor_t pal[256];
2335 int n = 1 << colorMap->getBits();
2337 for(t=0;t<256;t++) {
2339 colorMap->getRGB(pixBuf, &rgb);
2341 {/*if(maskColors && *maskColors==t) {
2342 msg("<notice> Color %d is transparent", t);
2343 if (imgData->maskColors) {
2345 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2346 if (pix[i] < imgData->maskColors[2*i] ||
2347 pix[i] > imgData->maskColors[2*i+1]) {
2362 pal[t].r = (unsigned char)(colToByte(rgb.r));
2363 pal[t].g = (unsigned char)(colToByte(rgb.g));
2364 pal[t].b = (unsigned char)(colToByte(rgb.b));
2365 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2368 for (y = 0; y < height; ++y) {
2369 for (x = 0; x < width; ++x) {
2370 imgStr->getPixel(pixBuf);
2371 pic[width*y+x] = pal[pixBuf[0]];
2375 if(maskWidth < width && maskHeight < height) {
2376 for(y = 0; y < height; y++) {
2377 for (x = 0; x < width; x++) {
2378 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2382 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2383 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2384 double dx = width / maskWidth;
2385 double dy = height / maskHeight;
2387 for(y = 0; y < maskHeight; y++) {
2389 for (x = 0; x < maskWidth; x++) {
2390 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2391 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2399 height = maskHeight;
2402 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2406 if(maskbitmap) free(maskbitmap);
2411 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2412 int width, int height, GBool invert,
2415 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2416 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2417 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2420 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2421 int width, int height, GfxImageColorMap *colorMap,
2422 int *maskColors, GBool inlineImg)
2424 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2425 colorMap?"colorMap":"no colorMap",
2426 maskColors?"maskColors":"no maskColors",
2428 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2429 colorMap?"colorMap":"no colorMap",
2430 maskColors?"maskColors":"no maskColors",
2433 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2434 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2435 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2438 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2439 int width, int height,
2440 GfxImageColorMap *colorMap,
2441 Stream *maskStr, int maskWidth, int maskHeight,
2444 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2445 colorMap?"colorMap":"no colorMap",
2446 maskWidth, maskHeight);
2447 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2448 colorMap?"colorMap":"no colorMap",
2449 maskWidth, maskHeight);
2451 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2452 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2453 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2456 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2457 int width, int height,
2458 GfxImageColorMap *colorMap,
2460 int maskWidth, int maskHeight,
2461 GfxImageColorMap *maskColorMap)
2463 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2464 colorMap?"colorMap":"no colorMap",
2465 maskWidth, maskHeight);
2466 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2467 colorMap?"colorMap":"no colorMap",
2468 maskWidth, maskHeight);
2470 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2471 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2472 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2475 void GFXOutputDev::stroke(GfxState *state)
2479 GfxPath * path = state->getPath();
2480 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2481 strokeGfxline(state, line, 0);
2485 void GFXOutputDev::fill(GfxState *state)
2487 gfxcolor_t col = getFillColor(state);
2488 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2490 GfxPath * path = state->getPath();
2491 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2492 if(config_optimize_polygons) {
2493 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2497 fillGfxLine(state, line);
2501 void GFXOutputDev::eoFill(GfxState *state)
2503 gfxcolor_t col = getFillColor(state);
2504 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2506 GfxPath * path = state->getPath();
2507 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2508 fillGfxLine(state, line);
2513 static const char* dirseparator()
2522 void addGlobalFont(const char*filename)
2524 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2525 memset(f, 0, sizeof(fontfile_t));
2526 f->filename = filename;
2527 int len = strlen(filename);
2528 char*r1 = strrchr(filename, '/');
2529 char*r2 = strrchr(filename, '\\');
2537 msg("<notice> Adding font \"%s\".", filename);
2538 if(global_fonts_next) {
2539 global_fonts_next->next = f;
2540 global_fonts_next = global_fonts_next->next;
2542 global_fonts_next = global_fonts = f;
2546 void addGlobalLanguageDir(const char*dir)
2548 msg("<notice> Adding %s to language pack directories", dir);
2551 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2552 strcpy(config_file, dir);
2553 strcat(config_file, dirseparator());
2554 strcat(config_file, "add-to-xpdfrc");
2556 fi = fopen(config_file, "rb");
2558 msg("<error> Could not open %s", config_file);
2561 globalParams->parseFile(new GString(config_file), fi);
2565 void addGlobalFontDir(const char*dirname)
2567 #ifdef HAVE_DIRENT_H
2568 msg("<notice> Adding %s to font directories", dirname);
2569 lastfontdir = strdup(dirname);
2570 DIR*dir = opendir(dirname);
2572 msg("<warning> Couldn't open directory %s", dirname);
2577 ent = readdir (dir);
2581 char*name = ent->d_name;
2587 if(!strncasecmp(&name[l-4], ".pfa", 4))
2589 if(!strncasecmp(&name[l-4], ".pfb", 4))
2591 if(!strncasecmp(&name[l-4], ".ttf", 4))
2594 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2595 strcpy(fontname, dirname);
2596 strcat(fontname, dirseparator());
2597 strcat(fontname, name);
2598 addGlobalFont(fontname);
2603 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2607 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2613 this->pagebuflen = 1024;
2614 if(pdfpage > this->pagebuflen)
2615 this->pagebuflen = pdfpage+1;
2616 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2617 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2620 while(pdfpage >= this->pagebuflen)
2622 int oldlen = this->pagebuflen;
2623 this->pagebuflen+=1024;
2624 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2625 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2628 this->pages[pdfpage] = outputpage;
2629 if(pdfpage>this->pagepos)
2630 this->pagepos = pdfpage;
2633 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2634 GfxColorSpace *blendingColorSpace,
2635 GBool isolated, GBool knockout,
2638 const char*colormodename = "";
2640 if(blendingColorSpace) {
2641 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2643 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);
2644 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);
2646 //states[statepos].createsoftmask |= forSoftMask;
2647 states[statepos].createsoftmask = forSoftMask;
2648 states[statepos].transparencygroup = !forSoftMask;
2649 states[statepos].isolated = isolated;
2651 states[statepos].olddevice = this->device;
2652 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2653 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2655 gfxdevice_record_init(this->device);
2657 /*if(!forSoftMask) { ////???
2658 state->setFillOpacity(0.0);
2663 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2666 gfxdevice_t*r = this->device;
2668 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2670 this->device = states[statepos].olddevice;
2672 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2673 /* if these errors occur more often, we should build a seperate
2674 transparency group stack, like xpdf/SplashOutputDev.cc does */
2675 restoreState(state);
2676 this->device = states[statepos].olddevice;
2678 states[statepos].olddevice = 0;
2680 gfxresult_t*recording = r->finish(r);
2682 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2683 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2685 if(states[statepos].createsoftmask) {
2686 states[statepos-1].softmaskrecording = recording;
2688 states[statepos-1].grouprecording = recording;
2691 states[statepos].createsoftmask = 0;
2692 states[statepos].transparencygroup = 0;
2696 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2698 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2699 "colordodge","colorburn","hardlight","softlight","difference",
2700 "exclusion","hue","saturation","color","luminosity"};
2702 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2703 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2705 if(state->getBlendMode() == gfxBlendNormal)
2706 infofeature("transparency groups");
2709 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2710 warnfeature(buffer, 0);
2713 gfxresult_t*grouprecording = states[statepos].grouprecording;
2715 int blendmode = state->getBlendMode();
2716 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2717 int alpha = (int)(state->getFillOpacity()*255);
2718 if(blendmode == gfxBlendMultiply && alpha>200)
2721 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2722 gfxdevice_ops_init(&ops, this->device, alpha);
2723 gfxresult_record_replay(grouprecording, &ops);
2726 grouprecording->destroy(grouprecording);
2728 states[statepos].grouprecording = 0;
2731 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2733 if(states[statepos].softmask) {
2734 /* shouldn't happen, but *does* happen */
2735 clearSoftMask(state);
2738 /* alpha = 1: retrieve mask values from alpha layer
2739 alpha = 0: retrieve mask values from luminance */
2741 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2742 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2743 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2744 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2746 infofeature("soft masks");
2748 warnfeature("soft masks from alpha channel",0);
2750 if(states[statepos].olddevice) {
2751 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2754 states[statepos].olddevice = this->device;
2755 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2756 gfxdevice_record_init(this->device);
2758 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2760 states[statepos].softmask = 1;
2761 states[statepos].softmask_alpha = alpha;
2764 static inline Guchar div255(int x) {
2765 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2768 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2770 if(c < min) c = min;
2771 if(c > max) c = max;
2775 void GFXOutputDev::clearSoftMask(GfxState *state)
2777 if(!states[statepos].softmask)
2779 states[statepos].softmask = 0;
2780 dbg("clearSoftMask statepos=%d", statepos);
2781 msg("<verbose> clearSoftMask statepos=%d", statepos);
2783 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2784 msg("<error> Error in softmask/tgroup ordering");
2788 gfxresult_t*mask = states[statepos].softmaskrecording;
2789 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2790 this->device = states[statepos].olddevice;
2792 /* get outline of all objects below the soft mask */
2793 gfxdevice_t uniondev;
2794 gfxdevice_union_init(&uniondev, 0);
2795 gfxresult_record_replay(below, &uniondev);
2796 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2797 uniondev.finish(&uniondev);
2798 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2799 gfxline_free(belowoutline);belowoutline=0;
2801 this->device->startclip(this->device, belowoutline);
2802 gfxresult_record_replay(below, this->device);
2803 gfxresult_record_replay(mask, this->device);
2804 this->device->endclip(this->device);
2807 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2808 if(width<=0 || height<=0)
2811 gfxdevice_t belowrender;
2812 gfxdevice_render_init(&belowrender);
2813 if(states[statepos+1].isolated) {
2814 belowrender.setparameter(&belowrender, "fillwhite", "1");
2816 belowrender.setparameter(&belowrender, "antialize", "2");
2817 belowrender.startpage(&belowrender, width, height);
2818 gfxresult_record_replay(below, &belowrender);
2819 belowrender.endpage(&belowrender);
2820 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2821 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2822 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2824 gfxdevice_t maskrender;
2825 gfxdevice_render_init(&maskrender);
2826 maskrender.startpage(&maskrender, width, height);
2827 gfxresult_record_replay(mask, &maskrender);
2828 maskrender.endpage(&maskrender);
2829 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2830 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2832 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2833 msg("<fatal> Internal error in mask drawing");
2838 for(y=0;y<height;y++) {
2839 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2840 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2841 for(x=0;x<width;x++) {
2843 if(states[statepos].softmask_alpha) {
2846 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2849 l2->a = div255(alpha*l2->a);
2851 /* DON'T premultiply alpha- this is done by fillbitmap,
2852 depending on the output device */
2853 //l2->r = div255(alpha*l2->r);
2854 //l2->g = div255(alpha*l2->g);
2855 //l2->b = div255(alpha*l2->b);
2861 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2864 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2865 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2867 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2869 mask->destroy(mask);
2870 below->destroy(below);
2871 maskresult->destroy(maskresult);
2872 belowresult->destroy(belowresult);
2873 states[statepos].softmaskrecording = 0;
2878 // public: ~MemCheck()
2880 // delete globalParams;globalParams=0;
2881 // Object::memCheck(stderr);
2882 // gMemReport(stderr);