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>
54 #include "OutputDev.h"
57 #include "CharCodeToUnicode.h"
58 #include "NameToUnicodeTable.h"
59 #include "GlobalParams.h"
60 #include "FoFiType1C.h"
61 #include "FoFiTrueType.h"
63 #include "GFXOutputDev.h"
65 // swftools header files
67 #include "../gfxdevice.h"
68 #include "../gfxtools.h"
69 #include "../gfxfont.h"
70 #include "../gfxpoly.h"
71 #include "../devices/record.h"
72 #include "../devices/ops.h"
73 #include "../devices/polyops.h"
74 #include "../devices/render.h"
81 #define SQRT2 1.41421356237309504880
83 typedef struct _fontfile
86 int len; // basename length
88 struct _fontfile*next;
93 static fontfile_t* global_fonts = 0;
94 static fontfile_t* global_fonts_next = 0;
96 static int fontnum = 0;
100 static char* lastfontdir = 0;
111 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
112 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
113 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
114 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
115 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
116 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
117 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
118 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
119 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
120 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
121 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
122 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
123 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
124 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
127 static int verbose = 0;
128 static int dbgindent = 0;
129 static void dbg(const char*format, ...)
136 va_start(arglist, format);
137 vsprintf(buf, format, arglist);
140 while(l && buf[l-1]=='\n') {
145 int indent = dbgindent;
155 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
157 feature_t*f = this->featurewarnings;
159 if(!strcmp(feature, f->string))
163 f = (feature_t*)malloc(sizeof(feature_t));
164 f->string = strdup(feature);
165 f->next = this->featurewarnings;
166 this->featurewarnings = f;
168 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
169 if(this->config_break_on_warning) {
170 msg("<fatal> Aborting conversion due to unsupported feature");
174 msg("<notice> File contains %s",feature);
177 void GFXOutputDev::warnfeature(const char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(const char*feature)
183 showfeature(feature,0,0);
186 GFXOutputState::GFXOutputState() {
188 this->createsoftmask = 0;
189 this->transparencygroup = 0;
191 this->grouprecording = 0;
195 GBool GFXOutputDev::interpretType3Chars()
200 typedef struct _drawnchar
218 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
219 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224 free(chars);chars = 0;
231 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
235 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
238 chars[num_chars].x = x;
239 chars[num_chars].y = y;
240 chars[num_chars].color = color;
241 chars[num_chars].charid = charid;
245 char* writeOutStdFont(fontentry* f)
250 char* tmpFileName = mktmpname(namebuf1);
252 sprintf(namebuf2, "%s.afm", tmpFileName);
253 fi = fopen(namebuf2, "wb");
256 fwrite(f->afm, 1, f->afmlen, fi);
259 sprintf(namebuf2, "%s.pfb", tmpFileName);
260 fi = fopen(namebuf2, "wb");
263 fwrite(f->pfb, 1, f->pfblen, fi);
265 return strdup(namebuf2);
267 void unlinkfont(char* filename)
272 msg("<verbose> Removing temporary font file %s", filename);
275 if(!strncmp(&filename[l-4],".afm",4)) {
276 memcpy(&filename[l-4],".pfb",4); unlink(filename);
277 memcpy(&filename[l-4],".pfa",4); unlink(filename);
278 memcpy(&filename[l-4],".afm",4);
281 if(!strncmp(&filename[l-4],".pfa",4)) {
282 memcpy(&filename[l-4],".afm",4); unlink(filename);
283 memcpy(&filename[l-4],".pfa",4);
286 if(!strncmp(&filename[l-4],".pfb",4)) {
287 memcpy(&filename[l-4],".afm",4); unlink(filename);
288 memcpy(&filename[l-4],".pfb",4);
293 static int config_use_fontconfig = 1;
294 static int fcinitcalled = 0;
296 GFXGlobalParams::GFXGlobalParams()
299 //setupBaseFonts(char *dir); //not tested yet
301 GFXGlobalParams::~GFXGlobalParams()
303 msg("<verbose> Performing cleanups");
305 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
306 if(pdf2t1map[t].fullfilename) {
307 unlinkfont(pdf2t1map[t].fullfilename);
310 #ifdef HAVE_FONTCONFIG
311 if(config_use_fontconfig && fcinitcalled)
315 #ifdef HAVE_FONTCONFIG
316 static char fc_ismatch(FcPattern*match, char*family, char*style)
318 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
319 FcBool scalable=FcFalse, outline=FcFalse;
320 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
321 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
322 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
323 FcPatternGetBool(match, "outline", 0, &outline);
324 FcPatternGetBool(match, "scalable", 0, &scalable);
326 if(scalable!=FcTrue || outline!=FcTrue)
329 if (!strcasecmp(fcfamily, family)) {
330 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
333 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
339 char* fontconfig_searchForFont(char*name)
341 #ifdef HAVE_FONTCONFIG
342 if(!config_use_fontconfig)
345 // call init ony once
349 // check whether we have a config file
350 char* configfile = (char*)FcConfigFilename(0);
351 int configexists = 0;
352 FILE*fi = fopen(configfile, "rb");
354 configexists = 1;fclose(fi);
355 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
357 msg("<debug> Initializing FontConfig (no configfile)");
361 /* A fontconfig instance which didn't find a configfile is unbelievably
362 cranky, so let's just write out a small xml file and make fontconfig
364 FcConfig*c = FcConfigCreate();
366 char* tmpFileName = mktmpname(namebuf);
367 FILE*fi = fopen(tmpFileName, "wb");
368 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
370 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
372 fprintf(fi, "<dir>~/.fonts</dir>\n");
374 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
376 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
377 fprintf(fi, "</fontconfig>\n");
379 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
380 FcConfigBuildFonts(c);
381 FcConfigSetCurrent(c);
385 msg("<debug> FontConfig Initialization failed. Disabling.");
386 config_use_fontconfig = 0;
389 FcConfig * config = FcConfigGetCurrent();
391 msg("<debug> FontConfig Config Initialization failed. Disabling.");
392 config_use_fontconfig = 0;
395 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
396 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
397 if(!set || !set->nfont) {
398 msg("<debug> FontConfig has zero fonts. Disabling.");
399 config_use_fontconfig = 0;
403 if(getLogLevel() >= LOGLEVEL_TRACE) {
405 for(t=0;t<set->nfont;t++) {
406 char*fcfamily=0,*fcstyle=0,*filename=0;
407 FcBool scalable=FcFalse, outline=FcFalse;
408 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
409 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
410 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
411 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
412 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
413 if(scalable && outline) {
414 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
420 char*family = strdup(name);
422 char*dash = strchr(family, '-');
423 FcPattern*pattern = 0;
427 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
428 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
430 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
431 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
435 FcConfigSubstitute(0, pattern, FcMatchPattern);
436 FcDefaultSubstitute(pattern);
438 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
441 for(t=0;t<set->nfont;t++) {
442 FcPattern*match = set->fonts[t];
443 //FcPattern*match = FcFontMatch(0, pattern, &result);
444 if(fc_ismatch(match, family, style)) {
446 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
447 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
450 //FcPatternDestroy(match);
451 msg("<debug> fontconfig: returning filename %s", filename);
453 FcPatternDestroy(pattern);
454 FcFontSetDestroy(set);
455 return filename?strdup(filename):0;
460 FcPatternDestroy(pattern);
461 FcFontSetDestroy(set);
468 static DisplayFontParamKind detectFontType(const char*filename)
470 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
471 return displayFontTT;
472 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
473 return displayFontT1;
474 return displayFontTT;
477 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
479 msg("<verbose> looking for font %s in global params", fontName->getCString());
481 char*name = fontName->getCString();
483 /* see if it is a pdf standard font */
485 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
486 if(!strcmp(name, pdf2t1map[t].pdffont)) {
487 if(!pdf2t1map[t].fullfilename) {
488 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
489 if(!pdf2t1map[t].fullfilename) {
490 msg("<error> Couldn't save default font- is the Temp Directory writable?");
492 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
495 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
496 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
501 int bestlen = 0x7fffffff;
502 const char*bestfilename = 0;
504 fontfile_t*f = global_fonts;
506 if(strstr(f->filename, name)) {
507 if(f->len < bestlen) {
509 bestfilename = f->filename;
515 /* if we didn't find anything up to now, try looking for the
516 font via fontconfig */
519 filename = fontconfig_searchForFont(name);
521 filename = strdup(bestfilename);
525 DisplayFontParamKind kind = detectFontType(filename);
526 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
527 if(kind == displayFontTT) {
528 dfp->tt.fileName = new GString(filename);
530 dfp->t1.fileName = new GString(filename);
535 return GlobalParams::getDisplayFont(fontName);
538 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
542 this->xref = doc->getXRef();
545 this->textmodeinfo = 0;
548 this->type3active = 0;
551 this->substitutepos = 0;
552 this->type3Warning = 0;
553 this->user_movex = 0;
554 this->user_movey = 0;
557 this->user_clipx1 = 0;
558 this->user_clipy1 = 0;
559 this->user_clipx2 = 0;
560 this->user_clipy2 = 0;
561 this->current_text_stroke = 0;
562 this->current_text_clip = 0;
563 this->outer_clip_box = 0;
565 this->pagebuflen = 0;
567 this->config_convertgradients=0;
568 this->config_break_on_warning=0;
569 this->config_remapunicode=0;
570 this->config_transparent=0;
571 this->config_extrafontdata = 0;
572 this->config_fontquality = 10;
573 this->config_optimize_polygons = 0;
575 this->gfxfontlist = gfxfontlist_create();
577 memset(states, 0, sizeof(states));
578 this->featurewarnings = 0;
581 void GFXOutputDev::setParameter(const char*key, const char*value)
583 if(!strcmp(key,"breakonwarning")) {
584 this->config_break_on_warning = atoi(value);
585 } else if(!strcmp(key,"remapunicode")) {
586 this->config_remapunicode = atoi(value);
587 } else if(!strcmp(key,"transparent")) {
588 this->config_transparent = atoi(value);
589 } else if(!strcmp(key,"extrafontdata")) {
590 this->config_extrafontdata = atoi(value);
591 } else if(!strcmp(key,"convertgradients")) {
592 this->config_convertgradients = atoi(value);
593 } else if(!strcmp(key,"optimize_polygons")) {
594 this->config_optimize_polygons = atoi(value);
595 } else if(!strcmp(key,"fontquality")) {
596 this->config_fontquality = atof(value);
597 if(this->config_fontquality<=1)
598 this->config_fontquality=1;
599 } else if(!strcmp(key,"help")) {
600 printf("\nPDF layer options:\n");
601 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
602 printf(" are not 100%% supported\n");
603 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
604 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
605 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
610 void GFXOutputDev::setDevice(gfxdevice_t*dev)
615 void GFXOutputDev::setMove(int x,int y)
617 this->user_movex = x;
618 this->user_movey = y;
621 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
623 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
624 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
626 this->user_clipx1 = x1;
627 this->user_clipy1 = y1;
628 this->user_clipx2 = x2;
629 this->user_clipy2 = y2;
632 static char*getFontName(GfxFont*font)
635 GString*gstr = font->getName();
636 char* fname = gstr==0?0:gstr->getCString();
640 sprintf(buf, "UFONT%d", r->num);
641 fontid = strdup(buf);
643 fontid = strdup(fname);
647 char* plus = strchr(fontid, '+');
648 if(plus && plus < &fontid[strlen(fontid)-1]) {
649 fontname = strdup(plus+1);
651 fontname = strdup(fontid);
657 static void dumpFontInfo(const char*loglevel, GfxFont*font);
658 static int lastdumps[1024];
659 static int lastdumppos = 0;
664 static void showFontError(GfxFont*font, int nr)
668 for(t=0;t<lastdumppos;t++)
669 if(lastdumps[t] == r->num)
673 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
674 lastdumps[lastdumppos++] = r->num;
676 msg("<warning> The following font caused problems:");
678 msg("<warning> The following font caused problems (substituting):");
680 msg("<warning> The following Type 3 Font will be rendered as graphics:");
681 dumpFontInfo("<warning>", font);
684 static void dumpFontInfo(const char*loglevel, GfxFont*font)
686 char* id = getFontID(font);
687 char* name = getFontName(font);
688 Ref* r=font->getID();
689 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
691 GString*gstr = font->getTag();
693 msg("%s| Tag: %s", loglevel, id);
695 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
697 GfxFontType type=font->getType();
699 case fontUnknownType:
700 msg("%s| Type: unknown",loglevel);
703 msg("%s| Type: 1",loglevel);
706 msg("%s| Type: 1C",loglevel);
709 msg("%s| Type: 3",loglevel);
712 msg("%s| Type: TrueType",loglevel);
715 msg("%s| Type: CIDType0",loglevel);
718 msg("%s| Type: CIDType0C",loglevel);
721 msg("%s| Type: CIDType2",loglevel);
726 GBool embedded = font->getEmbeddedFontID(&embRef);
728 if(font->getEmbeddedFontName()) {
729 embeddedName = font->getEmbeddedFontName()->getCString();
732 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
734 gstr = font->getExtFontFile();
736 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
738 // Get font descriptor flags.
739 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
740 if(font->isSerif()) msg("%s| is serif", loglevel);
741 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
742 if(font->isItalic()) msg("%s| is italic", loglevel);
743 if(font->isBold()) msg("%s| is bold", loglevel);
749 //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");}
750 //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");}
752 void dump_outline(gfxline_t*line)
755 if(line->type == gfx_moveTo) {
756 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
757 } else if(line->type == gfx_lineTo) {
758 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
759 } else if(line->type == gfx_splineTo) {
760 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
766 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
768 int num = path->getNumSubpaths();
771 double lastx=0,lasty=0,posx=0,posy=0;
774 msg("<warning> empty path");
778 gfxdrawer_target_gfxline(&draw);
780 for(t = 0; t < num; t++) {
781 GfxSubpath *subpath = path->getSubpath(t);
782 int subnum = subpath->getNumPoints();
783 double bx=0,by=0,cx=0,cy=0;
785 for(s=0;s<subnum;s++) {
788 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
791 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
792 draw.lineTo(&draw, lastx, lasty);
794 draw.moveTo(&draw, x,y);
799 } else if(subpath->getCurve(s) && cpos==0) {
803 } else if(subpath->getCurve(s) && cpos==1) {
811 draw.lineTo(&draw, x,y);
813 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
820 /* fix non-closed lines */
821 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
822 draw.lineTo(&draw, lastx, lasty);
824 gfxline_t*result = (gfxline_t*)draw.result(&draw);
826 gfxline_optimize(result);
831 GBool GFXOutputDev::useTilingPatternFill()
833 infofeature("tiled patterns");
834 // if(config_convertgradients)
838 GBool GFXOutputDev::useShadedFills()
840 infofeature("shaded fills");
841 if(config_convertgradients)
846 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
848 state->transform(x,y,nx,ny);
849 *nx += user_movex + clipmovex;
850 *ny += user_movey + clipmovey;
854 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
855 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
856 int paintType, Dict *resDict,
857 double *mat, double *bbox,
858 int x0, int y0, int x1, int y1,
859 double xStep, double yStep)
861 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
862 int paintType, Dict *resDict,
863 double *mat, double *bbox,
864 int x0, int y0, int x1, int y1,
865 double xStep, double yStep)
868 msg("<debug> tilingPatternFill");
869 infofeature("tiling pattern fills");
872 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
874 msg("<error> functionShadedFill not supported yet");
875 infofeature("function shaded fills");
878 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
882 colspace->getRGB(col, &rgb);
883 c.r = colToByte(rgb.r);
884 c.g = colToByte(rgb.g);
885 c.b = colToByte(rgb.b);
890 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
892 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
893 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
896 this->transformXY(state, x0,y0, &x0,&y0);
897 this->transformXY(state, x1,y1, &x1,&y1);
898 this->transformXY(state, x2,y2, &x2,&y2);
903 shading->getColor(0.0, &color0);
904 shading->getColor(0.5, &color1);
905 shading->getColor(1.0, &color2);
907 GfxColorSpace* colspace = shading->getColorSpace();
909 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
910 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
911 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
912 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
913 infofeature("radial shaded fills");
915 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
919 g[0].color = col2col(colspace, &color0);
920 g[1].color = col2col(colspace, &color1);
921 g[2].color = col2col(colspace, &color2);
926 gfxbbox_t b = states[statepos].clipbbox;
927 gfxline_t p1,p2,p3,p4,p5;
928 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
929 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
930 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
931 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
932 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
935 //m.m00 = (x3-x0); m.m10 = (x1-x0);
936 //m.m01 = (y3-y0); m.m11 = (y1-y0);
937 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
938 m.m00 = (x1-x0); m.m10 = (x2-x0);
939 m.m01 = (y1-y0); m.m11 = (y2-y0);
943 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
947 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
950 shading->getCoords(&x0,&y0,&x1,&y1);
951 this->transformXY(state, x0,y0,&x0,&y0);
952 this->transformXY(state, x1,y1,&x1,&y1);
957 shading->getColor(0.0, &color0);
958 shading->getColor(0.5, &color1);
959 shading->getColor(1.0, &color2);
961 GfxColorSpace* colspace = shading->getColorSpace();
963 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
964 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
965 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
966 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
968 infofeature("axial shaded fills");
970 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
974 g[0].color = col2col(colspace, &color0);
975 g[1].color = col2col(colspace, &color1);
976 g[2].color = col2col(colspace, &color2);
981 double xMin,yMin,xMax,yMax;
982 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
983 this->transformXY(state, xMin, yMin, &xMin, &yMin);
984 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
987 xMin = 1024; yMin = 1024;
989 gfxbbox_t b = states[statepos].clipbbox;
990 gfxline_t p1,p2,p3,p4,p5;
991 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
992 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
993 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
994 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
995 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
997 /* the gradient starts at (-1.0,0.0), so move (0,0) to
998 the middle of the two control points */
1000 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1001 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1002 m.tx = (x0 + x1)/2 - 0.5;
1003 m.ty = (y0 + y1)/2 - 0.5;
1005 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1009 GBool GFXOutputDev::useDrawForm()
1011 infofeature("forms");
1014 void GFXOutputDev::drawForm(Ref id)
1016 msg("<error> drawForm not implemented");
1018 GBool GFXOutputDev::needNonText()
1022 void GFXOutputDev::endPage()
1024 msg("<verbose> endPage (GfxOutputDev)");
1025 if(outer_clip_box) {
1026 device->endclip(device);
1029 this->dashPattern = 0;
1030 /* notice: we're not fully done yet with this page- there might still be
1031 a few calls to drawLink() yet to come */
1034 static inline double sqr(double x) {return x*x;}
1036 #define STROKE_FILL 1
1037 #define STROKE_CLIP 2
1038 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1040 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1041 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1042 double miterLimit = state->getMiterLimit();
1043 double width = state->getTransformedLineWidth();
1046 double opaq = state->getStrokeOpacity();
1048 state->getFillRGB(&rgb);
1050 state->getStrokeRGB(&rgb);
1052 col.r = colToByte(rgb.r);
1053 col.g = colToByte(rgb.g);
1054 col.b = colToByte(rgb.b);
1055 col.a = (unsigned char)(opaq*255);
1057 gfx_capType capType = gfx_capRound;
1058 if(lineCap == 0) capType = gfx_capButt;
1059 else if(lineCap == 1) capType = gfx_capRound;
1060 else if(lineCap == 2) capType = gfx_capSquare;
1062 gfx_joinType joinType = gfx_joinRound;
1063 if(lineJoin == 0) joinType = gfx_joinMiter;
1064 else if(lineJoin == 1) joinType = gfx_joinRound;
1065 else if(lineJoin == 2) joinType = gfx_joinBevel;
1067 gfxline_t*line2 = 0;
1069 if(this->dashLength && this->dashPattern) {
1070 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1073 /* try to find out how much the transformation matrix would
1074 stretch the dashes, and factor that into the dash lengths.
1075 This is not the entirely correct approach- it would be
1076 better to first convert the path to an unscaled version,
1077 then apply dashing, and then transform the path using
1078 the current transformation matrix. However there are few
1079 PDFs which actually stretch a dashed path in a non-orthonormal
1081 double tx1, ty1, tx2, ty2;
1082 this->transformXY(state, 0, 0, &tx1, &ty1);
1083 this->transformXY(state, 1, 1, &tx2, &ty2);
1084 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1086 msg("<trace> %d dashes", this->dashLength);
1087 msg("<trace> | phase: %f", this->dashStart);
1088 for(t=0;t<this->dashLength;t++) {
1089 dash[t] = (float)this->dashPattern[t] * f;
1090 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1092 dash[this->dashLength] = -1;
1093 if(getLogLevel() >= LOGLEVEL_TRACE) {
1097 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1100 msg("<trace> After dashing:");
1103 if(getLogLevel() >= LOGLEVEL_TRACE) {
1104 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1106 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1107 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1109 col.r,col.g,col.b,col.a
1114 if(flags&STROKE_FILL) {
1115 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1116 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1117 if(getLogLevel() >= LOGLEVEL_TRACE) {
1118 dump_outline(gfxline);
1121 msg("<warning> Empty polygon (resulting from stroked line)");
1123 if(flags&STROKE_CLIP) {
1124 device->startclip(device, gfxline);
1125 states[statepos].clipping++;
1127 device->fill(device, gfxline, &col);
1129 gfxline_free(gfxline);
1132 if(flags&STROKE_CLIP)
1133 msg("<error> Stroke&clip not supported at the same time");
1134 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1138 gfxline_free(line2);
1141 gfxcolor_t getFillColor(GfxState * state)
1144 double opaq = state->getFillOpacity();
1145 state->getFillRGB(&rgb);
1147 col.r = colToByte(rgb.r);
1148 col.g = colToByte(rgb.g);
1149 col.b = colToByte(rgb.b);
1150 col.a = (unsigned char)(opaq*255);
1154 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1156 gfxcolor_t col = getFillColor(state);
1158 if(getLogLevel() >= LOGLEVEL_TRACE) {
1159 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1162 device->fill(device, line, &col);
1165 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1167 if(getLogLevel() >= LOGLEVEL_TRACE) {
1170 gfxbbox_t bbox = gfxline_getbbox(line);
1171 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1173 device->startclip(device, line);
1174 states[statepos].clipping++;
1177 void GFXOutputDev::clip(GfxState *state)
1179 GfxPath * path = state->getPath();
1180 msg("<trace> clip");
1181 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1182 if(config_optimize_polygons) {
1183 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1187 clipToGfxLine(state, line);
1191 void GFXOutputDev::eoClip(GfxState *state)
1193 GfxPath * path = state->getPath();
1194 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1195 clipToGfxLine(state, line);
1198 void GFXOutputDev::clipToStrokePath(GfxState *state)
1200 GfxPath * path = state->getPath();
1201 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1203 if(getLogLevel() >= LOGLEVEL_TRACE) {
1204 msg("<trace> cliptostrokepath");
1208 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1212 void GFXOutputDev::finish()
1214 if(outer_clip_box) {
1216 device->endclip(device);
1222 GFXOutputDev::~GFXOutputDev()
1227 free(this->pages); this->pages = 0;
1230 feature_t*f = this->featurewarnings;
1232 feature_t*next = f->next;
1234 free(f->string);f->string =0;
1240 this->featurewarnings = 0;
1242 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1244 GBool GFXOutputDev::upsideDown()
1248 GBool GFXOutputDev::useDrawChar()
1253 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1254 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1256 static char tmp_printstr[4096];
1257 char* makeStringPrintable(char*str)
1259 int len = strlen(str);
1266 for(t=0;t<len;t++) {
1271 tmp_printstr[t] = c;
1274 tmp_printstr[len++] = '.';
1275 tmp_printstr[len++] = '.';
1276 tmp_printstr[len++] = '.';
1278 tmp_printstr[len] = 0;
1279 return tmp_printstr;
1281 #define INTERNAL_FONT_SIZE 1024.0
1282 void GFXOutputDev::updateFontMatrix(GfxState*state)
1284 double* ctm = state->getCTM();
1285 double fontSize = state->getFontSize();
1286 double*textMat = state->getTextMat();
1288 /* taking the absolute value of horizScaling seems to be required for
1289 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1290 double hscale = fabs(state->getHorizScaling());
1292 // from xpdf-3.02/SplashOutputDev:updateFont
1293 double mm11 = textMat[0] * fontSize * hscale;
1294 double mm12 = textMat[1] * fontSize * hscale;
1295 double mm21 = textMat[2] * fontSize;
1296 double mm22 = textMat[3] * fontSize;
1298 // multiply with ctm, like state->getFontTransMat() does
1299 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1300 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1301 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1302 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1303 this->current_font_matrix.tx = 0;
1304 this->current_font_matrix.ty = 0;
1307 void GFXOutputDev::beginString(GfxState *state, GString *s)
1309 int render = state->getRender();
1310 if(current_text_stroke) {
1311 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1314 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1317 static gfxline_t* mkEmptyGfxShape(double x, double y)
1319 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1320 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1324 static char isValidUnicode(int c)
1326 if(c>=32 && c<0x2fffe)
1331 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1332 double dx, double dy,
1333 double originX, double originY,
1334 CharCode charid, int nBytes, Unicode *_u, int uLen)
1336 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1337 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1341 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1343 int render = state->getRender();
1344 gfxcolor_t col = getFillColor(state);
1346 // check for invisible text -- this is used by Acrobat Capture
1347 if (render == RENDER_INVISIBLE) {
1349 if(!config_extrafontdata)
1353 GfxFont*font = state->getFont();
1355 if(font->getType() == fontType3) {
1356 /* type 3 chars are passed as graphics */
1357 msg("<debug> type3 char at %f/%f", x, y);
1361 Unicode u = uLen?(_u[0]):0;
1362 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);
1364 gfxmatrix_t m = this->current_font_matrix;
1365 this->transformXY(state, x, y, &m.tx, &m.ty);
1367 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1368 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1370 msg("<debug> Drawing glyph %d as shape", charid);
1372 msg("<notice> Some texts will be rendered as shape");
1375 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1376 gfxline_t*tglyph = gfxline_clone(glyph);
1377 gfxline_transform(tglyph, &m);
1378 if((render&3) != RENDER_INVISIBLE) {
1379 gfxline_t*add = gfxline_clone(tglyph);
1380 current_text_stroke = gfxline_append(current_text_stroke, add);
1382 if(render&RENDER_CLIP) {
1383 gfxline_t*add = gfxline_clone(tglyph);
1384 current_text_clip = gfxline_append(current_text_clip, add);
1385 if(!current_text_clip) {
1386 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1389 gfxline_free(tglyph);
1393 void GFXOutputDev::endString(GfxState *state)
1395 int render = state->getRender();
1396 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1398 if(current_text_stroke) {
1399 /* fillstroke and stroke text rendering objects we can process right
1400 now (as there may be texts of other rendering modes in this
1401 text object)- clipping objects have to wait until endTextObject,
1403 device->setparameter(device, "mark","TXT");
1404 if((render&3) == RENDER_FILL) {
1405 fillGfxLine(state, current_text_stroke);
1406 gfxline_free(current_text_stroke);
1407 current_text_stroke = 0;
1408 } else if((render&3) == RENDER_FILLSTROKE) {
1409 fillGfxLine(state, current_text_stroke);
1410 strokeGfxline(state, current_text_stroke,0);
1411 gfxline_free(current_text_stroke);
1412 current_text_stroke = 0;
1413 } else if((render&3) == RENDER_STROKE) {
1414 strokeGfxline(state, current_text_stroke,0);
1415 gfxline_free(current_text_stroke);
1416 current_text_stroke = 0;
1418 device->setparameter(device, "mark","");
1422 void GFXOutputDev::endTextObject(GfxState *state)
1424 int render = state->getRender();
1425 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1427 if(current_text_clip) {
1428 device->setparameter(device, "mark","TXT");
1429 clipToGfxLine(state, current_text_clip);
1430 device->setparameter(device, "mark","");
1431 gfxline_free(current_text_clip);
1432 current_text_clip = 0;
1436 /* the logic seems to be as following:
1437 first, beginType3Char is called, with the charcode and the coordinates.
1438 if this function returns true, it already knew about the char and has now drawn it.
1439 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1440 called with some parameters.
1441 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1442 at the position first passed to beginType3Char). the char ends with endType3Char.
1444 The drawing operations between beginType3Char and endType3Char are somewhat different to
1445 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1446 color determines the color of a font)
1449 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1451 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1454 if(config_extrafontdata && current_fontinfo) {
1456 gfxmatrix_t m = this->current_font_matrix;
1457 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1458 m.m00*=INTERNAL_FONT_SIZE;
1459 m.m01*=INTERNAL_FONT_SIZE;
1460 m.m10*=INTERNAL_FONT_SIZE;
1461 m.m11*=INTERNAL_FONT_SIZE;
1463 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1464 msg("<error> Invalid charid %d for font", charid);
1467 gfxcolor_t col={0,0,0,0};
1468 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1469 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1473 /* the character itself is going to be passed using the draw functions */
1474 return gFalse; /* gTrue= is_in_cache? */
1477 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1479 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1482 void GFXOutputDev::endType3Char(GfxState *state)
1485 msg("<debug> endType3Char");
1488 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1490 this->currentpage = pageNum;
1492 int rot = doc->getPageRotate(1);
1493 gfxcolor_t white = {255,255,255,255};
1494 gfxcolor_t black = {255,0,0,0};
1496 gfxline_t clippath[5];
1498 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1499 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1500 Use CropBox, not MediaBox, as page size
1507 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1508 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1510 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1511 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1513 this->clipmovex = -(int)x1;
1514 this->clipmovey = -(int)y1;
1516 /* apply user clip box */
1517 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1518 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1519 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1520 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1521 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1522 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1525 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1527 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);
1529 msg("<verbose> page is rotated %d degrees", rot);
1531 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1532 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1533 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1534 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1535 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1536 device->startclip(device, clippath); outer_clip_box = 1;
1537 if(!config_transparent) {
1538 device->fill(device, clippath, &white);
1540 states[statepos].clipbbox.xmin = x1;
1541 states[statepos].clipbbox.ymin = x1;
1542 states[statepos].clipbbox.xmax = x2;
1543 states[statepos].clipbbox.ymax = y2;
1545 this->dashPattern = 0;
1546 this->dashLength = 0;
1547 this->dashStart = 0;
1551 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1553 double x1, y1, x2, y2;
1554 gfxline_t points[5];
1557 msg("<debug> drawlink");
1559 link->getRect(&x1, &y1, &x2, &y2);
1560 cvtUserToDev(x1, y1, &x, &y);
1561 points[0].type = gfx_moveTo;
1562 points[0].x = points[4].x = x + user_movex + clipmovex;
1563 points[0].y = points[4].y = y + user_movey + clipmovey;
1564 points[0].next = &points[1];
1565 cvtUserToDev(x2, y1, &x, &y);
1566 points[1].type = gfx_lineTo;
1567 points[1].x = x + user_movex + clipmovex;
1568 points[1].y = y + user_movey + clipmovey;
1569 points[1].next = &points[2];
1570 cvtUserToDev(x2, y2, &x, &y);
1571 points[2].type = gfx_lineTo;
1572 points[2].x = x + user_movex + clipmovex;
1573 points[2].y = y + user_movey + clipmovey;
1574 points[2].next = &points[3];
1575 cvtUserToDev(x1, y2, &x, &y);
1576 points[3].type = gfx_lineTo;
1577 points[3].x = x + user_movex + clipmovex;
1578 points[3].y = y + user_movey + clipmovey;
1579 points[3].next = &points[4];
1580 cvtUserToDev(x1, y1, &x, &y);
1581 points[4].type = gfx_lineTo;
1582 points[4].x = x + user_movex + clipmovex;
1583 points[4].y = y + user_movey + clipmovey;
1586 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1587 points[0].x, points[0].y,
1588 points[1].x, points[1].y,
1589 points[2].x, points[2].y,
1590 points[3].x, points[3].y);
1592 if(getLogLevel() >= LOGLEVEL_TRACE) {
1593 dump_outline(points);
1596 LinkAction*action=link->getAction();
1599 const char*type = "-?-";
1602 msg("<trace> drawlink action=%d", action->getKind());
1603 switch(action->getKind())
1607 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1608 LinkDest *dest=NULL;
1609 if (ha->getDest()==NULL)
1610 dest=catalog->findDest(ha->getNamedDest());
1611 else dest=ha->getDest();
1613 if (dest->isPageRef()){
1614 Ref pageref=dest->getPageRef();
1615 page=catalog->findPage(pageref.num,pageref.gen);
1617 else page=dest->getPageNum();
1618 sprintf(buf, "%d", page);
1625 LinkGoToR*l = (LinkGoToR*)action;
1626 GString*g = l->getFileName();
1628 s = strdup(g->getCString());
1630 /* if the GoToR link has no filename, then
1631 try to find a refernce in the *local*
1633 GString*g = l->getNamedDest();
1635 s = strdup(g->getCString());
1641 LinkNamed*l = (LinkNamed*)action;
1642 GString*name = l->getName();
1644 s = strdup(name->lowerCase()->getCString());
1645 named = name->getCString();
1648 if(strstr(s, "next") || strstr(s, "forward"))
1650 page = currentpage + 1;
1652 else if(strstr(s, "prev") || strstr(s, "back"))
1654 page = currentpage - 1;
1656 else if(strstr(s, "last") || strstr(s, "end"))
1658 if(pages && pagepos>0)
1659 page = pages[pagepos-1];
1661 else if(strstr(s, "first") || strstr(s, "top"))
1669 case actionLaunch: {
1671 LinkLaunch*l = (LinkLaunch*)action;
1672 GString * str = new GString(l->getFileName());
1673 GString * params = l->getParams();
1675 str->append(params);
1676 s = strdup(str->getCString());
1683 LinkURI*l = (LinkURI*)action;
1684 GString*g = l->getURI();
1686 url = g->getCString();
1691 case actionUnknown: {
1693 LinkUnknown*l = (LinkUnknown*)action;
1698 msg("<error> Unknown link type!");
1703 if(!s) s = strdup("-?-");
1705 msg("<trace> drawlink s=%s", s);
1707 if(!linkinfo && (page || s))
1709 msg("<notice> File contains links");
1717 for(t=1;t<=pagepos;t++) {
1718 if(pages[t]==page) {
1727 sprintf(buf, "page%d", lpage);
1728 device->drawlink(device, points, buf);
1732 device->drawlink(device, points, s);
1735 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1739 void GFXOutputDev::saveState(GfxState *state) {
1740 dbg("saveState");dbgindent+=2;
1742 msg("<trace> saveState");
1745 msg("<error> Too many nested states in pdf.");
1749 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1750 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1751 states[statepos].clipping = 0;
1752 states[statepos].clipbbox = states[statepos-1].clipbbox;
1755 void GFXOutputDev::restoreState(GfxState *state) {
1756 dbgindent-=2; dbg("restoreState");
1759 msg("<error> Invalid restoreState");
1762 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1763 states[statepos].clipping?" (end clipping)":"");
1764 if(states[statepos].softmask) {
1765 clearSoftMask(state);
1768 while(states[statepos].clipping) {
1769 device->endclip(device);
1770 states[statepos].clipping--;
1775 void GFXOutputDev::updateLineDash(GfxState *state)
1777 state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1778 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1779 if(!this->dashLength) {
1780 this->dashPattern = 0;
1784 void GFXOutputDev::updateLineWidth(GfxState *state)
1786 double width = state->getTransformedLineWidth();
1789 void GFXOutputDev::updateLineCap(GfxState *state)
1791 int c = state->getLineCap();
1794 void GFXOutputDev::updateLineJoin(GfxState *state)
1796 int j = state->getLineJoin();
1799 void GFXOutputDev::updateFillColor(GfxState *state)
1802 double opaq = state->getFillOpacity();
1803 state->getFillRGB(&rgb);
1805 void GFXOutputDev::updateFillOpacity(GfxState *state)
1808 double opaq = state->getFillOpacity();
1809 state->getFillRGB(&rgb);
1810 dbg("update fillopaq %f", opaq);
1812 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1814 double opaq = state->getFillOpacity();
1815 dbg("update strokeopaq %f", opaq);
1817 void GFXOutputDev::updateFillOverprint(GfxState *state)
1819 double opaq = state->getFillOverprint();
1820 dbg("update filloverprint %f", opaq);
1822 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1824 double opaq = state->getStrokeOverprint();
1825 dbg("update strokeoverprint %f", opaq);
1827 void GFXOutputDev::updateTransfer(GfxState *state)
1829 dbg("update transfer");
1833 void GFXOutputDev::updateStrokeColor(GfxState *state)
1836 double opaq = state->getStrokeOpacity();
1837 state->getStrokeRGB(&rgb);
1841 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1843 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1844 memset(font, 0, sizeof(gfxfont_t));
1846 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1847 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1851 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1853 //printf("%d glyphs\n", font->num_glyphs);
1854 font->num_glyphs = 0;
1855 for(t=0;t<src->num_glyphs;t++) {
1856 if(src->glyphs[t]) {
1857 SplashPath*path = src->glyphs[t]->path;
1858 int len = path?path->getLength():0;
1859 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1860 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1861 src->glyphs[t]->glyphid = font->num_glyphs;
1862 glyph->unicode = src->glyphs[t]->unicode;
1863 if(glyph->unicode >= font->max_unicode)
1864 font->max_unicode = glyph->unicode+1;
1866 gfxdrawer_target_gfxline(&drawer);
1870 for(s=0;s<len;s++) {
1873 path->getPoint(s, &x, &y, &f);
1876 if(f&splashPathFirst) {
1877 drawer.moveTo(&drawer, x*scale, y*scale);
1879 if(f&splashPathCurve) {
1881 path->getPoint(++s, &x2, &y2, &f);
1882 if(f&splashPathCurve) {
1884 path->getPoint(++s, &x3, &y3, &f);
1885 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1887 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1890 drawer.lineTo(&drawer, x*scale, y*scale);
1892 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1893 // (f&splashPathFirst)?"first":"",
1894 // (f&splashPathLast)?"last":"");
1896 glyph->line = (gfxline_t*)drawer.result(&drawer);
1897 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1901 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1902 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1903 for(t=0;t<font->num_glyphs;t++) {
1904 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1905 font->unicode2glyph[font->glyphs[t].unicode] = t;
1909 msg("<trace> %d glyphs.", t, font->num_glyphs);
1913 void GFXOutputDev::updateFont(GfxState *state)
1915 GfxFont* gfxFont = state->getFont();
1919 char*id = getFontID(gfxFont);
1920 msg("<verbose> Updating font to %s", id);
1921 if(gfxFont->getType() == fontType3) {
1922 infofeature("Type3 fonts");
1923 if(!config_extrafontdata) {
1928 msg("<error> Internal Error: FontID is null");
1932 this->current_fontinfo = this->info->getFont(id);
1933 if(!this->current_fontinfo) {
1934 msg("<error> Internal Error: no fontinfo for font %s", id);
1937 if(!this->current_fontinfo->seen) {
1938 dumpFontInfo("<verbose>", gfxFont);
1941 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1943 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1944 font->id = strdup(id);
1945 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1947 device->addfont(device, font);
1949 current_gfxfont = font;
1952 updateFontMatrix(state);
1955 #define SQR(x) ((x)*(x))
1957 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1959 if((newwidth<2 || newheight<2) ||
1960 (width<=newwidth || height<=newheight))
1962 unsigned char*newdata;
1964 newdata= (unsigned char*)malloc(newwidth*newheight);
1965 double fx = (double)(width)/newwidth;
1966 double fy = (double)(height)/newheight;
1968 int blocksize = (int)(8192/(fx*fy));
1969 int r = 8192*256/palettesize;
1970 for(x=0;x<newwidth;x++) {
1971 double ex = px + fx;
1972 int fromx = (int)px;
1974 int xweight1 = (int)(((fromx+1)-px)*256);
1975 int xweight2 = (int)((ex-tox)*256);
1977 for(y=0;y<newheight;y++) {
1978 double ey = py + fy;
1979 int fromy = (int)py;
1981 int yweight1 = (int)(((fromy+1)-py)*256);
1982 int yweight2 = (int)((ey-toy)*256);
1985 for(xx=fromx;xx<=tox;xx++)
1986 for(yy=fromy;yy<=toy;yy++) {
1987 int b = 1-data[width*yy+xx];
1989 if(xx==fromx) weight = (weight*xweight1)/256;
1990 if(xx==tox) weight = (weight*xweight2)/256;
1991 if(yy==fromy) weight = (weight*yweight1)/256;
1992 if(yy==toy) weight = (weight*yweight2)/256;
1995 //if(a) a=(palettesize-1)*r/blocksize;
1996 newdata[y*newwidth+x] = (a*blocksize)/r;
2004 #define IMAGE_TYPE_JPEG 0
2005 #define IMAGE_TYPE_LOSSLESS 1
2007 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2008 double x1,double y1,
2009 double x2,double y2,
2010 double x3,double y3,
2011 double x4,double y4, int type)
2013 gfxcolor_t*newpic=0;
2015 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2016 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2018 gfxline_t p1,p2,p3,p4,p5;
2019 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2020 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2021 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2022 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2023 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2025 {p1.x = (int)(p1.x*20)/20.0;
2026 p1.y = (int)(p1.y*20)/20.0;
2027 p2.x = (int)(p2.x*20)/20.0;
2028 p2.y = (int)(p2.y*20)/20.0;
2029 p3.x = (int)(p3.x*20)/20.0;
2030 p3.y = (int)(p3.y*20)/20.0;
2031 p4.x = (int)(p4.x*20)/20.0;
2032 p4.y = (int)(p4.y*20)/20.0;
2033 p5.x = (int)(p5.x*20)/20.0;
2034 p5.y = (int)(p5.y*20)/20.0;
2038 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2039 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2044 img.data = (gfxcolor_t*)data;
2048 if(type == IMAGE_TYPE_JPEG)
2049 /* TODO: pass image_dpi to device instead */
2050 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2052 dev->fillbitmap(dev, &p1, &img, &m, 0);
2055 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2056 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2058 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2061 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2062 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2064 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2068 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2069 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2070 GBool inlineImg, int mask, int*maskColors,
2071 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2073 double x1,y1,x2,y2,x3,y3,x4,y4;
2074 ImageStream *imgStr;
2079 unsigned char* maskbitmap = 0;
2082 ncomps = colorMap->getNumPixelComps();
2083 bits = colorMap->getBits();
2088 unsigned char buf[8];
2089 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2091 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2092 imgMaskStr->reset();
2093 unsigned char pal[256];
2094 int n = 1 << colorMap->getBits();
2099 maskColorMap->getGray(pixBuf, &gray);
2100 pal[t] = colToByte(gray);
2102 for (y = 0; y < maskHeight; y++) {
2103 for (x = 0; x < maskWidth; x++) {
2104 imgMaskStr->getPixel(buf);
2105 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2110 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2111 imgMaskStr->reset();
2112 for (y = 0; y < maskHeight; y++) {
2113 for (x = 0; x < maskWidth; x++) {
2114 imgMaskStr->getPixel(buf);
2116 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2124 imgStr = new ImageStream(str, width, ncomps,bits);
2127 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2129 msg("<verbose> Ignoring %d by %d image", width, height);
2130 unsigned char buf[8];
2132 for (y = 0; y < height; ++y)
2133 for (x = 0; x < width; ++x) {
2134 imgStr->getPixel(buf);
2142 this->transformXY(state, 0, 1, &x1, &y1);
2143 this->transformXY(state, 0, 0, &x2, &y2);
2144 this->transformXY(state, 1, 0, &x3, &y3);
2145 this->transformXY(state, 1, 1, &x4, &y4);
2147 if(!pbminfo && !(str->getKind()==strDCT)) {
2149 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2153 msg("<verbose> drawing %d by %d masked picture", width, height);
2155 if(!jpeginfo && (str->getKind()==strDCT)) {
2156 msg("<notice> File contains jpeg pictures");
2161 unsigned char buf[8];
2163 unsigned char*pic = new unsigned char[width*height];
2164 gfxcolor_t pal[256];
2166 state->getFillRGB(&rgb);
2168 memset(pal,255,sizeof(pal));
2169 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2170 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2171 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2172 pal[0].a = 255; pal[1].a = 0;
2175 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2176 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2177 for (y = 0; y < height; ++y)
2178 for (x = 0; x < width; ++x)
2180 imgStr->getPixel(buf);
2183 pic[width*y+x] = buf[0];
2186 /* the size of the drawn image is added to the identifier
2187 as the same image may require different bitmaps if displayed
2188 at different sizes (due to antialiasing): */
2191 unsigned char*pic2 = 0;
2194 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2203 height = realheight;
2207 /* make a black/white palette */
2209 float r = 255./(float)(numpalette-1);
2211 for(t=0;t<numpalette;t++) {
2212 pal[t].r = colToByte(rgb.r);
2213 pal[t].g = colToByte(rgb.g);
2214 pal[t].b = colToByte(rgb.b);
2215 pal[t].a = (unsigned char)(t*r);
2219 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2220 for (y = 0; y < height; ++y) {
2221 for (x = 0; x < width; ++x) {
2222 pic2[width*y+x] = pal[pic[y*width+x]];
2225 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2229 if(maskbitmap) free(maskbitmap);
2235 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2236 gfxcolor_t*pic=new gfxcolor_t[width*height];
2237 for (y = 0; y < height; ++y) {
2238 for (x = 0; x < width; ++x) {
2239 imgStr->getPixel(pixBuf);
2240 colorMap->getRGB(pixBuf, &rgb);
2241 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2242 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2243 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2244 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2246 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2250 if(str->getKind()==strDCT)
2251 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2253 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2256 if(maskbitmap) free(maskbitmap);
2259 gfxcolor_t*pic=new gfxcolor_t[width*height];
2260 gfxcolor_t pal[256];
2261 int n = 1 << colorMap->getBits();
2263 for(t=0;t<256;t++) {
2265 colorMap->getRGB(pixBuf, &rgb);
2267 {/*if(maskColors && *maskColors==t) {
2268 msg("<notice> Color %d is transparent", t);
2269 if (imgData->maskColors) {
2271 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2272 if (pix[i] < imgData->maskColors[2*i] ||
2273 pix[i] > imgData->maskColors[2*i+1]) {
2288 pal[t].r = (unsigned char)(colToByte(rgb.r));
2289 pal[t].g = (unsigned char)(colToByte(rgb.g));
2290 pal[t].b = (unsigned char)(colToByte(rgb.b));
2291 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2294 for (y = 0; y < height; ++y) {
2295 for (x = 0; x < width; ++x) {
2296 imgStr->getPixel(pixBuf);
2297 pic[width*y+x] = pal[pixBuf[0]];
2301 if(maskWidth < width && maskHeight < height) {
2302 for(y = 0; y < height; y++) {
2303 for (x = 0; x < width; x++) {
2304 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2308 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2309 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2310 double dx = width / maskWidth;
2311 double dy = height / maskHeight;
2313 for(y = 0; y < maskHeight; y++) {
2315 for (x = 0; x < maskWidth; x++) {
2316 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2317 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2325 height = maskHeight;
2328 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2332 if(maskbitmap) free(maskbitmap);
2337 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2338 int width, int height, GBool invert,
2341 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2342 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2343 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2346 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2347 int width, int height, GfxImageColorMap *colorMap,
2348 int *maskColors, GBool inlineImg)
2350 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2351 colorMap?"colorMap":"no colorMap",
2352 maskColors?"maskColors":"no maskColors",
2354 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2355 colorMap?"colorMap":"no colorMap",
2356 maskColors?"maskColors":"no maskColors",
2359 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2360 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2361 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2364 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2365 int width, int height,
2366 GfxImageColorMap *colorMap,
2367 Stream *maskStr, int maskWidth, int maskHeight,
2370 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2371 colorMap?"colorMap":"no colorMap",
2372 maskWidth, maskHeight);
2373 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2374 colorMap?"colorMap":"no colorMap",
2375 maskWidth, maskHeight);
2377 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2378 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2379 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2382 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2383 int width, int height,
2384 GfxImageColorMap *colorMap,
2386 int maskWidth, int maskHeight,
2387 GfxImageColorMap *maskColorMap)
2389 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2390 colorMap?"colorMap":"no colorMap",
2391 maskWidth, maskHeight);
2392 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2393 colorMap?"colorMap":"no colorMap",
2394 maskWidth, maskHeight);
2396 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2397 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2398 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2401 void GFXOutputDev::stroke(GfxState *state)
2405 GfxPath * path = state->getPath();
2406 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2407 strokeGfxline(state, line, 0);
2411 void GFXOutputDev::fill(GfxState *state)
2413 gfxcolor_t col = getFillColor(state);
2414 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2416 GfxPath * path = state->getPath();
2417 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2418 if(config_optimize_polygons) {
2419 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2423 fillGfxLine(state, line);
2427 void GFXOutputDev::eoFill(GfxState *state)
2429 gfxcolor_t col = getFillColor(state);
2430 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2432 GfxPath * path = state->getPath();
2433 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2434 fillGfxLine(state, line);
2439 static const char* dirseparator()
2448 void addGlobalFont(const char*filename)
2450 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2451 memset(f, 0, sizeof(fontfile_t));
2452 f->filename = filename;
2453 int len = strlen(filename);
2454 char*r1 = strrchr(filename, '/');
2455 char*r2 = strrchr(filename, '\\');
2463 msg("<notice> Adding font \"%s\".", filename);
2464 if(global_fonts_next) {
2465 global_fonts_next->next = f;
2466 global_fonts_next = global_fonts_next->next;
2468 global_fonts_next = global_fonts = f;
2472 void addGlobalLanguageDir(const char*dir)
2474 msg("<notice> Adding %s to language pack directories", dir);
2477 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2478 strcpy(config_file, dir);
2479 strcat(config_file, dirseparator());
2480 strcat(config_file, "add-to-xpdfrc");
2482 fi = fopen(config_file, "rb");
2484 msg("<error> Could not open %s", config_file);
2487 globalParams->parseFile(new GString(config_file), fi);
2491 void addGlobalFontDir(const char*dirname)
2493 #ifdef HAVE_DIRENT_H
2494 msg("<notice> Adding %s to font directories", dirname);
2495 lastfontdir = strdup(dirname);
2496 DIR*dir = opendir(dirname);
2498 msg("<warning> Couldn't open directory %s", dirname);
2503 ent = readdir (dir);
2507 char*name = ent->d_name;
2513 if(!strncasecmp(&name[l-4], ".pfa", 4))
2515 if(!strncasecmp(&name[l-4], ".pfb", 4))
2517 if(!strncasecmp(&name[l-4], ".ttf", 4))
2520 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2521 strcpy(fontname, dirname);
2522 strcat(fontname, dirseparator());
2523 strcat(fontname, name);
2524 addGlobalFont(fontname);
2529 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2533 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2539 this->pagebuflen = 1024;
2540 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2541 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2543 while(pdfpage >= this->pagebuflen)
2545 int oldlen = this->pagebuflen;
2546 this->pagebuflen+=1024;
2547 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2548 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2551 this->pages[pdfpage] = outputpage;
2552 if(pdfpage>this->pagepos)
2553 this->pagepos = pdfpage;
2556 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2557 GfxColorSpace *blendingColorSpace,
2558 GBool isolated, GBool knockout,
2561 const char*colormodename = "";
2563 if(blendingColorSpace) {
2564 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2566 dbg("beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2567 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);
2569 //states[statepos].createsoftmask |= forSoftMask;
2570 states[statepos].createsoftmask = forSoftMask;
2571 states[statepos].transparencygroup = !forSoftMask;
2572 states[statepos].isolated = isolated;
2574 states[statepos].olddevice = this->device;
2575 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2577 gfxdevice_record_init(this->device);
2579 /*if(!forSoftMask) { ////???
2580 state->setFillOpacity(0.0);
2585 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2588 gfxdevice_t*r = this->device;
2590 this->device = states[statepos].olddevice;
2592 gfxresult_t*recording = r->finish(r);
2594 dbg("endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2595 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2597 if(states[statepos].createsoftmask) {
2598 states[statepos-1].softmaskrecording = recording;
2600 states[statepos-1].grouprecording = recording;
2603 states[statepos].createsoftmask = 0;
2604 states[statepos].transparencygroup = 0;
2608 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2610 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2611 "colordodge","colorburn","hardlight","softlight","difference",
2612 "exclusion","hue","saturation","color","luminosity"};
2614 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2615 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2617 if(state->getBlendMode() == gfxBlendNormal)
2618 infofeature("transparency groups");
2621 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2622 warnfeature(buffer, 0);
2625 gfxresult_t*grouprecording = states[statepos].grouprecording;
2627 int blendmode = state->getBlendMode();
2628 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2629 int alpha = (int)(state->getFillOpacity()*255);
2630 if(blendmode == gfxBlendMultiply && alpha>200)
2633 gfxdevice_ops_init(&ops, this->device, alpha);
2634 gfxresult_record_replay(grouprecording, &ops);
2637 grouprecording->destroy(grouprecording);
2639 states[statepos].grouprecording = 0;
2642 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2644 /* alpha = 1: retrieve mask values from alpha layer
2645 alpha = 0: retrieve mask values from luminance */
2646 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2647 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2648 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2649 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2651 infofeature("soft masks");
2653 warnfeature("soft masks from alpha channel",0);
2655 states[statepos].olddevice = this->device;
2656 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2657 gfxdevice_record_init(this->device);
2659 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2661 states[statepos].softmask = 1;
2662 states[statepos].softmask_alpha = alpha;
2665 static inline Guchar div255(int x) {
2666 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2669 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2671 if(c < min) c = min;
2672 if(c > max) c = max;
2676 void GFXOutputDev::clearSoftMask(GfxState *state)
2678 if(!states[statepos].softmask)
2680 states[statepos].softmask = 0;
2681 dbg("clearSoftMask statepos=%d", statepos);
2682 msg("<verbose> clearSoftMask");
2684 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2685 msg("<error> Error in softmask/tgroup ordering");
2689 gfxresult_t*mask = states[statepos].softmaskrecording;
2690 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2691 this->device = states[statepos].olddevice;
2693 /* get outline of all objects below the soft mask */
2694 gfxdevice_t uniondev;
2695 gfxdevice_union_init(&uniondev, 0);
2696 gfxresult_record_replay(below, &uniondev);
2697 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2698 uniondev.finish(&uniondev);
2699 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2700 gfxline_free(belowoutline);belowoutline=0;
2702 this->device->startclip(this->device, belowoutline);
2703 gfxresult_record_replay(below, this->device);
2704 gfxresult_record_replay(mask, this->device);
2705 this->device->endclip(this->device);
2708 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2709 if(width<=0 || height<=0)
2712 gfxdevice_t belowrender;
2713 gfxdevice_render_init(&belowrender);
2714 if(states[statepos+1].isolated) {
2715 belowrender.setparameter(&belowrender, "fillwhite", "1");
2717 belowrender.setparameter(&belowrender, "antialize", "2");
2718 belowrender.startpage(&belowrender, width, height);
2719 gfxresult_record_replay(below, &belowrender);
2720 belowrender.endpage(&belowrender);
2721 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2722 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2723 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2725 gfxdevice_t maskrender;
2726 gfxdevice_render_init(&maskrender);
2727 maskrender.startpage(&maskrender, width, height);
2728 gfxresult_record_replay(mask, &maskrender);
2729 maskrender.endpage(&maskrender);
2730 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2731 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2733 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2734 msg("<fatal> Internal error in mask drawing");
2739 for(y=0;y<height;y++) {
2740 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2741 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2742 for(x=0;x<width;x++) {
2744 if(states[statepos].softmask_alpha) {
2747 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2750 l2->a = div255(alpha*l2->a);
2752 /* DON'T premultiply alpha- this is done by fillbitmap,
2753 depending on the output device */
2754 //l2->r = div255(alpha*l2->r);
2755 //l2->g = div255(alpha*l2->g);
2756 //l2->b = div255(alpha*l2->b);
2762 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2765 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2766 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2768 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2770 mask->destroy(mask);
2771 below->destroy(below);
2772 maskresult->destroy(maskresult);
2773 belowresult->destroy(belowresult);
2774 states[statepos].softmaskrecording = 0;
2779 // public: ~MemCheck()
2781 // delete globalParams;globalParams=0;
2782 // Object::memCheck(stderr);
2783 // gMemReport(stderr);