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);
1010 GBool GFXOutputDev::useDrawForm()
1012 infofeature("forms");
1015 void GFXOutputDev::drawForm(Ref id)
1017 msg("<error> drawForm not implemented");
1019 GBool GFXOutputDev::needNonText()
1023 void GFXOutputDev::endPage()
1025 msg("<verbose> endPage (GfxOutputDev)");
1026 if(outer_clip_box) {
1027 device->endclip(device);
1030 this->dashPattern = 0;
1031 /* notice: we're not fully done yet with this page- there might still be
1032 a few calls to drawLink() yet to come */
1035 static inline double sqr(double x) {return x*x;}
1037 #define STROKE_FILL 1
1038 #define STROKE_CLIP 2
1039 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1041 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1042 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1043 double miterLimit = state->getMiterLimit();
1044 double width = state->getTransformedLineWidth();
1047 double opaq = state->getStrokeOpacity();
1049 state->getFillRGB(&rgb);
1051 state->getStrokeRGB(&rgb);
1053 col.r = colToByte(rgb.r);
1054 col.g = colToByte(rgb.g);
1055 col.b = colToByte(rgb.b);
1056 col.a = (unsigned char)(opaq*255);
1058 gfx_capType capType = gfx_capRound;
1059 if(lineCap == 0) capType = gfx_capButt;
1060 else if(lineCap == 1) capType = gfx_capRound;
1061 else if(lineCap == 2) capType = gfx_capSquare;
1063 gfx_joinType joinType = gfx_joinRound;
1064 if(lineJoin == 0) joinType = gfx_joinMiter;
1065 else if(lineJoin == 1) joinType = gfx_joinRound;
1066 else if(lineJoin == 2) joinType = gfx_joinBevel;
1068 gfxline_t*line2 = 0;
1070 if(this->dashLength && this->dashPattern) {
1071 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1074 /* try to find out how much the transformation matrix would
1075 stretch the dashes, and factor that into the dash lengths.
1076 This is not the entirely correct approach- it would be
1077 better to first convert the path to an unscaled version,
1078 then apply dashing, and then transform the path using
1079 the current transformation matrix. However there are few
1080 PDFs which actually stretch a dashed path in a non-orthonormal
1082 double tx1, ty1, tx2, ty2;
1083 this->transformXY(state, 0, 0, &tx1, &ty1);
1084 this->transformXY(state, 1, 1, &tx2, &ty2);
1085 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1087 msg("<trace> %d dashes", this->dashLength);
1088 msg("<trace> | phase: %f", this->dashStart);
1089 for(t=0;t<this->dashLength;t++) {
1090 dash[t] = (float)this->dashPattern[t] * f;
1091 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1093 dash[this->dashLength] = -1;
1094 if(getLogLevel() >= LOGLEVEL_TRACE) {
1098 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1101 msg("<trace> After dashing:");
1104 if(getLogLevel() >= LOGLEVEL_TRACE) {
1105 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1107 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1108 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1110 col.r,col.g,col.b,col.a
1115 if(flags&STROKE_FILL) {
1116 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1117 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1118 if(getLogLevel() >= LOGLEVEL_TRACE) {
1119 dump_outline(gfxline);
1122 msg("<warning> Empty polygon (resulting from stroked line)");
1124 if(flags&STROKE_CLIP) {
1125 device->startclip(device, gfxline);
1126 states[statepos].clipping++;
1128 device->fill(device, gfxline, &col);
1130 gfxline_free(gfxline);
1133 if(flags&STROKE_CLIP)
1134 msg("<error> Stroke&clip not supported at the same time");
1135 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1139 gfxline_free(line2);
1142 gfxcolor_t getFillColor(GfxState * state)
1145 double opaq = state->getFillOpacity();
1146 state->getFillRGB(&rgb);
1148 col.r = colToByte(rgb.r);
1149 col.g = colToByte(rgb.g);
1150 col.b = colToByte(rgb.b);
1151 col.a = (unsigned char)(opaq*255);
1155 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1157 gfxcolor_t col = getFillColor(state);
1159 if(getLogLevel() >= LOGLEVEL_TRACE) {
1160 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1163 device->fill(device, line, &col);
1166 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1168 if(getLogLevel() >= LOGLEVEL_TRACE) {
1171 gfxbbox_t bbox = gfxline_getbbox(line);
1172 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1174 device->startclip(device, line);
1175 states[statepos].clipping++;
1178 void GFXOutputDev::clip(GfxState *state)
1180 GfxPath * path = state->getPath();
1181 msg("<trace> clip");
1182 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1183 if(config_optimize_polygons) {
1184 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1188 clipToGfxLine(state, line);
1192 void GFXOutputDev::eoClip(GfxState *state)
1194 GfxPath * path = state->getPath();
1195 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1196 clipToGfxLine(state, line);
1199 void GFXOutputDev::clipToStrokePath(GfxState *state)
1201 GfxPath * path = state->getPath();
1202 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1204 if(getLogLevel() >= LOGLEVEL_TRACE) {
1205 double width = state->getTransformedLineWidth();
1206 msg("<trace> cliptostrokepath width=%f", width);
1210 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1214 void GFXOutputDev::finish()
1216 if(outer_clip_box) {
1218 device->endclip(device);
1224 GFXOutputDev::~GFXOutputDev()
1229 free(this->pages); this->pages = 0;
1232 feature_t*f = this->featurewarnings;
1234 feature_t*next = f->next;
1236 free(f->string);f->string =0;
1242 this->featurewarnings = 0;
1244 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1246 GBool GFXOutputDev::upsideDown()
1250 GBool GFXOutputDev::useDrawChar()
1255 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1256 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1258 static char tmp_printstr[4096];
1259 char* makeStringPrintable(char*str)
1261 int len = strlen(str);
1268 for(t=0;t<len;t++) {
1273 tmp_printstr[t] = c;
1276 tmp_printstr[len++] = '.';
1277 tmp_printstr[len++] = '.';
1278 tmp_printstr[len++] = '.';
1280 tmp_printstr[len] = 0;
1281 return tmp_printstr;
1283 #define INTERNAL_FONT_SIZE 1024.0
1284 void GFXOutputDev::updateFontMatrix(GfxState*state)
1286 double* ctm = state->getCTM();
1287 double fontSize = state->getFontSize();
1288 double*textMat = state->getTextMat();
1290 /* taking the absolute value of horizScaling seems to be required for
1291 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1292 double hscale = fabs(state->getHorizScaling());
1294 // from xpdf-3.02/SplashOutputDev:updateFont
1295 double mm11 = textMat[0] * fontSize * hscale;
1296 double mm12 = textMat[1] * fontSize * hscale;
1297 double mm21 = textMat[2] * fontSize;
1298 double mm22 = textMat[3] * fontSize;
1300 // multiply with ctm, like state->getFontTransMat() does
1301 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1302 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1303 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1304 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1305 this->current_font_matrix.tx = 0;
1306 this->current_font_matrix.ty = 0;
1309 void GFXOutputDev::beginString(GfxState *state, GString *s)
1311 int render = state->getRender();
1312 if(current_text_stroke) {
1313 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1316 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1319 static gfxline_t* mkEmptyGfxShape(double x, double y)
1321 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1322 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1326 static char isValidUnicode(int c)
1328 if(c>=32 && c<0x2fffe)
1333 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1334 double dx, double dy,
1335 double originX, double originY,
1336 CharCode charid, int nBytes, Unicode *_u, int uLen)
1338 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1339 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1343 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1345 int render = state->getRender();
1346 gfxcolor_t col = getFillColor(state);
1348 // check for invisible text -- this is used by Acrobat Capture
1349 if (render == RENDER_INVISIBLE) {
1351 if(!config_extrafontdata)
1355 GfxFont*font = state->getFont();
1357 if(font->getType() == fontType3) {
1358 /* type 3 chars are passed as graphics */
1359 msg("<debug> type3 char at %f/%f", x, y);
1363 Unicode u = uLen?(_u[0]):0;
1364 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);
1366 gfxmatrix_t m = this->current_font_matrix;
1367 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1368 m.tx += originX; m.ty += originY;
1370 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1371 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1373 msg("<debug> Drawing glyph %d as shape", charid);
1375 msg("<notice> Some texts will be rendered as shape");
1378 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1379 gfxline_t*tglyph = gfxline_clone(glyph);
1380 gfxline_transform(tglyph, &m);
1381 if((render&3) != RENDER_INVISIBLE) {
1382 gfxline_t*add = gfxline_clone(tglyph);
1383 current_text_stroke = gfxline_append(current_text_stroke, add);
1385 if(render&RENDER_CLIP) {
1386 gfxline_t*add = gfxline_clone(tglyph);
1387 current_text_clip = gfxline_append(current_text_clip, add);
1388 if(!current_text_clip) {
1389 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1392 gfxline_free(tglyph);
1396 void GFXOutputDev::endString(GfxState *state)
1398 int render = state->getRender();
1399 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1401 if(current_text_stroke) {
1402 /* fillstroke and stroke text rendering objects we can process right
1403 now (as there may be texts of other rendering modes in this
1404 text object)- clipping objects have to wait until endTextObject,
1406 device->setparameter(device, "mark","TXT");
1407 if((render&3) == RENDER_FILL) {
1408 fillGfxLine(state, current_text_stroke);
1409 gfxline_free(current_text_stroke);
1410 current_text_stroke = 0;
1411 } else if((render&3) == RENDER_FILLSTROKE) {
1412 fillGfxLine(state, current_text_stroke);
1413 strokeGfxline(state, current_text_stroke,0);
1414 gfxline_free(current_text_stroke);
1415 current_text_stroke = 0;
1416 } else if((render&3) == RENDER_STROKE) {
1417 strokeGfxline(state, current_text_stroke,0);
1418 gfxline_free(current_text_stroke);
1419 current_text_stroke = 0;
1421 device->setparameter(device, "mark","");
1425 void GFXOutputDev::endTextObject(GfxState *state)
1427 int render = state->getRender();
1428 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1430 if(current_text_clip) {
1431 device->setparameter(device, "mark","TXT");
1432 clipToGfxLine(state, current_text_clip);
1433 device->setparameter(device, "mark","");
1434 gfxline_free(current_text_clip);
1435 current_text_clip = 0;
1439 /* the logic seems to be as following:
1440 first, beginType3Char is called, with the charcode and the coordinates.
1441 if this function returns true, it already knew about the char and has now drawn it.
1442 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1443 called with some parameters.
1444 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1445 at the position first passed to beginType3Char). the char ends with endType3Char.
1447 The drawing operations between beginType3Char and endType3Char are somewhat different to
1448 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1449 color determines the color of a font)
1452 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1454 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1457 if(config_extrafontdata && current_fontinfo) {
1459 gfxmatrix_t m = this->current_font_matrix;
1460 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1461 m.m00*=INTERNAL_FONT_SIZE;
1462 m.m01*=INTERNAL_FONT_SIZE;
1463 m.m10*=INTERNAL_FONT_SIZE;
1464 m.m11*=INTERNAL_FONT_SIZE;
1466 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1467 msg("<error> Invalid charid %d for font", charid);
1470 gfxcolor_t col={0,0,0,0};
1471 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1472 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1476 /* the character itself is going to be passed using the draw functions */
1477 return gFalse; /* gTrue= is_in_cache? */
1480 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1482 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1485 void GFXOutputDev::endType3Char(GfxState *state)
1488 msg("<debug> endType3Char");
1491 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1493 this->currentpage = pageNum;
1495 int rot = doc->getPageRotate(1);
1496 gfxcolor_t white = {255,255,255,255};
1497 gfxcolor_t black = {255,0,0,0};
1499 gfxline_t clippath[5];
1501 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1502 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1503 Use CropBox, not MediaBox, as page size
1510 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1511 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1513 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1514 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1516 this->clipmovex = -(int)x1;
1517 this->clipmovey = -(int)y1;
1519 /* apply user clip box */
1520 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1521 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1522 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1523 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1524 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1525 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1528 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1530 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);
1532 msg("<verbose> page is rotated %d degrees", rot);
1534 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1535 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1536 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1537 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1538 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1539 device->startclip(device, clippath); outer_clip_box = 1;
1540 if(!config_transparent) {
1541 device->fill(device, clippath, &white);
1543 states[statepos].clipbbox.xmin = x1;
1544 states[statepos].clipbbox.ymin = x1;
1545 states[statepos].clipbbox.xmax = x2;
1546 states[statepos].clipbbox.ymax = y2;
1548 this->dashPattern = 0;
1549 this->dashLength = 0;
1550 this->dashStart = 0;
1554 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1556 double x1, y1, x2, y2;
1557 gfxline_t points[5];
1560 msg("<debug> drawlink");
1562 link->getRect(&x1, &y1, &x2, &y2);
1563 cvtUserToDev(x1, y1, &x, &y);
1564 points[0].type = gfx_moveTo;
1565 points[0].x = points[4].x = x + user_movex + clipmovex;
1566 points[0].y = points[4].y = y + user_movey + clipmovey;
1567 points[0].next = &points[1];
1568 cvtUserToDev(x2, y1, &x, &y);
1569 points[1].type = gfx_lineTo;
1570 points[1].x = x + user_movex + clipmovex;
1571 points[1].y = y + user_movey + clipmovey;
1572 points[1].next = &points[2];
1573 cvtUserToDev(x2, y2, &x, &y);
1574 points[2].type = gfx_lineTo;
1575 points[2].x = x + user_movex + clipmovex;
1576 points[2].y = y + user_movey + clipmovey;
1577 points[2].next = &points[3];
1578 cvtUserToDev(x1, y2, &x, &y);
1579 points[3].type = gfx_lineTo;
1580 points[3].x = x + user_movex + clipmovex;
1581 points[3].y = y + user_movey + clipmovey;
1582 points[3].next = &points[4];
1583 cvtUserToDev(x1, y1, &x, &y);
1584 points[4].type = gfx_lineTo;
1585 points[4].x = x + user_movex + clipmovex;
1586 points[4].y = y + user_movey + clipmovey;
1589 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1590 points[0].x, points[0].y,
1591 points[1].x, points[1].y,
1592 points[2].x, points[2].y,
1593 points[3].x, points[3].y);
1595 if(getLogLevel() >= LOGLEVEL_TRACE) {
1596 dump_outline(points);
1599 LinkAction*action=link->getAction();
1602 const char*type = "-?-";
1605 msg("<trace> drawlink action=%d", action->getKind());
1606 switch(action->getKind())
1610 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1611 LinkDest *dest=NULL;
1612 if (ha->getDest()==NULL)
1613 dest=catalog->findDest(ha->getNamedDest());
1614 else dest=ha->getDest();
1616 if (dest->isPageRef()){
1617 Ref pageref=dest->getPageRef();
1618 page=catalog->findPage(pageref.num,pageref.gen);
1620 else page=dest->getPageNum();
1621 sprintf(buf, "%d", page);
1628 LinkGoToR*l = (LinkGoToR*)action;
1629 GString*g = l->getFileName();
1631 s = strdup(g->getCString());
1633 /* if the GoToR link has no filename, then
1634 try to find a refernce in the *local*
1636 GString*g = l->getNamedDest();
1638 s = strdup(g->getCString());
1644 LinkNamed*l = (LinkNamed*)action;
1645 GString*name = l->getName();
1647 s = strdup(name->lowerCase()->getCString());
1648 named = name->getCString();
1651 if(strstr(s, "next") || strstr(s, "forward"))
1653 page = currentpage + 1;
1655 else if(strstr(s, "prev") || strstr(s, "back"))
1657 page = currentpage - 1;
1659 else if(strstr(s, "last") || strstr(s, "end"))
1661 if(pages && pagepos>0)
1662 page = pages[pagepos-1];
1664 else if(strstr(s, "first") || strstr(s, "top"))
1672 case actionLaunch: {
1674 LinkLaunch*l = (LinkLaunch*)action;
1675 GString * str = new GString(l->getFileName());
1676 GString * params = l->getParams();
1678 str->append(params);
1679 s = strdup(str->getCString());
1686 LinkURI*l = (LinkURI*)action;
1687 GString*g = l->getURI();
1689 url = g->getCString();
1694 case actionUnknown: {
1696 LinkUnknown*l = (LinkUnknown*)action;
1701 msg("<error> Unknown link type!");
1706 if(!s) s = strdup("-?-");
1708 msg("<trace> drawlink s=%s", s);
1710 if(!linkinfo && (page || s))
1712 msg("<notice> File contains links");
1720 for(t=1;t<=pagepos;t++) {
1721 if(pages[t]==page) {
1730 sprintf(buf, "page%d", lpage);
1731 device->drawlink(device, points, buf);
1735 device->drawlink(device, points, s);
1738 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1742 void GFXOutputDev::saveState(GfxState *state) {
1743 dbg("saveState %08x", state); dbgindent+=2;
1745 msg("<trace> saveState %08x", state);
1748 msg("<fatal> Too many nested states in pdf.");
1752 states[statepos].state = state;
1753 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1754 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1755 states[statepos].clipping = 0;
1756 states[statepos].olddevice = 0;
1757 states[statepos].clipbbox = states[statepos-1].clipbbox;
1760 void GFXOutputDev::restoreState(GfxState *state) {
1761 dbgindent-=2; dbg("restoreState %08x", state);
1764 msg("<fatal> Invalid restoreState");
1767 msg("<trace> restoreState %08x%s%s", state,
1768 states[statepos].softmask?" (end softmask)":"",
1769 states[statepos].clipping?" (end clipping)":"");
1770 if(states[statepos].softmask) {
1771 clearSoftMask(state);
1775 while(states[statepos].clipping) {
1776 device->endclip(device);
1777 states[statepos].clipping--;
1779 if(states[statepos].state!=state) {
1780 msg("<fatal> bad state nesting");
1783 states[statepos].state=0;
1787 void GFXOutputDev::updateLineDash(GfxState *state)
1789 state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1790 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1791 if(!this->dashLength) {
1792 this->dashPattern = 0;
1796 void GFXOutputDev::updateLineWidth(GfxState *state)
1798 double width = state->getTransformedLineWidth();
1801 void GFXOutputDev::updateLineCap(GfxState *state)
1803 int c = state->getLineCap();
1806 void GFXOutputDev::updateLineJoin(GfxState *state)
1808 int j = state->getLineJoin();
1811 void GFXOutputDev::updateFillColor(GfxState *state)
1814 double opaq = state->getFillOpacity();
1815 state->getFillRGB(&rgb);
1817 void GFXOutputDev::updateFillOpacity(GfxState *state)
1820 double opaq = state->getFillOpacity();
1821 state->getFillRGB(&rgb);
1822 dbg("update fillopaq %f", opaq);
1824 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1826 double opaq = state->getFillOpacity();
1827 dbg("update strokeopaq %f", opaq);
1829 void GFXOutputDev::updateFillOverprint(GfxState *state)
1831 double opaq = state->getFillOverprint();
1832 dbg("update filloverprint %f", opaq);
1834 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1836 double opaq = state->getStrokeOverprint();
1837 dbg("update strokeoverprint %f", opaq);
1839 void GFXOutputDev::updateTransfer(GfxState *state)
1841 dbg("update transfer");
1845 void GFXOutputDev::updateStrokeColor(GfxState *state)
1848 double opaq = state->getStrokeOpacity();
1849 state->getStrokeRGB(&rgb);
1853 gfxfont_t* GFXOutputDev::createGfxFont(GfxFont*xpdffont, FontInfo*src)
1855 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1856 memset(font, 0, sizeof(gfxfont_t));
1858 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1859 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1863 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1865 //printf("%d glyphs\n", font->num_glyphs);
1866 font->num_glyphs = 0;
1867 font->ascent = fabs(src->descender);
1868 font->descent = fabs(src->ascender);
1870 for(t=0;t<src->num_glyphs;t++) {
1871 if(src->glyphs[t]) {
1872 SplashPath*path = src->glyphs[t]->path;
1873 int len = path?path->getLength():0;
1874 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1875 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1876 src->glyphs[t]->glyphid = font->num_glyphs;
1877 glyph->unicode = src->glyphs[t]->unicode;
1878 if(glyph->unicode >= font->max_unicode)
1879 font->max_unicode = glyph->unicode+1;
1881 gfxdrawer_target_gfxline(&drawer);
1885 for(s=0;s<len;s++) {
1888 path->getPoint(s, &x, &y, &f);
1891 if(f&splashPathFirst) {
1892 drawer.moveTo(&drawer, x*scale, y*scale);
1894 if(f&splashPathCurve) {
1896 path->getPoint(++s, &x2, &y2, &f);
1897 if(f&splashPathCurve) {
1899 path->getPoint(++s, &x3, &y3, &f);
1900 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1902 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1905 drawer.lineTo(&drawer, x*scale, y*scale);
1907 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1908 // (f&splashPathFirst)?"first":"",
1909 // (f&splashPathLast)?"last":"");
1912 glyph->line = (gfxline_t*)drawer.result(&drawer);
1913 if(src->glyphs[t]->advance>0) {
1914 glyph->advance = src->glyphs[t]->advance;
1916 msg("<warning> Approximating advance value for glyph %d", t);
1917 glyph->advance = xmax*scale;
1919 if(this->config_bigchar) {
1920 double max = src->glyphs[t]->advance_max;
1921 if(max>0 && max > glyph->advance) {
1922 glyph->advance = max;
1929 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1930 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1931 for(t=0;t<font->num_glyphs;t++) {
1932 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1933 font->unicode2glyph[font->glyphs[t].unicode] = t;
1937 msg("<trace> %d glyphs.", t, font->num_glyphs);
1941 void GFXOutputDev::updateFont(GfxState *state)
1943 GfxFont* gfxFont = state->getFont();
1947 char*id = getFontID(gfxFont);
1948 msg("<verbose> Updating font to %s", id);
1949 if(gfxFont->getType() == fontType3) {
1950 infofeature("Type3 fonts");
1951 if(!config_extrafontdata) {
1956 msg("<error> Internal Error: FontID is null");
1960 this->current_fontinfo = this->info->getFont(id);
1961 if(!this->current_fontinfo) {
1962 msg("<error> Internal Error: no fontinfo for font %s", id);
1965 if(!this->current_fontinfo->seen) {
1966 dumpFontInfo("<verbose>", gfxFont);
1969 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1971 font = this->createGfxFont(gfxFont, current_fontinfo);
1972 font->id = strdup(id);
1973 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1975 device->addfont(device, font);
1977 current_gfxfont = font;
1980 updateFontMatrix(state);
1983 #define SQR(x) ((x)*(x))
1985 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1987 if((newwidth<1 || newheight<1) ||
1988 (width<=newwidth || height<=newheight))
1990 unsigned char*newdata;
1992 newdata= (unsigned char*)malloc(newwidth*newheight);
1993 double fx = ((double)width)/newwidth;
1994 double fy = ((double)height)/newheight;
1996 int blocksize = (int)(8192/(fx*fy));
1997 int r = 8192*256/palettesize;
1998 for(x=0;x<newwidth;x++) {
1999 double ex = px + fx;
2000 int fromx = (int)px;
2002 int xweight1 = (int)((1-(px-fromx))*256);
2003 int xweight2 = (int)((ex-tox)*256);
2005 for(y=0;y<newheight;y++) {
2006 double ey = py + fy;
2007 int fromy = (int)py;
2009 int yweight1 = (int)((1-(py-fromy))*256);
2010 int yweight2 = (int)((ey-toy)*256);
2017 for(xx=fromx;xx<=tox;xx++)
2018 for(yy=fromy;yy<=toy;yy++) {
2019 int b = 1-data[width*yy+xx];
2021 if(xx==fromx) weight = (weight*xweight1)/256;
2022 if(xx==tox) weight = (weight*xweight2)/256;
2023 if(yy==fromy) weight = (weight*yweight1)/256;
2024 if(yy==toy) weight = (weight*yweight2)/256;
2027 //if(a) a=(palettesize-1)*r/blocksize;
2028 newdata[y*newwidth+x] = (a*blocksize)/r;
2036 #define IMAGE_TYPE_JPEG 0
2037 #define IMAGE_TYPE_LOSSLESS 1
2039 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2040 double x1,double y1,
2041 double x2,double y2,
2042 double x3,double y3,
2043 double x4,double y4, int type, int multiply)
2045 gfxcolor_t*newpic=0;
2047 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2048 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2050 gfxline_t p1,p2,p3,p4,p5;
2051 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2052 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2053 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2054 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2055 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2057 {p1.x = (int)(p1.x*20)/20.0;
2058 p1.y = (int)(p1.y*20)/20.0;
2059 p2.x = (int)(p2.x*20)/20.0;
2060 p2.y = (int)(p2.y*20)/20.0;
2061 p3.x = (int)(p3.x*20)/20.0;
2062 p3.y = (int)(p3.y*20)/20.0;
2063 p4.x = (int)(p4.x*20)/20.0;
2064 p4.y = (int)(p4.y*20)/20.0;
2065 p5.x = (int)(p5.x*20)/20.0;
2066 p5.y = (int)(p5.y*20)/20.0;
2070 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2071 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2073 m.tx = p1.x - 0.5*multiply;
2074 m.ty = p1.y - 0.5*multiply;
2077 img.data = (gfxcolor_t*)data;
2081 if(type == IMAGE_TYPE_JPEG)
2082 /* TODO: pass image_dpi to device instead */
2083 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2086 dev->fillbitmap(dev, &p1, &img, &m, 0);
2089 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2090 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2092 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2095 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2096 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2098 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2102 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2103 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2104 GBool inlineImg, int mask, int*maskColors,
2105 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2107 /* the code in this function is *old*. It's not pretty, but it works. */
2109 double x1,y1,x2,y2,x3,y3,x4,y4;
2110 ImageStream *imgStr;
2115 unsigned char* maskbitmap = 0;
2118 ncomps = colorMap->getNumPixelComps();
2119 bits = colorMap->getBits();
2124 unsigned char buf[8];
2125 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2127 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2128 imgMaskStr->reset();
2129 unsigned char pal[256];
2130 int n = 1 << colorMap->getBits();
2135 maskColorMap->getGray(pixBuf, &gray);
2136 pal[t] = colToByte(gray);
2138 for (y = 0; y < maskHeight; y++) {
2139 for (x = 0; x < maskWidth; x++) {
2140 imgMaskStr->getPixel(buf);
2141 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2146 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2147 imgMaskStr->reset();
2148 for (y = 0; y < maskHeight; y++) {
2149 for (x = 0; x < maskWidth; x++) {
2150 imgMaskStr->getPixel(buf);
2152 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2160 imgStr = new ImageStream(str, width, ncomps,bits);
2163 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2165 msg("<verbose> Ignoring %d by %d image", width, height);
2166 unsigned char buf[8];
2168 for (y = 0; y < height; ++y)
2169 for (x = 0; x < width; ++x) {
2170 imgStr->getPixel(buf);
2178 this->transformXY(state, 0, 1, &x1, &y1);
2179 this->transformXY(state, 0, 0, &x2, &y2);
2180 this->transformXY(state, 1, 0, &x3, &y3);
2181 this->transformXY(state, 1, 1, &x4, &y4);
2184 /* as type 3 bitmaps are antialized, we need to place them
2185 at integer coordinates, otherwise flash player's antializing
2186 will kick in and make everything blurry */
2187 x1 = (int)(x1);y1 = (int)(y1);
2188 x2 = (int)(x2);y2 = (int)(y2);
2189 x3 = (int)(x3);y3 = (int)(y3);
2190 x4 = (int)(x4);y4 = (int)(y4);
2193 if(!pbminfo && !(str->getKind()==strDCT)) {
2195 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2199 msg("<verbose> drawing %d by %d masked picture", width, height);
2201 if(!jpeginfo && (str->getKind()==strDCT)) {
2202 msg("<notice> File contains jpeg pictures");
2207 unsigned char buf[8];
2209 unsigned char*pic = new unsigned char[width*height];
2210 gfxcolor_t pal[256];
2212 state->getFillRGB(&rgb);
2214 memset(pal,255,sizeof(pal));
2215 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2216 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2217 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2218 pal[0].a = 255; pal[1].a = 0;
2221 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2222 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2223 for (y = 0; y < height; ++y)
2224 for (x = 0; x < width; ++x)
2226 imgStr->getPixel(buf);
2229 pic[width*y+x] = buf[0];
2233 unsigned char*pic2 = 0;
2236 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2245 height = realheight;
2249 /* make a black/white palette */
2251 float r = 255./(float)(numpalette-1);
2253 for(t=0;t<numpalette;t++) {
2254 pal[t].r = colToByte(rgb.r);
2255 pal[t].g = colToByte(rgb.g);
2256 pal[t].b = colToByte(rgb.b);
2257 pal[t].a = (unsigned char)(t*r);
2262 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2263 for (y = 0; y < height; ++y) {
2264 for (x = 0; x < width; ++x) {
2265 pic2[width*y+x] = pal[pic[y*width+x]];
2268 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2272 if(maskbitmap) free(maskbitmap);
2278 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2279 gfxcolor_t*pic=new gfxcolor_t[width*height];
2280 for (y = 0; y < height; ++y) {
2281 for (x = 0; x < width; ++x) {
2282 imgStr->getPixel(pixBuf);
2283 colorMap->getRGB(pixBuf, &rgb);
2284 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2285 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2286 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2287 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2289 int x1 = x*maskWidth/width;
2290 int y1 = y*maskHeight/height;
2291 int x2 = (x+1)*maskWidth/width;
2292 int y2 = (y+1)*maskHeight/height;
2294 unsigned int alpha=0;
2295 unsigned int count=0;
2296 for(xx=x1;xx<x2;xx++)
2297 for(yy=y1;yy<y2;yy++) {
2298 alpha += maskbitmap[yy*maskWidth+xx];
2302 pic[width*y+x].a = alpha / count;
2304 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2309 if(str->getKind()==strDCT)
2310 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2312 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2315 if(maskbitmap) free(maskbitmap);
2318 gfxcolor_t*pic=new gfxcolor_t[width*height];
2319 gfxcolor_t pal[256];
2320 int n = 1 << colorMap->getBits();
2322 for(t=0;t<256;t++) {
2324 colorMap->getRGB(pixBuf, &rgb);
2326 {/*if(maskColors && *maskColors==t) {
2327 msg("<notice> Color %d is transparent", t);
2328 if (imgData->maskColors) {
2330 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2331 if (pix[i] < imgData->maskColors[2*i] ||
2332 pix[i] > imgData->maskColors[2*i+1]) {
2347 pal[t].r = (unsigned char)(colToByte(rgb.r));
2348 pal[t].g = (unsigned char)(colToByte(rgb.g));
2349 pal[t].b = (unsigned char)(colToByte(rgb.b));
2350 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2353 for (y = 0; y < height; ++y) {
2354 for (x = 0; x < width; ++x) {
2355 imgStr->getPixel(pixBuf);
2356 pic[width*y+x] = pal[pixBuf[0]];
2360 if(maskWidth < width && maskHeight < height) {
2361 for(y = 0; y < height; y++) {
2362 for (x = 0; x < width; x++) {
2363 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2367 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2368 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2369 double dx = width / maskWidth;
2370 double dy = height / maskHeight;
2372 for(y = 0; y < maskHeight; y++) {
2374 for (x = 0; x < maskWidth; x++) {
2375 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2376 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2384 height = maskHeight;
2387 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2391 if(maskbitmap) free(maskbitmap);
2396 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2397 int width, int height, GBool invert,
2400 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2401 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2402 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2405 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2406 int width, int height, GfxImageColorMap *colorMap,
2407 int *maskColors, GBool inlineImg)
2409 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2410 colorMap?"colorMap":"no colorMap",
2411 maskColors?"maskColors":"no maskColors",
2413 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2414 colorMap?"colorMap":"no colorMap",
2415 maskColors?"maskColors":"no maskColors",
2418 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2419 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2420 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2423 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2424 int width, int height,
2425 GfxImageColorMap *colorMap,
2426 Stream *maskStr, int maskWidth, int maskHeight,
2429 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2430 colorMap?"colorMap":"no colorMap",
2431 maskWidth, maskHeight);
2432 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2433 colorMap?"colorMap":"no colorMap",
2434 maskWidth, maskHeight);
2436 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2437 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2438 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2441 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2442 int width, int height,
2443 GfxImageColorMap *colorMap,
2445 int maskWidth, int maskHeight,
2446 GfxImageColorMap *maskColorMap)
2448 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2449 colorMap?"colorMap":"no colorMap",
2450 maskWidth, maskHeight);
2451 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2452 colorMap?"colorMap":"no colorMap",
2453 maskWidth, maskHeight);
2455 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2456 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2457 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2460 void GFXOutputDev::stroke(GfxState *state)
2464 GfxPath * path = state->getPath();
2465 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2466 strokeGfxline(state, line, 0);
2470 void GFXOutputDev::fill(GfxState *state)
2472 gfxcolor_t col = getFillColor(state);
2473 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2475 GfxPath * path = state->getPath();
2476 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2477 if(config_optimize_polygons) {
2478 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2482 fillGfxLine(state, line);
2486 void GFXOutputDev::eoFill(GfxState *state)
2488 gfxcolor_t col = getFillColor(state);
2489 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2491 GfxPath * path = state->getPath();
2492 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2493 fillGfxLine(state, line);
2498 static const char* dirseparator()
2507 void addGlobalFont(const char*filename)
2509 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2510 memset(f, 0, sizeof(fontfile_t));
2511 f->filename = filename;
2512 int len = strlen(filename);
2513 char*r1 = strrchr(filename, '/');
2514 char*r2 = strrchr(filename, '\\');
2522 msg("<notice> Adding font \"%s\".", filename);
2523 if(global_fonts_next) {
2524 global_fonts_next->next = f;
2525 global_fonts_next = global_fonts_next->next;
2527 global_fonts_next = global_fonts = f;
2531 void addGlobalLanguageDir(const char*dir)
2533 msg("<notice> Adding %s to language pack directories", dir);
2536 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2537 strcpy(config_file, dir);
2538 strcat(config_file, dirseparator());
2539 strcat(config_file, "add-to-xpdfrc");
2541 fi = fopen(config_file, "rb");
2543 msg("<error> Could not open %s", config_file);
2546 globalParams->parseFile(new GString(config_file), fi);
2550 void addGlobalFontDir(const char*dirname)
2552 #ifdef HAVE_DIRENT_H
2553 msg("<notice> Adding %s to font directories", dirname);
2554 lastfontdir = strdup(dirname);
2555 DIR*dir = opendir(dirname);
2557 msg("<warning> Couldn't open directory %s", dirname);
2562 ent = readdir (dir);
2566 char*name = ent->d_name;
2572 if(!strncasecmp(&name[l-4], ".pfa", 4))
2574 if(!strncasecmp(&name[l-4], ".pfb", 4))
2576 if(!strncasecmp(&name[l-4], ".ttf", 4))
2579 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2580 strcpy(fontname, dirname);
2581 strcat(fontname, dirseparator());
2582 strcat(fontname, name);
2583 addGlobalFont(fontname);
2588 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2592 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2598 this->pagebuflen = 1024;
2599 if(pdfpage > this->pagebuflen)
2600 this->pagebuflen = pdfpage+1;
2601 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2602 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2605 while(pdfpage >= this->pagebuflen)
2607 int oldlen = this->pagebuflen;
2608 this->pagebuflen+=1024;
2609 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2610 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2613 this->pages[pdfpage] = outputpage;
2614 if(pdfpage>this->pagepos)
2615 this->pagepos = pdfpage;
2618 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2619 GfxColorSpace *blendingColorSpace,
2620 GBool isolated, GBool knockout,
2623 const char*colormodename = "";
2625 if(blendingColorSpace) {
2626 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2628 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);
2629 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);
2631 //states[statepos].createsoftmask |= forSoftMask;
2632 states[statepos].createsoftmask = forSoftMask;
2633 states[statepos].transparencygroup = !forSoftMask;
2634 states[statepos].isolated = isolated;
2636 states[statepos].olddevice = this->device;
2637 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2638 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2640 gfxdevice_record_init(this->device);
2642 /*if(!forSoftMask) { ////???
2643 state->setFillOpacity(0.0);
2648 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2651 gfxdevice_t*r = this->device;
2653 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2655 this->device = states[statepos].olddevice;
2657 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2658 /* if these errors occur more often, we should build a seperate
2659 transparency group stack, like xpdf/SplashOutputDev.cc does */
2660 restoreState(state);
2661 this->device = states[statepos].olddevice;
2663 states[statepos].olddevice = 0;
2665 gfxresult_t*recording = r->finish(r);
2667 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2668 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2670 if(states[statepos].createsoftmask) {
2671 states[statepos-1].softmaskrecording = recording;
2673 states[statepos-1].grouprecording = recording;
2676 states[statepos].createsoftmask = 0;
2677 states[statepos].transparencygroup = 0;
2681 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2683 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2684 "colordodge","colorburn","hardlight","softlight","difference",
2685 "exclusion","hue","saturation","color","luminosity"};
2687 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2688 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2690 if(state->getBlendMode() == gfxBlendNormal)
2691 infofeature("transparency groups");
2694 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2695 warnfeature(buffer, 0);
2698 gfxresult_t*grouprecording = states[statepos].grouprecording;
2700 int blendmode = state->getBlendMode();
2701 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2702 int alpha = (int)(state->getFillOpacity()*255);
2703 if(blendmode == gfxBlendMultiply && alpha>200)
2706 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2707 gfxdevice_ops_init(&ops, this->device, alpha);
2708 gfxresult_record_replay(grouprecording, &ops);
2711 grouprecording->destroy(grouprecording);
2713 states[statepos].grouprecording = 0;
2716 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2718 if(states[statepos].softmask) {
2719 /* shouldn't happen, but *does* happen */
2720 clearSoftMask(state);
2723 /* alpha = 1: retrieve mask values from alpha layer
2724 alpha = 0: retrieve mask values from luminance */
2726 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2727 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2728 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2729 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2731 infofeature("soft masks");
2733 warnfeature("soft masks from alpha channel",0);
2735 if(states[statepos].olddevice) {
2736 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2739 states[statepos].olddevice = this->device;
2740 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2741 gfxdevice_record_init(this->device);
2743 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2745 states[statepos].softmask = 1;
2746 states[statepos].softmask_alpha = alpha;
2749 static inline Guchar div255(int x) {
2750 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2753 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2755 if(c < min) c = min;
2756 if(c > max) c = max;
2760 void GFXOutputDev::clearSoftMask(GfxState *state)
2762 if(!states[statepos].softmask)
2764 states[statepos].softmask = 0;
2765 dbg("clearSoftMask statepos=%d", statepos);
2766 msg("<verbose> clearSoftMask statepos=%d", statepos);
2768 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2769 msg("<error> Error in softmask/tgroup ordering");
2773 gfxresult_t*mask = states[statepos].softmaskrecording;
2774 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2775 this->device = states[statepos].olddevice;
2777 /* get outline of all objects below the soft mask */
2778 gfxdevice_t uniondev;
2779 gfxdevice_union_init(&uniondev, 0);
2780 gfxresult_record_replay(below, &uniondev);
2781 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2782 uniondev.finish(&uniondev);
2783 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2784 gfxline_free(belowoutline);belowoutline=0;
2786 this->device->startclip(this->device, belowoutline);
2787 gfxresult_record_replay(below, this->device);
2788 gfxresult_record_replay(mask, this->device);
2789 this->device->endclip(this->device);
2792 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2793 if(width<=0 || height<=0)
2796 gfxdevice_t belowrender;
2797 gfxdevice_render_init(&belowrender);
2798 if(states[statepos+1].isolated) {
2799 belowrender.setparameter(&belowrender, "fillwhite", "1");
2801 belowrender.setparameter(&belowrender, "antialize", "2");
2802 belowrender.startpage(&belowrender, width, height);
2803 gfxresult_record_replay(below, &belowrender);
2804 belowrender.endpage(&belowrender);
2805 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2806 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2807 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2809 gfxdevice_t maskrender;
2810 gfxdevice_render_init(&maskrender);
2811 maskrender.startpage(&maskrender, width, height);
2812 gfxresult_record_replay(mask, &maskrender);
2813 maskrender.endpage(&maskrender);
2814 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2815 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2817 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2818 msg("<fatal> Internal error in mask drawing");
2823 for(y=0;y<height;y++) {
2824 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2825 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2826 for(x=0;x<width;x++) {
2828 if(states[statepos].softmask_alpha) {
2831 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2834 l2->a = div255(alpha*l2->a);
2836 /* DON'T premultiply alpha- this is done by fillbitmap,
2837 depending on the output device */
2838 //l2->r = div255(alpha*l2->r);
2839 //l2->g = div255(alpha*l2->g);
2840 //l2->b = div255(alpha*l2->b);
2846 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2849 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2850 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2852 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2854 mask->destroy(mask);
2855 below->destroy(below);
2856 maskresult->destroy(maskresult);
2857 belowresult->destroy(belowresult);
2858 states[statepos].softmaskrecording = 0;
2863 // public: ~MemCheck()
2865 // delete globalParams;globalParams=0;
2866 // Object::memCheck(stderr);
2867 // gMemReport(stderr);