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;
1093 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1095 dash[this->dashLength] = -1;
1096 if(getLogLevel() >= LOGLEVEL_TRACE) {
1100 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1103 msg("<trace> After dashing:");
1106 if(getLogLevel() >= LOGLEVEL_TRACE) {
1107 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1109 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1110 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1112 col.r,col.g,col.b,col.a
1117 if(flags&STROKE_FILL) {
1118 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1119 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1120 if(getLogLevel() >= LOGLEVEL_TRACE) {
1121 dump_outline(gfxline);
1124 msg("<warning> Empty polygon (resulting from stroked line)");
1126 if(flags&STROKE_CLIP) {
1127 device->startclip(device, gfxline);
1128 states[statepos].clipping++;
1130 device->fill(device, gfxline, &col);
1132 gfxline_free(gfxline);
1135 if(flags&STROKE_CLIP)
1136 msg("<error> Stroke&clip not supported at the same time");
1137 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1141 gfxline_free(line2);
1144 gfxcolor_t getFillColor(GfxState * state)
1147 double opaq = state->getFillOpacity();
1148 state->getFillRGB(&rgb);
1150 col.r = colToByte(rgb.r);
1151 col.g = colToByte(rgb.g);
1152 col.b = colToByte(rgb.b);
1153 col.a = (unsigned char)(opaq*255);
1157 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1159 gfxcolor_t col = getFillColor(state);
1161 if(getLogLevel() >= LOGLEVEL_TRACE) {
1162 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1165 device->fill(device, line, &col);
1168 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1170 if(getLogLevel() >= LOGLEVEL_TRACE) {
1173 gfxbbox_t bbox = gfxline_getbbox(line);
1174 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1176 device->startclip(device, line);
1177 states[statepos].clipping++;
1180 void GFXOutputDev::clip(GfxState *state)
1182 GfxPath * path = state->getPath();
1183 msg("<trace> clip");
1184 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1185 if(config_optimize_polygons) {
1186 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1190 clipToGfxLine(state, line);
1194 void GFXOutputDev::eoClip(GfxState *state)
1196 GfxPath * path = state->getPath();
1197 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1198 clipToGfxLine(state, line);
1201 void GFXOutputDev::clipToStrokePath(GfxState *state)
1203 GfxPath * path = state->getPath();
1204 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1206 if(getLogLevel() >= LOGLEVEL_TRACE) {
1207 double width = state->getTransformedLineWidth();
1208 msg("<trace> cliptostrokepath width=%f", width);
1212 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1216 void GFXOutputDev::finish()
1218 if(outer_clip_box) {
1220 device->endclip(device);
1226 GFXOutputDev::~GFXOutputDev()
1231 free(this->pages); this->pages = 0;
1233 if(this->dashPattern) {
1234 free(this->dashPattern);this->dashPattern = 0;
1237 feature_t*f = this->featurewarnings;
1239 feature_t*next = f->next;
1241 free(f->string);f->string =0;
1247 this->featurewarnings = 0;
1249 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1251 GBool GFXOutputDev::upsideDown()
1255 GBool GFXOutputDev::useDrawChar()
1260 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1261 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1263 static char tmp_printstr[4096];
1264 char* makeStringPrintable(char*str)
1266 int len = strlen(str);
1273 for(t=0;t<len;t++) {
1278 tmp_printstr[t] = c;
1281 tmp_printstr[len++] = '.';
1282 tmp_printstr[len++] = '.';
1283 tmp_printstr[len++] = '.';
1285 tmp_printstr[len] = 0;
1286 return tmp_printstr;
1288 #define INTERNAL_FONT_SIZE 1024.0
1289 void GFXOutputDev::updateFontMatrix(GfxState*state)
1291 double* ctm = state->getCTM();
1292 double fontSize = state->getFontSize();
1293 double*textMat = state->getTextMat();
1295 /* taking the absolute value of horizScaling seems to be required for
1296 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1297 double hscale = fabs(state->getHorizScaling());
1299 // from xpdf-3.02/SplashOutputDev:updateFont
1300 double mm11 = textMat[0] * fontSize * hscale;
1301 double mm12 = textMat[1] * fontSize * hscale;
1302 double mm21 = textMat[2] * fontSize;
1303 double mm22 = textMat[3] * fontSize;
1305 // multiply with ctm, like state->getFontTransMat() does
1306 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1307 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1308 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1309 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1310 this->current_font_matrix.tx = 0;
1311 this->current_font_matrix.ty = 0;
1314 void GFXOutputDev::beginString(GfxState *state, GString *s)
1316 int render = state->getRender();
1317 if(current_text_stroke) {
1318 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1321 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1324 static gfxline_t* mkEmptyGfxShape(double x, double y)
1326 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1327 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1331 static char isValidUnicode(int c)
1333 if(c>=32 && c<0x2fffe)
1338 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1339 double dx, double dy,
1340 double originX, double originY,
1341 CharCode charid, int nBytes, Unicode *_u, int uLen)
1343 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1344 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1348 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1350 int render = state->getRender();
1351 gfxcolor_t col = getFillColor(state);
1353 // check for invisible text -- this is used by Acrobat Capture
1354 if (render == RENDER_INVISIBLE) {
1356 if(!config_extrafontdata)
1360 GfxFont*font = state->getFont();
1362 if(font->getType() == fontType3) {
1363 /* type 3 chars are passed as graphics */
1364 msg("<debug> type3 char at %f/%f", x, y);
1368 Unicode u = uLen?(_u[0]):0;
1369 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);
1371 gfxmatrix_t m = this->current_font_matrix;
1372 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1373 m.tx += originX; m.ty += originY;
1375 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1376 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1378 msg("<debug> Drawing glyph %d as shape", charid);
1380 msg("<notice> Some texts will be rendered as shape");
1383 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1384 gfxline_t*tglyph = gfxline_clone(glyph);
1385 gfxline_transform(tglyph, &m);
1386 if((render&3) != RENDER_INVISIBLE) {
1387 gfxline_t*add = gfxline_clone(tglyph);
1388 current_text_stroke = gfxline_append(current_text_stroke, add);
1390 if(render&RENDER_CLIP) {
1391 gfxline_t*add = gfxline_clone(tglyph);
1392 current_text_clip = gfxline_append(current_text_clip, add);
1393 if(!current_text_clip) {
1394 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1397 gfxline_free(tglyph);
1401 void GFXOutputDev::endString(GfxState *state)
1403 int render = state->getRender();
1404 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1406 if(current_text_stroke) {
1407 /* fillstroke and stroke text rendering objects we can process right
1408 now (as there may be texts of other rendering modes in this
1409 text object)- clipping objects have to wait until endTextObject,
1411 device->setparameter(device, "mark","TXT");
1412 if((render&3) == RENDER_FILL) {
1413 fillGfxLine(state, current_text_stroke);
1414 gfxline_free(current_text_stroke);
1415 current_text_stroke = 0;
1416 } else if((render&3) == RENDER_FILLSTROKE) {
1417 fillGfxLine(state, current_text_stroke);
1418 strokeGfxline(state, current_text_stroke,0);
1419 gfxline_free(current_text_stroke);
1420 current_text_stroke = 0;
1421 } else if((render&3) == RENDER_STROKE) {
1422 strokeGfxline(state, current_text_stroke,0);
1423 gfxline_free(current_text_stroke);
1424 current_text_stroke = 0;
1426 device->setparameter(device, "mark","");
1430 void GFXOutputDev::endTextObject(GfxState *state)
1432 int render = state->getRender();
1433 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1435 if(current_text_clip) {
1436 device->setparameter(device, "mark","TXT");
1437 clipToGfxLine(state, current_text_clip);
1438 device->setparameter(device, "mark","");
1439 gfxline_free(current_text_clip);
1440 current_text_clip = 0;
1444 /* the logic seems to be as following:
1445 first, beginType3Char is called, with the charcode and the coordinates.
1446 if this function returns true, it already knew about the char and has now drawn it.
1447 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1448 called with some parameters.
1449 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1450 at the position first passed to beginType3Char). the char ends with endType3Char.
1452 The drawing operations between beginType3Char and endType3Char are somewhat different to
1453 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1454 color determines the color of a font)
1457 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1459 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1462 if(config_extrafontdata && current_fontinfo) {
1464 gfxmatrix_t m = this->current_font_matrix;
1465 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1466 m.m00*=INTERNAL_FONT_SIZE;
1467 m.m01*=INTERNAL_FONT_SIZE;
1468 m.m10*=INTERNAL_FONT_SIZE;
1469 m.m11*=INTERNAL_FONT_SIZE;
1471 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1472 msg("<error> Invalid charid %d for font", charid);
1475 gfxcolor_t col={0,0,0,0};
1476 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1477 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1481 /* the character itself is going to be passed using the draw functions */
1482 return gFalse; /* gTrue= is_in_cache? */
1485 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1487 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1490 void GFXOutputDev::endType3Char(GfxState *state)
1493 msg("<debug> endType3Char");
1496 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1498 this->currentpage = pageNum;
1500 int rot = doc->getPageRotate(1);
1501 gfxcolor_t white = {255,255,255,255};
1502 gfxcolor_t black = {255,0,0,0};
1504 gfxline_t clippath[5];
1506 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1507 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1508 Use CropBox, not MediaBox, as page size
1515 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1516 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1518 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1519 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1521 this->clipmovex = -(int)x1;
1522 this->clipmovey = -(int)y1;
1524 /* apply user clip box */
1525 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1526 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1527 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1528 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1529 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1530 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1533 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1535 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);
1537 msg("<verbose> page is rotated %d degrees", rot);
1539 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1540 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1541 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1542 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1543 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1544 device->startclip(device, clippath); outer_clip_box = 1;
1545 if(!config_transparent) {
1546 device->fill(device, clippath, &white);
1548 states[statepos].clipbbox.xmin = x1;
1549 states[statepos].clipbbox.ymin = x1;
1550 states[statepos].clipbbox.xmax = x2;
1551 states[statepos].clipbbox.ymax = y2;
1553 this->dashPattern = 0;
1554 this->dashLength = 0;
1555 this->dashStart = 0;
1559 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1561 double x1, y1, x2, y2;
1562 gfxline_t points[5];
1565 msg("<debug> drawlink");
1567 link->getRect(&x1, &y1, &x2, &y2);
1568 cvtUserToDev(x1, y1, &x, &y);
1569 points[0].type = gfx_moveTo;
1570 points[0].x = points[4].x = x + user_movex + clipmovex;
1571 points[0].y = points[4].y = y + user_movey + clipmovey;
1572 points[0].next = &points[1];
1573 cvtUserToDev(x2, y1, &x, &y);
1574 points[1].type = gfx_lineTo;
1575 points[1].x = x + user_movex + clipmovex;
1576 points[1].y = y + user_movey + clipmovey;
1577 points[1].next = &points[2];
1578 cvtUserToDev(x2, y2, &x, &y);
1579 points[2].type = gfx_lineTo;
1580 points[2].x = x + user_movex + clipmovex;
1581 points[2].y = y + user_movey + clipmovey;
1582 points[2].next = &points[3];
1583 cvtUserToDev(x1, y2, &x, &y);
1584 points[3].type = gfx_lineTo;
1585 points[3].x = x + user_movex + clipmovex;
1586 points[3].y = y + user_movey + clipmovey;
1587 points[3].next = &points[4];
1588 cvtUserToDev(x1, y1, &x, &y);
1589 points[4].type = gfx_lineTo;
1590 points[4].x = x + user_movex + clipmovex;
1591 points[4].y = y + user_movey + clipmovey;
1594 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1595 points[0].x, points[0].y,
1596 points[1].x, points[1].y,
1597 points[2].x, points[2].y,
1598 points[3].x, points[3].y);
1600 if(getLogLevel() >= LOGLEVEL_TRACE) {
1601 dump_outline(points);
1604 LinkAction*action=link->getAction();
1607 const char*type = "-?-";
1610 msg("<trace> drawlink action=%d", action->getKind());
1611 switch(action->getKind())
1615 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1616 LinkDest *dest=NULL;
1617 if (ha->getDest()==NULL)
1618 dest=catalog->findDest(ha->getNamedDest());
1619 else dest=ha->getDest();
1621 if (dest->isPageRef()){
1622 Ref pageref=dest->getPageRef();
1623 page=catalog->findPage(pageref.num,pageref.gen);
1625 else page=dest->getPageNum();
1626 sprintf(buf, "%d", page);
1633 LinkGoToR*l = (LinkGoToR*)action;
1634 GString*g = l->getFileName();
1636 s = strdup(g->getCString());
1638 /* if the GoToR link has no filename, then
1639 try to find a refernce in the *local*
1641 GString*g = l->getNamedDest();
1643 s = strdup(g->getCString());
1649 LinkNamed*l = (LinkNamed*)action;
1650 GString*name = l->getName();
1652 s = strdup(name->lowerCase()->getCString());
1653 named = name->getCString();
1656 if(strstr(s, "next") || strstr(s, "forward"))
1658 page = currentpage + 1;
1660 else if(strstr(s, "prev") || strstr(s, "back"))
1662 page = currentpage - 1;
1664 else if(strstr(s, "last") || strstr(s, "end"))
1666 if(pages && pagepos>0)
1667 page = pages[pagepos-1];
1669 else if(strstr(s, "first") || strstr(s, "top"))
1677 case actionLaunch: {
1679 LinkLaunch*l = (LinkLaunch*)action;
1680 GString * str = new GString(l->getFileName());
1681 GString * params = l->getParams();
1683 str->append(params);
1684 s = strdup(str->getCString());
1691 LinkURI*l = (LinkURI*)action;
1692 GString*g = l->getURI();
1694 url = g->getCString();
1699 case actionUnknown: {
1701 LinkUnknown*l = (LinkUnknown*)action;
1706 msg("<error> Unknown link type!");
1711 if(!s) s = strdup("-?-");
1713 msg("<trace> drawlink s=%s", s);
1715 if(!linkinfo && (page || s))
1717 msg("<notice> File contains links");
1725 for(t=1;t<=pagepos;t++) {
1726 if(pages[t]==page) {
1735 sprintf(buf, "page%d", lpage);
1736 device->drawlink(device, points, buf);
1740 device->drawlink(device, points, s);
1743 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1747 void GFXOutputDev::saveState(GfxState *state) {
1748 dbg("saveState %08x", state); dbgindent+=2;
1750 msg("<trace> saveState %08x", state);
1753 msg("<fatal> Too many nested states in pdf.");
1757 states[statepos].state = state;
1758 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1759 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1760 states[statepos].clipping = 0;
1761 states[statepos].olddevice = 0;
1762 states[statepos].clipbbox = states[statepos-1].clipbbox;
1765 void GFXOutputDev::restoreState(GfxState *state) {
1766 dbgindent-=2; dbg("restoreState %08x", state);
1769 msg("<fatal> Invalid restoreState");
1772 msg("<trace> restoreState %08x%s%s", state,
1773 states[statepos].softmask?" (end softmask)":"",
1774 states[statepos].clipping?" (end clipping)":"");
1775 if(states[statepos].softmask) {
1776 clearSoftMask(state);
1780 while(states[statepos].clipping) {
1781 device->endclip(device);
1782 states[statepos].clipping--;
1784 if(states[statepos].state!=state) {
1785 msg("<fatal> bad state nesting");
1788 states[statepos].state=0;
1792 void GFXOutputDev::updateLineDash(GfxState *state)
1794 if(this->dashPattern) {
1795 free(this->dashPattern);this->dashPattern = 0;
1797 double *pattern = 0;
1798 state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1799 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1800 if(!this->dashLength) {
1801 this->dashPattern = 0;
1803 double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1804 memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1805 this->dashPattern = p;
1809 void GFXOutputDev::updateLineWidth(GfxState *state)
1811 double width = state->getTransformedLineWidth();
1814 void GFXOutputDev::updateLineCap(GfxState *state)
1816 int c = state->getLineCap();
1819 void GFXOutputDev::updateLineJoin(GfxState *state)
1821 int j = state->getLineJoin();
1824 void GFXOutputDev::updateFillColor(GfxState *state)
1827 double opaq = state->getFillOpacity();
1828 state->getFillRGB(&rgb);
1830 void GFXOutputDev::updateFillOpacity(GfxState *state)
1833 double opaq = state->getFillOpacity();
1834 state->getFillRGB(&rgb);
1835 dbg("update fillopaq %f", opaq);
1837 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1839 double opaq = state->getFillOpacity();
1840 dbg("update strokeopaq %f", opaq);
1842 void GFXOutputDev::updateFillOverprint(GfxState *state)
1844 double opaq = state->getFillOverprint();
1845 dbg("update filloverprint %f", opaq);
1847 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1849 double opaq = state->getStrokeOverprint();
1850 dbg("update strokeoverprint %f", opaq);
1852 void GFXOutputDev::updateTransfer(GfxState *state)
1854 dbg("update transfer");
1858 void GFXOutputDev::updateStrokeColor(GfxState *state)
1861 double opaq = state->getStrokeOpacity();
1862 state->getStrokeRGB(&rgb);
1866 gfxfont_t* GFXOutputDev::createGfxFont(GfxFont*xpdffont, FontInfo*src)
1868 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1869 memset(font, 0, sizeof(gfxfont_t));
1871 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1872 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1876 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1878 //printf("%d glyphs\n", font->num_glyphs);
1879 font->num_glyphs = 0;
1880 font->ascent = fabs(src->descender);
1881 font->descent = fabs(src->ascender);
1883 for(t=0;t<src->num_glyphs;t++) {
1884 if(src->glyphs[t]) {
1885 SplashPath*path = src->glyphs[t]->path;
1886 int len = path?path->getLength():0;
1887 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1888 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1889 src->glyphs[t]->glyphid = font->num_glyphs;
1890 glyph->unicode = src->glyphs[t]->unicode;
1891 if(glyph->unicode >= font->max_unicode)
1892 font->max_unicode = glyph->unicode+1;
1894 gfxdrawer_target_gfxline(&drawer);
1898 for(s=0;s<len;s++) {
1901 path->getPoint(s, &x, &y, &f);
1904 if(f&splashPathFirst) {
1905 drawer.moveTo(&drawer, x*scale, y*scale);
1907 if(f&splashPathCurve) {
1909 path->getPoint(++s, &x2, &y2, &f);
1910 if(f&splashPathCurve) {
1912 path->getPoint(++s, &x3, &y3, &f);
1913 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1915 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1918 drawer.lineTo(&drawer, x*scale, y*scale);
1920 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1921 // (f&splashPathFirst)?"first":"",
1922 // (f&splashPathLast)?"last":"");
1925 glyph->line = (gfxline_t*)drawer.result(&drawer);
1926 if(src->glyphs[t]->advance>0) {
1927 glyph->advance = src->glyphs[t]->advance;
1929 msg("<warning> Approximating advance value for glyph %d", t);
1930 glyph->advance = xmax*scale;
1932 if(this->config_bigchar) {
1933 double max = src->glyphs[t]->advance_max;
1934 if(max>0 && max > glyph->advance) {
1935 glyph->advance = max;
1942 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1943 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1944 for(t=0;t<font->num_glyphs;t++) {
1945 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1946 font->unicode2glyph[font->glyphs[t].unicode] = t;
1950 msg("<trace> %d glyphs.", t, font->num_glyphs);
1954 void GFXOutputDev::updateFont(GfxState *state)
1956 GfxFont* gfxFont = state->getFont();
1960 char*id = getFontID(gfxFont);
1961 msg("<verbose> Updating font to %s", id);
1962 if(gfxFont->getType() == fontType3) {
1963 infofeature("Type3 fonts");
1964 if(!config_extrafontdata) {
1969 msg("<error> Internal Error: FontID is null");
1973 this->current_fontinfo = this->info->getFont(id);
1974 if(!this->current_fontinfo) {
1975 msg("<error> Internal Error: no fontinfo for font %s", id);
1978 if(!this->current_fontinfo->seen) {
1979 dumpFontInfo("<verbose>", gfxFont);
1982 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1984 font = this->createGfxFont(gfxFont, current_fontinfo);
1985 font->id = strdup(id);
1986 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1988 device->addfont(device, font);
1990 current_gfxfont = font;
1993 updateFontMatrix(state);
1996 #define SQR(x) ((x)*(x))
1998 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2000 if((newwidth<1 || newheight<1) ||
2001 (width<=newwidth || height<=newheight))
2003 unsigned char*newdata;
2005 newdata= (unsigned char*)malloc(newwidth*newheight);
2006 double fx = ((double)width)/newwidth;
2007 double fy = ((double)height)/newheight;
2009 int blocksize = (int)(8192/(fx*fy));
2010 int r = 8192*256/palettesize;
2011 for(x=0;x<newwidth;x++) {
2012 double ex = px + fx;
2013 int fromx = (int)px;
2015 int xweight1 = (int)((1-(px-fromx))*256);
2016 int xweight2 = (int)((ex-tox)*256);
2018 for(y=0;y<newheight;y++) {
2019 double ey = py + fy;
2020 int fromy = (int)py;
2022 int yweight1 = (int)((1-(py-fromy))*256);
2023 int yweight2 = (int)((ey-toy)*256);
2030 for(xx=fromx;xx<=tox;xx++)
2031 for(yy=fromy;yy<=toy;yy++) {
2032 int b = 1-data[width*yy+xx];
2034 if(xx==fromx) weight = (weight*xweight1)/256;
2035 if(xx==tox) weight = (weight*xweight2)/256;
2036 if(yy==fromy) weight = (weight*yweight1)/256;
2037 if(yy==toy) weight = (weight*yweight2)/256;
2040 //if(a) a=(palettesize-1)*r/blocksize;
2041 newdata[y*newwidth+x] = (a*blocksize)/r;
2049 #define IMAGE_TYPE_JPEG 0
2050 #define IMAGE_TYPE_LOSSLESS 1
2052 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2053 double x1,double y1,
2054 double x2,double y2,
2055 double x3,double y3,
2056 double x4,double y4, int type, int multiply)
2058 gfxcolor_t*newpic=0;
2060 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2061 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2063 gfxline_t p1,p2,p3,p4,p5;
2064 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2065 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2066 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2067 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2068 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2070 {p1.x = (int)(p1.x*20)/20.0;
2071 p1.y = (int)(p1.y*20)/20.0;
2072 p2.x = (int)(p2.x*20)/20.0;
2073 p2.y = (int)(p2.y*20)/20.0;
2074 p3.x = (int)(p3.x*20)/20.0;
2075 p3.y = (int)(p3.y*20)/20.0;
2076 p4.x = (int)(p4.x*20)/20.0;
2077 p4.y = (int)(p4.y*20)/20.0;
2078 p5.x = (int)(p5.x*20)/20.0;
2079 p5.y = (int)(p5.y*20)/20.0;
2083 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2084 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2086 m.tx = p1.x - 0.5*multiply;
2087 m.ty = p1.y - 0.5*multiply;
2090 img.data = (gfxcolor_t*)data;
2094 if(type == IMAGE_TYPE_JPEG)
2095 /* TODO: pass image_dpi to device instead */
2096 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2099 dev->fillbitmap(dev, &p1, &img, &m, 0);
2102 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2103 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2105 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2108 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2109 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2111 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2115 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2116 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2117 GBool inlineImg, int mask, int*maskColors,
2118 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2120 /* the code in this function is *old*. It's not pretty, but it works. */
2122 double x1,y1,x2,y2,x3,y3,x4,y4;
2123 ImageStream *imgStr;
2128 unsigned char* maskbitmap = 0;
2131 ncomps = colorMap->getNumPixelComps();
2132 bits = colorMap->getBits();
2137 unsigned char buf[8];
2138 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2140 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2141 imgMaskStr->reset();
2142 unsigned char pal[256];
2143 int n = 1 << colorMap->getBits();
2148 maskColorMap->getGray(pixBuf, &gray);
2149 pal[t] = colToByte(gray);
2151 for (y = 0; y < maskHeight; y++) {
2152 for (x = 0; x < maskWidth; x++) {
2153 imgMaskStr->getPixel(buf);
2154 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2159 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2160 imgMaskStr->reset();
2161 for (y = 0; y < maskHeight; y++) {
2162 for (x = 0; x < maskWidth; x++) {
2163 imgMaskStr->getPixel(buf);
2165 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2173 imgStr = new ImageStream(str, width, ncomps,bits);
2176 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2178 msg("<verbose> Ignoring %d by %d image", width, height);
2179 unsigned char buf[8];
2181 for (y = 0; y < height; ++y)
2182 for (x = 0; x < width; ++x) {
2183 imgStr->getPixel(buf);
2191 this->transformXY(state, 0, 1, &x1, &y1);
2192 this->transformXY(state, 0, 0, &x2, &y2);
2193 this->transformXY(state, 1, 0, &x3, &y3);
2194 this->transformXY(state, 1, 1, &x4, &y4);
2197 /* as type 3 bitmaps are antialized, we need to place them
2198 at integer coordinates, otherwise flash player's antializing
2199 will kick in and make everything blurry */
2200 x1 = (int)(x1);y1 = (int)(y1);
2201 x2 = (int)(x2);y2 = (int)(y2);
2202 x3 = (int)(x3);y3 = (int)(y3);
2203 x4 = (int)(x4);y4 = (int)(y4);
2206 if(!pbminfo && !(str->getKind()==strDCT)) {
2208 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2212 msg("<verbose> drawing %d by %d masked picture", width, height);
2214 if(!jpeginfo && (str->getKind()==strDCT)) {
2215 msg("<notice> File contains jpeg pictures");
2220 unsigned char buf[8];
2222 unsigned char*pic = new unsigned char[width*height];
2223 gfxcolor_t pal[256];
2225 state->getFillRGB(&rgb);
2227 memset(pal,255,sizeof(pal));
2228 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2229 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2230 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2231 pal[0].a = 255; pal[1].a = 0;
2234 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2235 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2236 for (y = 0; y < height; ++y)
2237 for (x = 0; x < width; ++x)
2239 imgStr->getPixel(buf);
2242 pic[width*y+x] = buf[0];
2246 unsigned char*pic2 = 0;
2249 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2258 height = realheight;
2262 /* make a black/white palette */
2264 float r = 255./(float)(numpalette-1);
2266 for(t=0;t<numpalette;t++) {
2267 pal[t].r = colToByte(rgb.r);
2268 pal[t].g = colToByte(rgb.g);
2269 pal[t].b = colToByte(rgb.b);
2270 pal[t].a = (unsigned char)(t*r);
2275 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2276 for (y = 0; y < height; ++y) {
2277 for (x = 0; x < width; ++x) {
2278 pic2[width*y+x] = pal[pic[y*width+x]];
2281 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2285 if(maskbitmap) free(maskbitmap);
2291 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2292 gfxcolor_t*pic=new gfxcolor_t[width*height];
2293 for (y = 0; y < height; ++y) {
2294 for (x = 0; x < width; ++x) {
2295 imgStr->getPixel(pixBuf);
2296 colorMap->getRGB(pixBuf, &rgb);
2297 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2298 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2299 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2300 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2302 int x1 = x*maskWidth/width;
2303 int y1 = y*maskHeight/height;
2304 int x2 = (x+1)*maskWidth/width;
2305 int y2 = (y+1)*maskHeight/height;
2307 unsigned int alpha=0;
2308 unsigned int count=0;
2309 for(xx=x1;xx<x2;xx++)
2310 for(yy=y1;yy<y2;yy++) {
2311 alpha += maskbitmap[yy*maskWidth+xx];
2315 pic[width*y+x].a = alpha / count;
2317 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2322 if(str->getKind()==strDCT)
2323 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2325 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2328 if(maskbitmap) free(maskbitmap);
2331 gfxcolor_t*pic=new gfxcolor_t[width*height];
2332 gfxcolor_t pal[256];
2333 int n = 1 << colorMap->getBits();
2335 for(t=0;t<256;t++) {
2337 colorMap->getRGB(pixBuf, &rgb);
2339 {/*if(maskColors && *maskColors==t) {
2340 msg("<notice> Color %d is transparent", t);
2341 if (imgData->maskColors) {
2343 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2344 if (pix[i] < imgData->maskColors[2*i] ||
2345 pix[i] > imgData->maskColors[2*i+1]) {
2360 pal[t].r = (unsigned char)(colToByte(rgb.r));
2361 pal[t].g = (unsigned char)(colToByte(rgb.g));
2362 pal[t].b = (unsigned char)(colToByte(rgb.b));
2363 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2366 for (y = 0; y < height; ++y) {
2367 for (x = 0; x < width; ++x) {
2368 imgStr->getPixel(pixBuf);
2369 pic[width*y+x] = pal[pixBuf[0]];
2373 if(maskWidth < width && maskHeight < height) {
2374 for(y = 0; y < height; y++) {
2375 for (x = 0; x < width; x++) {
2376 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2380 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2381 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2382 double dx = width / maskWidth;
2383 double dy = height / maskHeight;
2385 for(y = 0; y < maskHeight; y++) {
2387 for (x = 0; x < maskWidth; x++) {
2388 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2389 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2397 height = maskHeight;
2400 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2404 if(maskbitmap) free(maskbitmap);
2409 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2410 int width, int height, GBool invert,
2413 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2414 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2415 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2418 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2419 int width, int height, GfxImageColorMap *colorMap,
2420 int *maskColors, GBool inlineImg)
2422 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2423 colorMap?"colorMap":"no colorMap",
2424 maskColors?"maskColors":"no maskColors",
2426 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2427 colorMap?"colorMap":"no colorMap",
2428 maskColors?"maskColors":"no maskColors",
2431 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2432 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2433 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2436 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2437 int width, int height,
2438 GfxImageColorMap *colorMap,
2439 Stream *maskStr, int maskWidth, int maskHeight,
2442 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2443 colorMap?"colorMap":"no colorMap",
2444 maskWidth, maskHeight);
2445 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2446 colorMap?"colorMap":"no colorMap",
2447 maskWidth, maskHeight);
2449 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2450 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2451 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2454 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2455 int width, int height,
2456 GfxImageColorMap *colorMap,
2458 int maskWidth, int maskHeight,
2459 GfxImageColorMap *maskColorMap)
2461 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2462 colorMap?"colorMap":"no colorMap",
2463 maskWidth, maskHeight);
2464 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2465 colorMap?"colorMap":"no colorMap",
2466 maskWidth, maskHeight);
2468 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2469 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2470 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2473 void GFXOutputDev::stroke(GfxState *state)
2477 GfxPath * path = state->getPath();
2478 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2479 strokeGfxline(state, line, 0);
2483 void GFXOutputDev::fill(GfxState *state)
2485 gfxcolor_t col = getFillColor(state);
2486 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2488 GfxPath * path = state->getPath();
2489 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2490 if(config_optimize_polygons) {
2491 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2495 fillGfxLine(state, line);
2499 void GFXOutputDev::eoFill(GfxState *state)
2501 gfxcolor_t col = getFillColor(state);
2502 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2504 GfxPath * path = state->getPath();
2505 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2506 fillGfxLine(state, line);
2511 static const char* dirseparator()
2520 void addGlobalFont(const char*filename)
2522 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2523 memset(f, 0, sizeof(fontfile_t));
2524 f->filename = filename;
2525 int len = strlen(filename);
2526 char*r1 = strrchr(filename, '/');
2527 char*r2 = strrchr(filename, '\\');
2535 msg("<notice> Adding font \"%s\".", filename);
2536 if(global_fonts_next) {
2537 global_fonts_next->next = f;
2538 global_fonts_next = global_fonts_next->next;
2540 global_fonts_next = global_fonts = f;
2544 void addGlobalLanguageDir(const char*dir)
2546 msg("<notice> Adding %s to language pack directories", dir);
2549 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2550 strcpy(config_file, dir);
2551 strcat(config_file, dirseparator());
2552 strcat(config_file, "add-to-xpdfrc");
2554 fi = fopen(config_file, "rb");
2556 msg("<error> Could not open %s", config_file);
2559 globalParams->parseFile(new GString(config_file), fi);
2563 void addGlobalFontDir(const char*dirname)
2565 #ifdef HAVE_DIRENT_H
2566 msg("<notice> Adding %s to font directories", dirname);
2567 lastfontdir = strdup(dirname);
2568 DIR*dir = opendir(dirname);
2570 msg("<warning> Couldn't open directory %s", dirname);
2575 ent = readdir (dir);
2579 char*name = ent->d_name;
2585 if(!strncasecmp(&name[l-4], ".pfa", 4))
2587 if(!strncasecmp(&name[l-4], ".pfb", 4))
2589 if(!strncasecmp(&name[l-4], ".ttf", 4))
2592 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2593 strcpy(fontname, dirname);
2594 strcat(fontname, dirseparator());
2595 strcat(fontname, name);
2596 addGlobalFont(fontname);
2601 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2605 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2611 this->pagebuflen = 1024;
2612 if(pdfpage > this->pagebuflen)
2613 this->pagebuflen = pdfpage+1;
2614 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2615 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2618 while(pdfpage >= this->pagebuflen)
2620 int oldlen = this->pagebuflen;
2621 this->pagebuflen+=1024;
2622 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2623 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2626 this->pages[pdfpage] = outputpage;
2627 if(pdfpage>this->pagepos)
2628 this->pagepos = pdfpage;
2631 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2632 GfxColorSpace *blendingColorSpace,
2633 GBool isolated, GBool knockout,
2636 const char*colormodename = "";
2638 if(blendingColorSpace) {
2639 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2641 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);
2642 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);
2644 //states[statepos].createsoftmask |= forSoftMask;
2645 states[statepos].createsoftmask = forSoftMask;
2646 states[statepos].transparencygroup = !forSoftMask;
2647 states[statepos].isolated = isolated;
2649 states[statepos].olddevice = this->device;
2650 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2651 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2653 gfxdevice_record_init(this->device);
2655 /*if(!forSoftMask) { ////???
2656 state->setFillOpacity(0.0);
2661 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2664 gfxdevice_t*r = this->device;
2666 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2668 this->device = states[statepos].olddevice;
2670 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2671 /* if these errors occur more often, we should build a seperate
2672 transparency group stack, like xpdf/SplashOutputDev.cc does */
2673 restoreState(state);
2674 this->device = states[statepos].olddevice;
2676 states[statepos].olddevice = 0;
2678 gfxresult_t*recording = r->finish(r);
2680 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2681 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2683 if(states[statepos].createsoftmask) {
2684 states[statepos-1].softmaskrecording = recording;
2686 states[statepos-1].grouprecording = recording;
2689 states[statepos].createsoftmask = 0;
2690 states[statepos].transparencygroup = 0;
2694 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2696 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2697 "colordodge","colorburn","hardlight","softlight","difference",
2698 "exclusion","hue","saturation","color","luminosity"};
2700 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2701 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2703 if(state->getBlendMode() == gfxBlendNormal)
2704 infofeature("transparency groups");
2707 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2708 warnfeature(buffer, 0);
2711 gfxresult_t*grouprecording = states[statepos].grouprecording;
2713 int blendmode = state->getBlendMode();
2714 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2715 int alpha = (int)(state->getFillOpacity()*255);
2716 if(blendmode == gfxBlendMultiply && alpha>200)
2719 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2720 gfxdevice_ops_init(&ops, this->device, alpha);
2721 gfxresult_record_replay(grouprecording, &ops);
2724 grouprecording->destroy(grouprecording);
2726 states[statepos].grouprecording = 0;
2729 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2731 if(states[statepos].softmask) {
2732 /* shouldn't happen, but *does* happen */
2733 clearSoftMask(state);
2736 /* alpha = 1: retrieve mask values from alpha layer
2737 alpha = 0: retrieve mask values from luminance */
2739 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2740 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2741 msg("<verbose> 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]));
2744 infofeature("soft masks");
2746 warnfeature("soft masks from alpha channel",0);
2748 if(states[statepos].olddevice) {
2749 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2752 states[statepos].olddevice = this->device;
2753 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2754 gfxdevice_record_init(this->device);
2756 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2758 states[statepos].softmask = 1;
2759 states[statepos].softmask_alpha = alpha;
2762 static inline Guchar div255(int x) {
2763 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2766 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2768 if(c < min) c = min;
2769 if(c > max) c = max;
2773 void GFXOutputDev::clearSoftMask(GfxState *state)
2775 if(!states[statepos].softmask)
2777 states[statepos].softmask = 0;
2778 dbg("clearSoftMask statepos=%d", statepos);
2779 msg("<verbose> clearSoftMask statepos=%d", statepos);
2781 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2782 msg("<error> Error in softmask/tgroup ordering");
2786 gfxresult_t*mask = states[statepos].softmaskrecording;
2787 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2788 this->device = states[statepos].olddevice;
2790 /* get outline of all objects below the soft mask */
2791 gfxdevice_t uniondev;
2792 gfxdevice_union_init(&uniondev, 0);
2793 gfxresult_record_replay(below, &uniondev);
2794 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2795 uniondev.finish(&uniondev);
2796 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2797 gfxline_free(belowoutline);belowoutline=0;
2799 this->device->startclip(this->device, belowoutline);
2800 gfxresult_record_replay(below, this->device);
2801 gfxresult_record_replay(mask, this->device);
2802 this->device->endclip(this->device);
2805 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2806 if(width<=0 || height<=0)
2809 gfxdevice_t belowrender;
2810 gfxdevice_render_init(&belowrender);
2811 if(states[statepos+1].isolated) {
2812 belowrender.setparameter(&belowrender, "fillwhite", "1");
2814 belowrender.setparameter(&belowrender, "antialize", "2");
2815 belowrender.startpage(&belowrender, width, height);
2816 gfxresult_record_replay(below, &belowrender);
2817 belowrender.endpage(&belowrender);
2818 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2819 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2820 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2822 gfxdevice_t maskrender;
2823 gfxdevice_render_init(&maskrender);
2824 maskrender.startpage(&maskrender, width, height);
2825 gfxresult_record_replay(mask, &maskrender);
2826 maskrender.endpage(&maskrender);
2827 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2828 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2830 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2831 msg("<fatal> Internal error in mask drawing");
2836 for(y=0;y<height;y++) {
2837 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2838 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2839 for(x=0;x<width;x++) {
2841 if(states[statepos].softmask_alpha) {
2844 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2847 l2->a = div255(alpha*l2->a);
2849 /* DON'T premultiply alpha- this is done by fillbitmap,
2850 depending on the output device */
2851 //l2->r = div255(alpha*l2->r);
2852 //l2->g = div255(alpha*l2->g);
2853 //l2->b = div255(alpha*l2->b);
2859 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2862 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2863 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2865 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2867 mask->destroy(mask);
2868 below->destroy(below);
2869 maskresult->destroy(maskresult);
2870 belowresult->destroy(belowresult);
2871 states[statepos].softmaskrecording = 0;
2876 // public: ~MemCheck()
2878 // delete globalParams;globalParams=0;
2879 // Object::memCheck(stderr);
2880 // gMemReport(stderr);