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"
76 #include "../art/libart.h"
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);
1031 #define STROKE_FILL 1
1032 #define STROKE_CLIP 2
1033 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1035 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1036 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1037 double miterLimit = state->getMiterLimit();
1038 double width = state->getTransformedLineWidth();
1041 double opaq = state->getStrokeOpacity();
1043 state->getFillRGB(&rgb);
1045 state->getStrokeRGB(&rgb);
1047 col.r = colToByte(rgb.r);
1048 col.g = colToByte(rgb.g);
1049 col.b = colToByte(rgb.b);
1050 col.a = (unsigned char)(opaq*255);
1052 gfx_capType capType = gfx_capRound;
1053 if(lineCap == 0) capType = gfx_capButt;
1054 else if(lineCap == 1) capType = gfx_capRound;
1055 else if(lineCap == 2) capType = gfx_capSquare;
1057 gfx_joinType joinType = gfx_joinRound;
1058 if(lineJoin == 0) joinType = gfx_joinMiter;
1059 else if(lineJoin == 1) joinType = gfx_joinRound;
1060 else if(lineJoin == 2) joinType = gfx_joinBevel;
1063 double dashphase = 0;
1065 state->getLineDash(&ldash, &dashnum, &dashphase);
1067 gfxline_t*line2 = 0;
1069 if(dashnum && ldash) {
1070 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
1072 msg("<trace> %d dashes", dashnum);
1073 msg("<trace> | phase: %f", dashphase);
1074 for(t=0;t<dashnum;t++) {
1075 dash[t] = (float)ldash[t];
1076 msg("<trace> | d%-3d: %f", t, ldash[t]);
1079 if(getLogLevel() >= LOGLEVEL_TRACE) {
1083 line2 = gfxtool_dash_line(line, dash, (float)dashphase);
1086 msg("<trace> After dashing:");
1089 if(getLogLevel() >= LOGLEVEL_TRACE) {
1090 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1092 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1093 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1095 col.r,col.g,col.b,col.a
1100 if(flags&STROKE_FILL) {
1101 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1102 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1103 if(getLogLevel() >= LOGLEVEL_TRACE) {
1104 dump_outline(gfxline);
1107 msg("<warning> Empty polygon (resulting from stroked line)");
1109 if(flags&STROKE_CLIP) {
1110 device->startclip(device, gfxline);
1111 states[statepos].clipping++;
1113 device->fill(device, gfxline, &col);
1118 if(flags&STROKE_CLIP)
1119 msg("<error> Stroke&clip not supported at the same time");
1120 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1124 gfxline_free(line2);
1127 gfxcolor_t getFillColor(GfxState * state)
1130 double opaq = state->getFillOpacity();
1131 state->getFillRGB(&rgb);
1133 col.r = colToByte(rgb.r);
1134 col.g = colToByte(rgb.g);
1135 col.b = colToByte(rgb.b);
1136 col.a = (unsigned char)(opaq*255);
1140 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1142 gfxcolor_t col = getFillColor(state);
1144 if(getLogLevel() >= LOGLEVEL_TRACE) {
1145 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1148 device->fill(device, line, &col);
1151 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1153 if(getLogLevel() >= LOGLEVEL_TRACE) {
1156 gfxbbox_t bbox = gfxline_getbbox(line);
1157 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1159 device->startclip(device, line);
1160 states[statepos].clipping++;
1163 void GFXOutputDev::clip(GfxState *state)
1165 GfxPath * path = state->getPath();
1166 msg("<trace> clip");
1167 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1168 if(config_optimize_polygons) {
1169 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1173 clipToGfxLine(state, line);
1177 void GFXOutputDev::eoClip(GfxState *state)
1179 GfxPath * path = state->getPath();
1180 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1181 clipToGfxLine(state, line);
1184 void GFXOutputDev::clipToStrokePath(GfxState *state)
1186 GfxPath * path = state->getPath();
1187 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1189 if(getLogLevel() >= LOGLEVEL_TRACE) {
1190 msg("<trace> cliptostrokepath");
1194 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1198 void GFXOutputDev::finish()
1200 if(outer_clip_box) {
1202 device->endclip(device);
1208 GFXOutputDev::~GFXOutputDev()
1213 free(this->pages); this->pages = 0;
1216 feature_t*f = this->featurewarnings;
1218 feature_t*next = f->next;
1220 free(f->string);f->string =0;
1226 this->featurewarnings = 0;
1228 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1230 GBool GFXOutputDev::upsideDown()
1234 GBool GFXOutputDev::useDrawChar()
1239 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1240 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1242 static char tmp_printstr[4096];
1243 char* makeStringPrintable(char*str)
1245 int len = strlen(str);
1252 for(t=0;t<len;t++) {
1257 tmp_printstr[t] = c;
1260 tmp_printstr[len++] = '.';
1261 tmp_printstr[len++] = '.';
1262 tmp_printstr[len++] = '.';
1264 tmp_printstr[len] = 0;
1265 return tmp_printstr;
1267 #define INTERNAL_FONT_SIZE 1024.0
1268 void GFXOutputDev::updateFontMatrix(GfxState*state)
1270 double* ctm = state->getCTM();
1271 double fontSize = state->getFontSize();
1272 double*textMat = state->getTextMat();
1274 /* taking the absolute value of horizScaling seems to be required for
1275 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1276 double hscale = fabs(state->getHorizScaling());
1278 // from xpdf-3.02/SplashOutputDev:updateFont
1279 double mm11 = textMat[0] * fontSize * hscale;
1280 double mm12 = textMat[1] * fontSize * hscale;
1281 double mm21 = textMat[2] * fontSize;
1282 double mm22 = textMat[3] * fontSize;
1284 // multiply with ctm, like state->getFontTransMat() does
1285 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1286 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1287 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1288 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1289 this->current_font_matrix.tx = 0;
1290 this->current_font_matrix.ty = 0;
1293 void GFXOutputDev::beginString(GfxState *state, GString *s)
1295 int render = state->getRender();
1296 if(current_text_stroke) {
1297 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1300 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1303 static gfxline_t* mkEmptyGfxShape(double x, double y)
1305 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1306 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1310 static char isValidUnicode(int c)
1312 if(c>=32 && c<0x2fffe)
1317 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1318 double dx, double dy,
1319 double originX, double originY,
1320 CharCode charid, int nBytes, Unicode *_u, int uLen)
1322 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1323 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1327 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1329 int render = state->getRender();
1330 gfxcolor_t col = getFillColor(state);
1332 // check for invisible text -- this is used by Acrobat Capture
1333 if (render == RENDER_INVISIBLE) {
1335 if(!config_extrafontdata)
1339 GfxFont*font = state->getFont();
1341 if(font->getType() == fontType3) {
1342 /* type 3 chars are passed as graphics */
1343 msg("<debug> type3 char at %f/%f", x, y);
1347 Unicode u = uLen?(_u[0]):0;
1348 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);
1350 gfxmatrix_t m = this->current_font_matrix;
1351 this->transformXY(state, x, y, &m.tx, &m.ty);
1353 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1354 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1356 msg("<debug> Drawing glyph %d as shape", charid);
1358 msg("<notice> Some texts will be rendered as shape");
1361 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1362 gfxline_t*tglyph = gfxline_clone(glyph);
1363 gfxline_transform(tglyph, &m);
1364 if((render&3) != RENDER_INVISIBLE) {
1365 gfxline_t*add = gfxline_clone(tglyph);
1366 current_text_stroke = gfxline_append(current_text_stroke, add);
1368 if(render&RENDER_CLIP) {
1369 gfxline_t*add = gfxline_clone(tglyph);
1370 current_text_clip = gfxline_append(current_text_clip, add);
1371 if(!current_text_clip) {
1372 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1375 gfxline_free(tglyph);
1379 void GFXOutputDev::endString(GfxState *state)
1381 int render = state->getRender();
1382 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1384 if(current_text_stroke) {
1385 /* fillstroke and stroke text rendering objects we can process right
1386 now (as there may be texts of other rendering modes in this
1387 text object)- clipping objects have to wait until endTextObject,
1389 device->setparameter(device, "mark","TXT");
1390 if((render&3) == RENDER_FILL) {
1391 fillGfxLine(state, current_text_stroke);
1392 gfxline_free(current_text_stroke);
1393 current_text_stroke = 0;
1394 } else if((render&3) == RENDER_FILLSTROKE) {
1395 fillGfxLine(state, current_text_stroke);
1396 strokeGfxline(state, current_text_stroke,0);
1397 gfxline_free(current_text_stroke);
1398 current_text_stroke = 0;
1399 } else if((render&3) == RENDER_STROKE) {
1400 strokeGfxline(state, current_text_stroke,0);
1401 gfxline_free(current_text_stroke);
1402 current_text_stroke = 0;
1404 device->setparameter(device, "mark","");
1408 void GFXOutputDev::endTextObject(GfxState *state)
1410 int render = state->getRender();
1411 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1413 if(current_text_clip) {
1414 device->setparameter(device, "mark","TXT");
1415 clipToGfxLine(state, current_text_clip);
1416 device->setparameter(device, "mark","");
1417 gfxline_free(current_text_clip);
1418 current_text_clip = 0;
1422 /* the logic seems to be as following:
1423 first, beginType3Char is called, with the charcode and the coordinates.
1424 if this function returns true, it already knew about the char and has now drawn it.
1425 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1426 called with some parameters.
1427 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1428 at the position first passed to beginType3Char). the char ends with endType3Char.
1430 The drawing operations between beginType3Char and endType3Char are somewhat different to
1431 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1432 color determines the color of a font)
1435 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1437 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1440 if(config_extrafontdata && current_fontinfo) {
1442 gfxmatrix_t m = this->current_font_matrix;
1443 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1444 m.m00*=INTERNAL_FONT_SIZE;
1445 m.m01*=INTERNAL_FONT_SIZE;
1446 m.m10*=INTERNAL_FONT_SIZE;
1447 m.m11*=INTERNAL_FONT_SIZE;
1449 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1450 msg("<error> Invalid charid %d for font", charid);
1453 gfxcolor_t col={0,0,0,0};
1454 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1455 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1459 /* the character itself is going to be passed using the draw functions */
1460 return gFalse; /* gTrue= is_in_cache? */
1463 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1465 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1468 void GFXOutputDev::endType3Char(GfxState *state)
1471 msg("<debug> endType3Char");
1474 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1476 this->currentpage = pageNum;
1478 int rot = doc->getPageRotate(1);
1479 gfxcolor_t white = {255,255,255,255};
1480 gfxcolor_t black = {255,0,0,0};
1482 gfxline_t clippath[5];
1484 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1485 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1486 Use CropBox, not MediaBox, as page size
1493 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1494 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1496 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1497 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1499 this->clipmovex = -(int)x1;
1500 this->clipmovey = -(int)y1;
1502 /* apply user clip box */
1503 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1504 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1505 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1506 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1507 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1508 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1511 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1513 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);
1515 msg("<verbose> page is rotated %d degrees", rot);
1517 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1518 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1519 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1520 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1521 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1522 device->startclip(device, clippath); outer_clip_box = 1;
1523 if(!config_transparent) {
1524 device->fill(device, clippath, &white);
1526 states[statepos].clipbbox.xmin = x1;
1527 states[statepos].clipbbox.ymin = x1;
1528 states[statepos].clipbbox.xmax = x2;
1529 states[statepos].clipbbox.ymax = y2;
1533 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1535 double x1, y1, x2, y2;
1536 gfxline_t points[5];
1539 msg("<debug> drawlink");
1541 link->getRect(&x1, &y1, &x2, &y2);
1542 cvtUserToDev(x1, y1, &x, &y);
1543 points[0].type = gfx_moveTo;
1544 points[0].x = points[4].x = x + user_movex + clipmovex;
1545 points[0].y = points[4].y = y + user_movey + clipmovey;
1546 points[0].next = &points[1];
1547 cvtUserToDev(x2, y1, &x, &y);
1548 points[1].type = gfx_lineTo;
1549 points[1].x = x + user_movex + clipmovex;
1550 points[1].y = y + user_movey + clipmovey;
1551 points[1].next = &points[2];
1552 cvtUserToDev(x2, y2, &x, &y);
1553 points[2].type = gfx_lineTo;
1554 points[2].x = x + user_movex + clipmovex;
1555 points[2].y = y + user_movey + clipmovey;
1556 points[2].next = &points[3];
1557 cvtUserToDev(x1, y2, &x, &y);
1558 points[3].type = gfx_lineTo;
1559 points[3].x = x + user_movex + clipmovex;
1560 points[3].y = y + user_movey + clipmovey;
1561 points[3].next = &points[4];
1562 cvtUserToDev(x1, y1, &x, &y);
1563 points[4].type = gfx_lineTo;
1564 points[4].x = x + user_movex + clipmovex;
1565 points[4].y = y + user_movey + clipmovey;
1568 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1569 points[0].x, points[0].y,
1570 points[1].x, points[1].y,
1571 points[2].x, points[2].y,
1572 points[3].x, points[3].y);
1574 if(getLogLevel() >= LOGLEVEL_TRACE) {
1575 dump_outline(points);
1578 LinkAction*action=link->getAction();
1581 const char*type = "-?-";
1584 msg("<trace> drawlink action=%d", action->getKind());
1585 switch(action->getKind())
1589 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1590 LinkDest *dest=NULL;
1591 if (ha->getDest()==NULL)
1592 dest=catalog->findDest(ha->getNamedDest());
1593 else dest=ha->getDest();
1595 if (dest->isPageRef()){
1596 Ref pageref=dest->getPageRef();
1597 page=catalog->findPage(pageref.num,pageref.gen);
1599 else page=dest->getPageNum();
1600 sprintf(buf, "%d", page);
1607 LinkGoToR*l = (LinkGoToR*)action;
1608 GString*g = l->getFileName();
1610 s = strdup(g->getCString());
1612 /* if the GoToR link has no filename, then
1613 try to find a refernce in the *local*
1615 GString*g = l->getNamedDest();
1617 s = strdup(g->getCString());
1623 LinkNamed*l = (LinkNamed*)action;
1624 GString*name = l->getName();
1626 s = strdup(name->lowerCase()->getCString());
1627 named = name->getCString();
1630 if(strstr(s, "next") || strstr(s, "forward"))
1632 page = currentpage + 1;
1634 else if(strstr(s, "prev") || strstr(s, "back"))
1636 page = currentpage - 1;
1638 else if(strstr(s, "last") || strstr(s, "end"))
1640 if(pages && pagepos>0)
1641 page = pages[pagepos-1];
1643 else if(strstr(s, "first") || strstr(s, "top"))
1651 case actionLaunch: {
1653 LinkLaunch*l = (LinkLaunch*)action;
1654 GString * str = new GString(l->getFileName());
1655 GString * params = l->getParams();
1657 str->append(params);
1658 s = strdup(str->getCString());
1665 LinkURI*l = (LinkURI*)action;
1666 GString*g = l->getURI();
1668 url = g->getCString();
1673 case actionUnknown: {
1675 LinkUnknown*l = (LinkUnknown*)action;
1680 msg("<error> Unknown link type!");
1685 if(!s) s = strdup("-?-");
1687 msg("<trace> drawlink s=%s", s);
1689 if(!linkinfo && (page || s))
1691 msg("<notice> File contains links");
1699 for(t=1;t<=pagepos;t++) {
1700 if(pages[t]==page) {
1709 sprintf(buf, "page%d", lpage);
1710 device->drawlink(device, points, buf);
1714 device->drawlink(device, points, s);
1717 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1721 void GFXOutputDev::saveState(GfxState *state) {
1722 dbg("saveState");dbgindent+=2;
1724 msg("<trace> saveState");
1727 msg("<error> Too many nested states in pdf.");
1731 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1732 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1733 states[statepos].clipping = 0;
1734 states[statepos].clipbbox = states[statepos-1].clipbbox;
1737 void GFXOutputDev::restoreState(GfxState *state) {
1738 dbgindent-=2; dbg("restoreState");
1741 msg("<error> Invalid restoreState");
1744 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1745 states[statepos].clipping?" (end clipping)":"");
1746 if(states[statepos].softmask) {
1747 clearSoftMask(state);
1750 while(states[statepos].clipping) {
1751 device->endclip(device);
1752 states[statepos].clipping--;
1757 void GFXOutputDev::updateLineWidth(GfxState *state)
1759 double width = state->getTransformedLineWidth();
1760 //swfoutput_setlinewidth(&device, width);
1763 void GFXOutputDev::updateLineCap(GfxState *state)
1765 int c = state->getLineCap();
1768 void GFXOutputDev::updateLineJoin(GfxState *state)
1770 int j = state->getLineJoin();
1773 void GFXOutputDev::updateFillColor(GfxState *state)
1776 double opaq = state->getFillOpacity();
1777 state->getFillRGB(&rgb);
1779 void GFXOutputDev::updateFillOpacity(GfxState *state)
1782 double opaq = state->getFillOpacity();
1783 state->getFillRGB(&rgb);
1784 dbg("update fillopaq %f", opaq);
1786 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1788 double opaq = state->getFillOpacity();
1789 dbg("update strokeopaq %f", opaq);
1791 void GFXOutputDev::updateFillOverprint(GfxState *state)
1793 double opaq = state->getFillOverprint();
1794 dbg("update filloverprint %f", opaq);
1796 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1798 double opaq = state->getStrokeOverprint();
1799 dbg("update strokeoverprint %f", opaq);
1801 void GFXOutputDev::updateTransfer(GfxState *state)
1803 dbg("update transfer");
1807 void GFXOutputDev::updateStrokeColor(GfxState *state)
1810 double opaq = state->getStrokeOpacity();
1811 state->getStrokeRGB(&rgb);
1815 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1817 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1818 memset(font, 0, sizeof(gfxfont_t));
1820 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1821 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1825 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1827 //printf("%d glyphs\n", font->num_glyphs);
1828 font->num_glyphs = 0;
1829 for(t=0;t<src->num_glyphs;t++) {
1830 if(src->glyphs[t]) {
1831 SplashPath*path = src->glyphs[t]->path;
1832 int len = path?path->getLength():0;
1833 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1834 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1835 src->glyphs[t]->glyphid = font->num_glyphs;
1836 glyph->unicode = src->glyphs[t]->unicode;
1837 if(glyph->unicode >= font->max_unicode)
1838 font->max_unicode = glyph->unicode+1;
1840 gfxdrawer_target_gfxline(&drawer);
1844 for(s=0;s<len;s++) {
1847 path->getPoint(s, &x, &y, &f);
1850 if(f&splashPathFirst) {
1851 drawer.moveTo(&drawer, x*scale, y*scale);
1853 if(f&splashPathCurve) {
1855 path->getPoint(++s, &x2, &y2, &f);
1856 if(f&splashPathCurve) {
1858 path->getPoint(++s, &x3, &y3, &f);
1859 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1861 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1864 drawer.lineTo(&drawer, x*scale, y*scale);
1866 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1867 // (f&splashPathFirst)?"first":"",
1868 // (f&splashPathLast)?"last":"");
1870 glyph->line = (gfxline_t*)drawer.result(&drawer);
1871 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1875 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1876 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1877 for(t=0;t<font->num_glyphs;t++) {
1878 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1879 font->unicode2glyph[font->glyphs[t].unicode] = t;
1883 msg("<trace> %d glyphs.", t, font->num_glyphs);
1887 void GFXOutputDev::updateFont(GfxState *state)
1889 GfxFont* gfxFont = state->getFont();
1893 char*id = getFontID(gfxFont);
1894 msg("<verbose> Updating font to %s", id);
1895 if(gfxFont->getType() == fontType3) {
1896 infofeature("Type3 fonts");
1897 if(!config_extrafontdata) {
1902 msg("<error> Internal Error: FontID is null");
1906 this->current_fontinfo = this->info->getFont(id);
1907 if(!this->current_fontinfo) {
1908 msg("<error> Internal Error: no fontinfo for font %s", id);
1911 if(!this->current_fontinfo->seen) {
1912 dumpFontInfo("<verbose>", gfxFont);
1915 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1917 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1918 font->id = strdup(id);
1919 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1921 device->addfont(device, font);
1923 current_gfxfont = font;
1926 updateFontMatrix(state);
1929 #define SQR(x) ((x)*(x))
1931 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1933 if((newwidth<2 || newheight<2) ||
1934 (width<=newwidth || height<=newheight))
1936 unsigned char*newdata;
1938 newdata= (unsigned char*)malloc(newwidth*newheight);
1939 double fx = (double)(width)/newwidth;
1940 double fy = (double)(height)/newheight;
1942 int blocksize = (int)(8192/(fx*fy));
1943 int r = 8192*256/palettesize;
1944 for(x=0;x<newwidth;x++) {
1945 double ex = px + fx;
1946 int fromx = (int)px;
1948 int xweight1 = (int)(((fromx+1)-px)*256);
1949 int xweight2 = (int)((ex-tox)*256);
1951 for(y=0;y<newheight;y++) {
1952 double ey = py + fy;
1953 int fromy = (int)py;
1955 int yweight1 = (int)(((fromy+1)-py)*256);
1956 int yweight2 = (int)((ey-toy)*256);
1959 for(xx=fromx;xx<=tox;xx++)
1960 for(yy=fromy;yy<=toy;yy++) {
1961 int b = 1-data[width*yy+xx];
1963 if(xx==fromx) weight = (weight*xweight1)/256;
1964 if(xx==tox) weight = (weight*xweight2)/256;
1965 if(yy==fromy) weight = (weight*yweight1)/256;
1966 if(yy==toy) weight = (weight*yweight2)/256;
1969 //if(a) a=(palettesize-1)*r/blocksize;
1970 newdata[y*newwidth+x] = (a*blocksize)/r;
1978 #define IMAGE_TYPE_JPEG 0
1979 #define IMAGE_TYPE_LOSSLESS 1
1981 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1982 double x1,double y1,
1983 double x2,double y2,
1984 double x3,double y3,
1985 double x4,double y4, int type)
1987 gfxcolor_t*newpic=0;
1989 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1990 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1992 gfxline_t p1,p2,p3,p4,p5;
1993 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1994 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1995 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1996 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1997 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1999 {p1.x = (int)(p1.x*20)/20.0;
2000 p1.y = (int)(p1.y*20)/20.0;
2001 p2.x = (int)(p2.x*20)/20.0;
2002 p2.y = (int)(p2.y*20)/20.0;
2003 p3.x = (int)(p3.x*20)/20.0;
2004 p3.y = (int)(p3.y*20)/20.0;
2005 p4.x = (int)(p4.x*20)/20.0;
2006 p4.y = (int)(p4.y*20)/20.0;
2007 p5.x = (int)(p5.x*20)/20.0;
2008 p5.y = (int)(p5.y*20)/20.0;
2012 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2013 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2018 img.data = (gfxcolor_t*)data;
2022 if(type == IMAGE_TYPE_JPEG)
2023 /* TODO: pass image_dpi to device instead */
2024 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2026 dev->fillbitmap(dev, &p1, &img, &m, 0);
2029 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2030 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2032 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2035 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2036 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2038 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2042 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2043 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2044 GBool inlineImg, int mask, int*maskColors,
2045 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2047 double x1,y1,x2,y2,x3,y3,x4,y4;
2048 ImageStream *imgStr;
2053 unsigned char* maskbitmap = 0;
2056 ncomps = colorMap->getNumPixelComps();
2057 bits = colorMap->getBits();
2062 unsigned char buf[8];
2063 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2065 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2066 imgMaskStr->reset();
2067 unsigned char pal[256];
2068 int n = 1 << colorMap->getBits();
2073 maskColorMap->getGray(pixBuf, &gray);
2074 pal[t] = colToByte(gray);
2076 for (y = 0; y < maskHeight; y++) {
2077 for (x = 0; x < maskWidth; x++) {
2078 imgMaskStr->getPixel(buf);
2079 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2084 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2085 imgMaskStr->reset();
2086 for (y = 0; y < maskHeight; y++) {
2087 for (x = 0; x < maskWidth; x++) {
2088 imgMaskStr->getPixel(buf);
2090 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2098 imgStr = new ImageStream(str, width, ncomps,bits);
2101 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2103 msg("<verbose> Ignoring %d by %d image", width, height);
2104 unsigned char buf[8];
2106 for (y = 0; y < height; ++y)
2107 for (x = 0; x < width; ++x) {
2108 imgStr->getPixel(buf);
2116 this->transformXY(state, 0, 1, &x1, &y1);
2117 this->transformXY(state, 0, 0, &x2, &y2);
2118 this->transformXY(state, 1, 0, &x3, &y3);
2119 this->transformXY(state, 1, 1, &x4, &y4);
2121 if(!pbminfo && !(str->getKind()==strDCT)) {
2123 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2127 msg("<verbose> drawing %d by %d masked picture", width, height);
2129 if(!jpeginfo && (str->getKind()==strDCT)) {
2130 msg("<notice> File contains jpeg pictures");
2135 unsigned char buf[8];
2137 unsigned char*pic = new unsigned char[width*height];
2138 gfxcolor_t pal[256];
2140 state->getFillRGB(&rgb);
2142 memset(pal,255,sizeof(pal));
2143 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2144 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2145 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2146 pal[0].a = 255; pal[1].a = 0;
2149 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2150 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2151 for (y = 0; y < height; ++y)
2152 for (x = 0; x < width; ++x)
2154 imgStr->getPixel(buf);
2157 pic[width*y+x] = buf[0];
2160 /* the size of the drawn image is added to the identifier
2161 as the same image may require different bitmaps if displayed
2162 at different sizes (due to antialiasing): */
2165 unsigned char*pic2 = 0;
2168 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2177 height = realheight;
2181 /* make a black/white palette */
2183 float r = 255./(float)(numpalette-1);
2185 for(t=0;t<numpalette;t++) {
2186 pal[t].r = colToByte(rgb.r);
2187 pal[t].g = colToByte(rgb.g);
2188 pal[t].b = colToByte(rgb.b);
2189 pal[t].a = (unsigned char)(t*r);
2193 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2194 for (y = 0; y < height; ++y) {
2195 for (x = 0; x < width; ++x) {
2196 pic2[width*y+x] = pal[pic[y*width+x]];
2199 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2203 if(maskbitmap) free(maskbitmap);
2209 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2210 gfxcolor_t*pic=new gfxcolor_t[width*height];
2211 for (y = 0; y < height; ++y) {
2212 for (x = 0; x < width; ++x) {
2213 imgStr->getPixel(pixBuf);
2214 colorMap->getRGB(pixBuf, &rgb);
2215 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2216 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2217 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2218 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2220 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2224 if(str->getKind()==strDCT)
2225 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2227 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2230 if(maskbitmap) free(maskbitmap);
2233 gfxcolor_t*pic=new gfxcolor_t[width*height];
2234 gfxcolor_t pal[256];
2235 int n = 1 << colorMap->getBits();
2237 for(t=0;t<256;t++) {
2239 colorMap->getRGB(pixBuf, &rgb);
2241 {/*if(maskColors && *maskColors==t) {
2242 msg("<notice> Color %d is transparent", t);
2243 if (imgData->maskColors) {
2245 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2246 if (pix[i] < imgData->maskColors[2*i] ||
2247 pix[i] > imgData->maskColors[2*i+1]) {
2262 pal[t].r = (unsigned char)(colToByte(rgb.r));
2263 pal[t].g = (unsigned char)(colToByte(rgb.g));
2264 pal[t].b = (unsigned char)(colToByte(rgb.b));
2265 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2268 for (y = 0; y < height; ++y) {
2269 for (x = 0; x < width; ++x) {
2270 imgStr->getPixel(pixBuf);
2271 pic[width*y+x] = pal[pixBuf[0]];
2275 if(maskWidth < width && maskHeight < height) {
2276 for(y = 0; y < height; y++) {
2277 for (x = 0; x < width; x++) {
2278 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2282 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2283 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2284 double dx = width / maskWidth;
2285 double dy = height / maskHeight;
2287 for(y = 0; y < maskHeight; y++) {
2289 for (x = 0; x < maskWidth; x++) {
2290 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2291 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2299 height = maskHeight;
2302 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2306 if(maskbitmap) free(maskbitmap);
2311 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2312 int width, int height, GBool invert,
2315 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2316 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2317 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2320 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2321 int width, int height, GfxImageColorMap *colorMap,
2322 int *maskColors, GBool inlineImg)
2324 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2325 colorMap?"colorMap":"no colorMap",
2326 maskColors?"maskColors":"no maskColors",
2328 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2329 colorMap?"colorMap":"no colorMap",
2330 maskColors?"maskColors":"no maskColors",
2333 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2334 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2335 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2338 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2339 int width, int height,
2340 GfxImageColorMap *colorMap,
2341 Stream *maskStr, int maskWidth, int maskHeight,
2344 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2345 colorMap?"colorMap":"no colorMap",
2346 maskWidth, maskHeight);
2347 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2348 colorMap?"colorMap":"no colorMap",
2349 maskWidth, maskHeight);
2351 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2352 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2353 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2356 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2357 int width, int height,
2358 GfxImageColorMap *colorMap,
2360 int maskWidth, int maskHeight,
2361 GfxImageColorMap *maskColorMap)
2363 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2364 colorMap?"colorMap":"no colorMap",
2365 maskWidth, maskHeight);
2366 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2367 colorMap?"colorMap":"no colorMap",
2368 maskWidth, maskHeight);
2370 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2371 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2372 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2375 void GFXOutputDev::stroke(GfxState *state)
2379 GfxPath * path = state->getPath();
2380 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2381 strokeGfxline(state, line, 0);
2385 void GFXOutputDev::fill(GfxState *state)
2387 gfxcolor_t col = getFillColor(state);
2388 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2390 GfxPath * path = state->getPath();
2391 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2392 if(config_optimize_polygons) {
2393 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2397 fillGfxLine(state, line);
2401 void GFXOutputDev::eoFill(GfxState *state)
2403 gfxcolor_t col = getFillColor(state);
2404 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2406 GfxPath * path = state->getPath();
2407 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2408 fillGfxLine(state, line);
2413 static const char* dirseparator()
2422 void addGlobalFont(const char*filename)
2424 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2425 memset(f, 0, sizeof(fontfile_t));
2426 f->filename = filename;
2427 int len = strlen(filename);
2428 char*r1 = strrchr(filename, '/');
2429 char*r2 = strrchr(filename, '\\');
2437 msg("<notice> Adding font \"%s\".", filename);
2438 if(global_fonts_next) {
2439 global_fonts_next->next = f;
2440 global_fonts_next = global_fonts_next->next;
2442 global_fonts_next = global_fonts = f;
2446 void addGlobalLanguageDir(const char*dir)
2448 msg("<notice> Adding %s to language pack directories", dir);
2451 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2452 strcpy(config_file, dir);
2453 strcat(config_file, dirseparator());
2454 strcat(config_file, "add-to-xpdfrc");
2456 fi = fopen(config_file, "rb");
2458 msg("<error> Could not open %s", config_file);
2461 globalParams->parseFile(new GString(config_file), fi);
2465 void addGlobalFontDir(const char*dirname)
2467 #ifdef HAVE_DIRENT_H
2468 msg("<notice> Adding %s to font directories", dirname);
2469 lastfontdir = strdup(dirname);
2470 DIR*dir = opendir(dirname);
2472 msg("<warning> Couldn't open directory %s", dirname);
2477 ent = readdir (dir);
2481 char*name = ent->d_name;
2487 if(!strncasecmp(&name[l-4], ".pfa", 4))
2489 if(!strncasecmp(&name[l-4], ".pfb", 4))
2491 if(!strncasecmp(&name[l-4], ".ttf", 4))
2494 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2495 strcpy(fontname, dirname);
2496 strcat(fontname, dirseparator());
2497 strcat(fontname, name);
2498 addGlobalFont(fontname);
2503 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2507 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2513 this->pagebuflen = 1024;
2514 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2515 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2517 while(pdfpage >= this->pagebuflen)
2519 int oldlen = this->pagebuflen;
2520 this->pagebuflen+=1024;
2521 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2522 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2525 this->pages[pdfpage] = outputpage;
2526 if(pdfpage>this->pagepos)
2527 this->pagepos = pdfpage;
2530 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2531 GfxColorSpace *blendingColorSpace,
2532 GBool isolated, GBool knockout,
2535 const char*colormodename = "";
2537 if(blendingColorSpace) {
2538 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2540 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);
2541 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);
2543 //states[statepos].createsoftmask |= forSoftMask;
2544 states[statepos].createsoftmask = forSoftMask;
2545 states[statepos].transparencygroup = !forSoftMask;
2546 states[statepos].isolated = isolated;
2548 states[statepos].olddevice = this->device;
2549 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2551 gfxdevice_record_init(this->device);
2553 /*if(!forSoftMask) { ////???
2554 state->setFillOpacity(0.0);
2559 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2562 gfxdevice_t*r = this->device;
2564 this->device = states[statepos].olddevice;
2566 gfxresult_t*recording = r->finish(r);
2568 dbg("endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2569 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2571 if(states[statepos].createsoftmask) {
2572 states[statepos-1].softmaskrecording = recording;
2574 states[statepos-1].grouprecording = recording;
2577 states[statepos].createsoftmask = 0;
2578 states[statepos].transparencygroup = 0;
2582 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2584 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2585 "colordodge","colorburn","hardlight","softlight","difference",
2586 "exclusion","hue","saturation","color","luminosity"};
2588 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2589 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2591 if(state->getBlendMode() == gfxBlendNormal)
2592 infofeature("transparency groups");
2595 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2596 warnfeature(buffer, 0);
2599 gfxresult_t*grouprecording = states[statepos].grouprecording;
2601 int blendmode = state->getBlendMode();
2602 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2603 int alpha = (int)(state->getFillOpacity()*255);
2604 if(blendmode == gfxBlendMultiply && alpha>200)
2607 gfxdevice_ops_init(&ops, this->device, alpha);
2608 gfxresult_record_replay(grouprecording, &ops);
2611 grouprecording->destroy(grouprecording);
2613 states[statepos].grouprecording = 0;
2616 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2618 /* alpha = 1: retrieve mask values from alpha layer
2619 alpha = 0: retrieve mask values from luminance */
2620 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2621 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2622 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2623 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2625 infofeature("soft masks");
2627 warnfeature("soft masks from alpha channel",0);
2629 states[statepos].olddevice = this->device;
2630 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2631 gfxdevice_record_init(this->device);
2633 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2635 states[statepos].softmask = 1;
2636 states[statepos].softmask_alpha = alpha;
2639 static inline Guchar div255(int x) {
2640 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2643 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2645 if(c < min) c = min;
2646 if(c > max) c = max;
2650 void GFXOutputDev::clearSoftMask(GfxState *state)
2652 if(!states[statepos].softmask)
2654 states[statepos].softmask = 0;
2655 dbg("clearSoftMask statepos=%d", statepos);
2656 msg("<verbose> clearSoftMask");
2658 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2659 msg("<error> Error in softmask/tgroup ordering");
2663 gfxresult_t*mask = states[statepos].softmaskrecording;
2664 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2665 this->device = states[statepos].olddevice;
2667 /* get outline of all objects below the soft mask */
2668 gfxdevice_t uniondev;
2669 gfxdevice_union_init(&uniondev, 0);
2670 gfxresult_record_replay(below, &uniondev);
2671 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2672 uniondev.finish(&uniondev);
2673 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2674 gfxline_free(belowoutline);belowoutline=0;
2676 this->device->startclip(this->device, belowoutline);
2677 gfxresult_record_replay(below, this->device);
2678 gfxresult_record_replay(mask, this->device);
2679 this->device->endclip(this->device);
2682 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2683 if(width<=0 || height<=0)
2686 gfxdevice_t belowrender;
2687 gfxdevice_render_init(&belowrender);
2688 if(states[statepos+1].isolated) {
2689 belowrender.setparameter(&belowrender, "fillwhite", "1");
2691 belowrender.setparameter(&belowrender, "antialize", "2");
2692 belowrender.startpage(&belowrender, width, height);
2693 gfxresult_record_replay(below, &belowrender);
2694 belowrender.endpage(&belowrender);
2695 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2696 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2697 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2699 gfxdevice_t maskrender;
2700 gfxdevice_render_init(&maskrender);
2701 maskrender.startpage(&maskrender, width, height);
2702 gfxresult_record_replay(mask, &maskrender);
2703 maskrender.endpage(&maskrender);
2704 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2705 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2707 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2708 msg("<fatal> Internal error in mask drawing");
2713 for(y=0;y<height;y++) {
2714 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2715 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2716 for(x=0;x<width;x++) {
2718 if(states[statepos].softmask_alpha) {
2721 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2724 l2->a = div255(alpha*l2->a);
2726 /* DON'T premultiply alpha- this is done by fillbitmap,
2727 depending on the output device */
2728 //l2->r = div255(alpha*l2->r);
2729 //l2->g = div255(alpha*l2->g);
2730 //l2->b = div255(alpha*l2->b);
2736 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2739 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2740 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2742 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2744 mask->destroy(mask);
2745 below->destroy(below);
2746 maskresult->destroy(maskresult);
2747 belowresult->destroy(belowresult);
2748 states[statepos].softmaskrecording = 0;
2753 // public: ~MemCheck()
2755 // delete globalParams;globalParams=0;
2756 // Object::memCheck(stderr);
2757 // gMemReport(stderr);