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);
1029 if(this->dashPattern) {
1030 free(this->dashPattern);
1031 this->dashPattern = 0;
1033 /* notice: we're not fully done yet with this page- there might still be
1034 a few calls to drawLink() yet to come */
1037 #define STROKE_FILL 1
1038 #define STROKE_CLIP 2
1039 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1041 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1042 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1043 double miterLimit = state->getMiterLimit();
1044 double width = state->getTransformedLineWidth();
1047 double opaq = state->getStrokeOpacity();
1049 state->getFillRGB(&rgb);
1051 state->getStrokeRGB(&rgb);
1053 col.r = colToByte(rgb.r);
1054 col.g = colToByte(rgb.g);
1055 col.b = colToByte(rgb.b);
1056 col.a = (unsigned char)(opaq*255);
1058 gfx_capType capType = gfx_capRound;
1059 if(lineCap == 0) capType = gfx_capButt;
1060 else if(lineCap == 1) capType = gfx_capRound;
1061 else if(lineCap == 2) capType = gfx_capSquare;
1063 gfx_joinType joinType = gfx_joinRound;
1064 if(lineJoin == 0) joinType = gfx_joinMiter;
1065 else if(lineJoin == 1) joinType = gfx_joinRound;
1066 else if(lineJoin == 2) joinType = gfx_joinBevel;
1068 gfxline_t*line2 = 0;
1070 if(0 && this->dashLength && this->dashPattern) {
1071 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1073 msg("<trace> %d dashes", this->dashLength);
1074 msg("<trace> | phase: %f", this->dashStart);
1075 for(t=0;t<this->dashLength;t++) {
1076 dash[t] = (float)this->dashPattern[t];
1077 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1079 dash[this->dashLength] = -1;
1080 if(getLogLevel() >= LOGLEVEL_TRACE) {
1084 line2 = gfxtool_dash_line(line, dash, (float)this->dashStart);
1087 msg("<trace> After dashing:");
1090 if(getLogLevel() >= LOGLEVEL_TRACE) {
1091 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1093 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1094 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1096 col.r,col.g,col.b,col.a
1101 if(flags&STROKE_FILL) {
1102 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1103 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1104 if(getLogLevel() >= LOGLEVEL_TRACE) {
1105 dump_outline(gfxline);
1108 msg("<warning> Empty polygon (resulting from stroked line)");
1110 if(flags&STROKE_CLIP) {
1111 device->startclip(device, gfxline);
1112 states[statepos].clipping++;
1114 device->fill(device, gfxline, &col);
1119 if(flags&STROKE_CLIP)
1120 msg("<error> Stroke&clip not supported at the same time");
1121 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1125 gfxline_free(line2);
1128 gfxcolor_t getFillColor(GfxState * state)
1131 double opaq = state->getFillOpacity();
1132 state->getFillRGB(&rgb);
1134 col.r = colToByte(rgb.r);
1135 col.g = colToByte(rgb.g);
1136 col.b = colToByte(rgb.b);
1137 col.a = (unsigned char)(opaq*255);
1141 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1143 gfxcolor_t col = getFillColor(state);
1145 if(getLogLevel() >= LOGLEVEL_TRACE) {
1146 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1149 device->fill(device, line, &col);
1152 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1154 if(getLogLevel() >= LOGLEVEL_TRACE) {
1157 gfxbbox_t bbox = gfxline_getbbox(line);
1158 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1160 device->startclip(device, line);
1161 states[statepos].clipping++;
1164 void GFXOutputDev::clip(GfxState *state)
1166 GfxPath * path = state->getPath();
1167 msg("<trace> clip");
1168 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1169 if(config_optimize_polygons) {
1170 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1174 clipToGfxLine(state, line);
1178 void GFXOutputDev::eoClip(GfxState *state)
1180 GfxPath * path = state->getPath();
1181 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1182 clipToGfxLine(state, line);
1185 void GFXOutputDev::clipToStrokePath(GfxState *state)
1187 GfxPath * path = state->getPath();
1188 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1190 if(getLogLevel() >= LOGLEVEL_TRACE) {
1191 msg("<trace> cliptostrokepath");
1195 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1199 void GFXOutputDev::finish()
1201 if(outer_clip_box) {
1203 device->endclip(device);
1209 GFXOutputDev::~GFXOutputDev()
1214 free(this->pages); this->pages = 0;
1217 feature_t*f = this->featurewarnings;
1219 feature_t*next = f->next;
1221 free(f->string);f->string =0;
1227 this->featurewarnings = 0;
1229 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1231 GBool GFXOutputDev::upsideDown()
1235 GBool GFXOutputDev::useDrawChar()
1240 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1241 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1243 static char tmp_printstr[4096];
1244 char* makeStringPrintable(char*str)
1246 int len = strlen(str);
1253 for(t=0;t<len;t++) {
1258 tmp_printstr[t] = c;
1261 tmp_printstr[len++] = '.';
1262 tmp_printstr[len++] = '.';
1263 tmp_printstr[len++] = '.';
1265 tmp_printstr[len] = 0;
1266 return tmp_printstr;
1268 #define INTERNAL_FONT_SIZE 1024.0
1269 void GFXOutputDev::updateFontMatrix(GfxState*state)
1271 double* ctm = state->getCTM();
1272 double fontSize = state->getFontSize();
1273 double*textMat = state->getTextMat();
1275 /* taking the absolute value of horizScaling seems to be required for
1276 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1277 double hscale = fabs(state->getHorizScaling());
1279 // from xpdf-3.02/SplashOutputDev:updateFont
1280 double mm11 = textMat[0] * fontSize * hscale;
1281 double mm12 = textMat[1] * fontSize * hscale;
1282 double mm21 = textMat[2] * fontSize;
1283 double mm22 = textMat[3] * fontSize;
1285 // multiply with ctm, like state->getFontTransMat() does
1286 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1287 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1288 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1289 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1290 this->current_font_matrix.tx = 0;
1291 this->current_font_matrix.ty = 0;
1294 void GFXOutputDev::beginString(GfxState *state, GString *s)
1296 int render = state->getRender();
1297 if(current_text_stroke) {
1298 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1301 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1304 static gfxline_t* mkEmptyGfxShape(double x, double y)
1306 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1307 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1311 static char isValidUnicode(int c)
1313 if(c>=32 && c<0x2fffe)
1318 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1319 double dx, double dy,
1320 double originX, double originY,
1321 CharCode charid, int nBytes, Unicode *_u, int uLen)
1323 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1324 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1328 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1330 int render = state->getRender();
1331 gfxcolor_t col = getFillColor(state);
1333 // check for invisible text -- this is used by Acrobat Capture
1334 if (render == RENDER_INVISIBLE) {
1336 if(!config_extrafontdata)
1340 GfxFont*font = state->getFont();
1342 if(font->getType() == fontType3) {
1343 /* type 3 chars are passed as graphics */
1344 msg("<debug> type3 char at %f/%f", x, y);
1348 Unicode u = uLen?(_u[0]):0;
1349 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);
1351 gfxmatrix_t m = this->current_font_matrix;
1352 this->transformXY(state, x, y, &m.tx, &m.ty);
1354 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1355 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1357 msg("<debug> Drawing glyph %d as shape", charid);
1359 msg("<notice> Some texts will be rendered as shape");
1362 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1363 gfxline_t*tglyph = gfxline_clone(glyph);
1364 gfxline_transform(tglyph, &m);
1365 if((render&3) != RENDER_INVISIBLE) {
1366 gfxline_t*add = gfxline_clone(tglyph);
1367 current_text_stroke = gfxline_append(current_text_stroke, add);
1369 if(render&RENDER_CLIP) {
1370 gfxline_t*add = gfxline_clone(tglyph);
1371 current_text_clip = gfxline_append(current_text_clip, add);
1372 if(!current_text_clip) {
1373 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1376 gfxline_free(tglyph);
1380 void GFXOutputDev::endString(GfxState *state)
1382 int render = state->getRender();
1383 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1385 if(current_text_stroke) {
1386 /* fillstroke and stroke text rendering objects we can process right
1387 now (as there may be texts of other rendering modes in this
1388 text object)- clipping objects have to wait until endTextObject,
1390 device->setparameter(device, "mark","TXT");
1391 if((render&3) == RENDER_FILL) {
1392 fillGfxLine(state, current_text_stroke);
1393 gfxline_free(current_text_stroke);
1394 current_text_stroke = 0;
1395 } else if((render&3) == RENDER_FILLSTROKE) {
1396 fillGfxLine(state, current_text_stroke);
1397 strokeGfxline(state, current_text_stroke,0);
1398 gfxline_free(current_text_stroke);
1399 current_text_stroke = 0;
1400 } else if((render&3) == RENDER_STROKE) {
1401 strokeGfxline(state, current_text_stroke,0);
1402 gfxline_free(current_text_stroke);
1403 current_text_stroke = 0;
1405 device->setparameter(device, "mark","");
1409 void GFXOutputDev::endTextObject(GfxState *state)
1411 int render = state->getRender();
1412 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1414 if(current_text_clip) {
1415 device->setparameter(device, "mark","TXT");
1416 clipToGfxLine(state, current_text_clip);
1417 device->setparameter(device, "mark","");
1418 gfxline_free(current_text_clip);
1419 current_text_clip = 0;
1423 /* the logic seems to be as following:
1424 first, beginType3Char is called, with the charcode and the coordinates.
1425 if this function returns true, it already knew about the char and has now drawn it.
1426 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1427 called with some parameters.
1428 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1429 at the position first passed to beginType3Char). the char ends with endType3Char.
1431 The drawing operations between beginType3Char and endType3Char are somewhat different to
1432 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1433 color determines the color of a font)
1436 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1438 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1441 if(config_extrafontdata && current_fontinfo) {
1443 gfxmatrix_t m = this->current_font_matrix;
1444 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1445 m.m00*=INTERNAL_FONT_SIZE;
1446 m.m01*=INTERNAL_FONT_SIZE;
1447 m.m10*=INTERNAL_FONT_SIZE;
1448 m.m11*=INTERNAL_FONT_SIZE;
1450 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1451 msg("<error> Invalid charid %d for font", charid);
1454 gfxcolor_t col={0,0,0,0};
1455 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1456 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1460 /* the character itself is going to be passed using the draw functions */
1461 return gFalse; /* gTrue= is_in_cache? */
1464 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1466 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1469 void GFXOutputDev::endType3Char(GfxState *state)
1472 msg("<debug> endType3Char");
1475 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1477 this->currentpage = pageNum;
1479 int rot = doc->getPageRotate(1);
1480 gfxcolor_t white = {255,255,255,255};
1481 gfxcolor_t black = {255,0,0,0};
1483 gfxline_t clippath[5];
1485 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1486 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1487 Use CropBox, not MediaBox, as page size
1494 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1495 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1497 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1498 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1500 this->clipmovex = -(int)x1;
1501 this->clipmovey = -(int)y1;
1503 /* apply user clip box */
1504 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1505 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1506 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1507 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1508 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1509 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1512 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1514 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);
1516 msg("<verbose> page is rotated %d degrees", rot);
1518 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1519 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1520 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1521 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1522 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1523 device->startclip(device, clippath); outer_clip_box = 1;
1524 if(!config_transparent) {
1525 device->fill(device, clippath, &white);
1527 states[statepos].clipbbox.xmin = x1;
1528 states[statepos].clipbbox.ymin = x1;
1529 states[statepos].clipbbox.xmax = x2;
1530 states[statepos].clipbbox.ymax = y2;
1532 this->dashPattern = 0;
1533 this->dashLength = 0;
1534 this->dashStart = 0;
1538 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1540 double x1, y1, x2, y2;
1541 gfxline_t points[5];
1544 msg("<debug> drawlink");
1546 link->getRect(&x1, &y1, &x2, &y2);
1547 cvtUserToDev(x1, y1, &x, &y);
1548 points[0].type = gfx_moveTo;
1549 points[0].x = points[4].x = x + user_movex + clipmovex;
1550 points[0].y = points[4].y = y + user_movey + clipmovey;
1551 points[0].next = &points[1];
1552 cvtUserToDev(x2, y1, &x, &y);
1553 points[1].type = gfx_lineTo;
1554 points[1].x = x + user_movex + clipmovex;
1555 points[1].y = y + user_movey + clipmovey;
1556 points[1].next = &points[2];
1557 cvtUserToDev(x2, y2, &x, &y);
1558 points[2].type = gfx_lineTo;
1559 points[2].x = x + user_movex + clipmovex;
1560 points[2].y = y + user_movey + clipmovey;
1561 points[2].next = &points[3];
1562 cvtUserToDev(x1, y2, &x, &y);
1563 points[3].type = gfx_lineTo;
1564 points[3].x = x + user_movex + clipmovex;
1565 points[3].y = y + user_movey + clipmovey;
1566 points[3].next = &points[4];
1567 cvtUserToDev(x1, y1, &x, &y);
1568 points[4].type = gfx_lineTo;
1569 points[4].x = x + user_movex + clipmovex;
1570 points[4].y = y + user_movey + clipmovey;
1573 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1574 points[0].x, points[0].y,
1575 points[1].x, points[1].y,
1576 points[2].x, points[2].y,
1577 points[3].x, points[3].y);
1579 if(getLogLevel() >= LOGLEVEL_TRACE) {
1580 dump_outline(points);
1583 LinkAction*action=link->getAction();
1586 const char*type = "-?-";
1589 msg("<trace> drawlink action=%d", action->getKind());
1590 switch(action->getKind())
1594 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1595 LinkDest *dest=NULL;
1596 if (ha->getDest()==NULL)
1597 dest=catalog->findDest(ha->getNamedDest());
1598 else dest=ha->getDest();
1600 if (dest->isPageRef()){
1601 Ref pageref=dest->getPageRef();
1602 page=catalog->findPage(pageref.num,pageref.gen);
1604 else page=dest->getPageNum();
1605 sprintf(buf, "%d", page);
1612 LinkGoToR*l = (LinkGoToR*)action;
1613 GString*g = l->getFileName();
1615 s = strdup(g->getCString());
1617 /* if the GoToR link has no filename, then
1618 try to find a refernce in the *local*
1620 GString*g = l->getNamedDest();
1622 s = strdup(g->getCString());
1628 LinkNamed*l = (LinkNamed*)action;
1629 GString*name = l->getName();
1631 s = strdup(name->lowerCase()->getCString());
1632 named = name->getCString();
1635 if(strstr(s, "next") || strstr(s, "forward"))
1637 page = currentpage + 1;
1639 else if(strstr(s, "prev") || strstr(s, "back"))
1641 page = currentpage - 1;
1643 else if(strstr(s, "last") || strstr(s, "end"))
1645 if(pages && pagepos>0)
1646 page = pages[pagepos-1];
1648 else if(strstr(s, "first") || strstr(s, "top"))
1656 case actionLaunch: {
1658 LinkLaunch*l = (LinkLaunch*)action;
1659 GString * str = new GString(l->getFileName());
1660 GString * params = l->getParams();
1662 str->append(params);
1663 s = strdup(str->getCString());
1670 LinkURI*l = (LinkURI*)action;
1671 GString*g = l->getURI();
1673 url = g->getCString();
1678 case actionUnknown: {
1680 LinkUnknown*l = (LinkUnknown*)action;
1685 msg("<error> Unknown link type!");
1690 if(!s) s = strdup("-?-");
1692 msg("<trace> drawlink s=%s", s);
1694 if(!linkinfo && (page || s))
1696 msg("<notice> File contains links");
1704 for(t=1;t<=pagepos;t++) {
1705 if(pages[t]==page) {
1714 sprintf(buf, "page%d", lpage);
1715 device->drawlink(device, points, buf);
1719 device->drawlink(device, points, s);
1722 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1726 void GFXOutputDev::saveState(GfxState *state) {
1727 dbg("saveState");dbgindent+=2;
1729 msg("<trace> saveState");
1732 msg("<error> Too many nested states in pdf.");
1736 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1737 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1738 states[statepos].clipping = 0;
1739 states[statepos].clipbbox = states[statepos-1].clipbbox;
1742 void GFXOutputDev::restoreState(GfxState *state) {
1743 dbgindent-=2; dbg("restoreState");
1746 msg("<error> Invalid restoreState");
1749 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1750 states[statepos].clipping?" (end clipping)":"");
1751 if(states[statepos].softmask) {
1752 clearSoftMask(state);
1755 while(states[statepos].clipping) {
1756 device->endclip(device);
1757 states[statepos].clipping--;
1762 void GFXOutputDev::updateLineDash(GfxState *state)
1764 state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1765 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1766 if(!this->dashLength) {
1767 this->dashPattern = 0;
1771 void GFXOutputDev::updateLineWidth(GfxState *state)
1773 double width = state->getTransformedLineWidth();
1776 void GFXOutputDev::updateLineCap(GfxState *state)
1778 int c = state->getLineCap();
1781 void GFXOutputDev::updateLineJoin(GfxState *state)
1783 int j = state->getLineJoin();
1786 void GFXOutputDev::updateFillColor(GfxState *state)
1789 double opaq = state->getFillOpacity();
1790 state->getFillRGB(&rgb);
1792 void GFXOutputDev::updateFillOpacity(GfxState *state)
1795 double opaq = state->getFillOpacity();
1796 state->getFillRGB(&rgb);
1797 dbg("update fillopaq %f", opaq);
1799 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1801 double opaq = state->getFillOpacity();
1802 dbg("update strokeopaq %f", opaq);
1804 void GFXOutputDev::updateFillOverprint(GfxState *state)
1806 double opaq = state->getFillOverprint();
1807 dbg("update filloverprint %f", opaq);
1809 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1811 double opaq = state->getStrokeOverprint();
1812 dbg("update strokeoverprint %f", opaq);
1814 void GFXOutputDev::updateTransfer(GfxState *state)
1816 dbg("update transfer");
1820 void GFXOutputDev::updateStrokeColor(GfxState *state)
1823 double opaq = state->getStrokeOpacity();
1824 state->getStrokeRGB(&rgb);
1828 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1830 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1831 memset(font, 0, sizeof(gfxfont_t));
1833 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1834 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1838 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1840 //printf("%d glyphs\n", font->num_glyphs);
1841 font->num_glyphs = 0;
1842 for(t=0;t<src->num_glyphs;t++) {
1843 if(src->glyphs[t]) {
1844 SplashPath*path = src->glyphs[t]->path;
1845 int len = path?path->getLength():0;
1846 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1847 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1848 src->glyphs[t]->glyphid = font->num_glyphs;
1849 glyph->unicode = src->glyphs[t]->unicode;
1850 if(glyph->unicode >= font->max_unicode)
1851 font->max_unicode = glyph->unicode+1;
1853 gfxdrawer_target_gfxline(&drawer);
1857 for(s=0;s<len;s++) {
1860 path->getPoint(s, &x, &y, &f);
1863 if(f&splashPathFirst) {
1864 drawer.moveTo(&drawer, x*scale, y*scale);
1866 if(f&splashPathCurve) {
1868 path->getPoint(++s, &x2, &y2, &f);
1869 if(f&splashPathCurve) {
1871 path->getPoint(++s, &x3, &y3, &f);
1872 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1874 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1877 drawer.lineTo(&drawer, x*scale, y*scale);
1879 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1880 // (f&splashPathFirst)?"first":"",
1881 // (f&splashPathLast)?"last":"");
1883 glyph->line = (gfxline_t*)drawer.result(&drawer);
1884 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1888 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1889 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1890 for(t=0;t<font->num_glyphs;t++) {
1891 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1892 font->unicode2glyph[font->glyphs[t].unicode] = t;
1896 msg("<trace> %d glyphs.", t, font->num_glyphs);
1900 void GFXOutputDev::updateFont(GfxState *state)
1902 GfxFont* gfxFont = state->getFont();
1906 char*id = getFontID(gfxFont);
1907 msg("<verbose> Updating font to %s", id);
1908 if(gfxFont->getType() == fontType3) {
1909 infofeature("Type3 fonts");
1910 if(!config_extrafontdata) {
1915 msg("<error> Internal Error: FontID is null");
1919 this->current_fontinfo = this->info->getFont(id);
1920 if(!this->current_fontinfo) {
1921 msg("<error> Internal Error: no fontinfo for font %s", id);
1924 if(!this->current_fontinfo->seen) {
1925 dumpFontInfo("<verbose>", gfxFont);
1928 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1930 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1931 font->id = strdup(id);
1932 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1934 device->addfont(device, font);
1936 current_gfxfont = font;
1939 updateFontMatrix(state);
1942 #define SQR(x) ((x)*(x))
1944 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1946 if((newwidth<2 || newheight<2) ||
1947 (width<=newwidth || height<=newheight))
1949 unsigned char*newdata;
1951 newdata= (unsigned char*)malloc(newwidth*newheight);
1952 double fx = (double)(width)/newwidth;
1953 double fy = (double)(height)/newheight;
1955 int blocksize = (int)(8192/(fx*fy));
1956 int r = 8192*256/palettesize;
1957 for(x=0;x<newwidth;x++) {
1958 double ex = px + fx;
1959 int fromx = (int)px;
1961 int xweight1 = (int)(((fromx+1)-px)*256);
1962 int xweight2 = (int)((ex-tox)*256);
1964 for(y=0;y<newheight;y++) {
1965 double ey = py + fy;
1966 int fromy = (int)py;
1968 int yweight1 = (int)(((fromy+1)-py)*256);
1969 int yweight2 = (int)((ey-toy)*256);
1972 for(xx=fromx;xx<=tox;xx++)
1973 for(yy=fromy;yy<=toy;yy++) {
1974 int b = 1-data[width*yy+xx];
1976 if(xx==fromx) weight = (weight*xweight1)/256;
1977 if(xx==tox) weight = (weight*xweight2)/256;
1978 if(yy==fromy) weight = (weight*yweight1)/256;
1979 if(yy==toy) weight = (weight*yweight2)/256;
1982 //if(a) a=(palettesize-1)*r/blocksize;
1983 newdata[y*newwidth+x] = (a*blocksize)/r;
1991 #define IMAGE_TYPE_JPEG 0
1992 #define IMAGE_TYPE_LOSSLESS 1
1994 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1995 double x1,double y1,
1996 double x2,double y2,
1997 double x3,double y3,
1998 double x4,double y4, int type)
2000 gfxcolor_t*newpic=0;
2002 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2003 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2005 gfxline_t p1,p2,p3,p4,p5;
2006 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2007 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2008 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2009 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2010 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2012 {p1.x = (int)(p1.x*20)/20.0;
2013 p1.y = (int)(p1.y*20)/20.0;
2014 p2.x = (int)(p2.x*20)/20.0;
2015 p2.y = (int)(p2.y*20)/20.0;
2016 p3.x = (int)(p3.x*20)/20.0;
2017 p3.y = (int)(p3.y*20)/20.0;
2018 p4.x = (int)(p4.x*20)/20.0;
2019 p4.y = (int)(p4.y*20)/20.0;
2020 p5.x = (int)(p5.x*20)/20.0;
2021 p5.y = (int)(p5.y*20)/20.0;
2025 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2026 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2031 img.data = (gfxcolor_t*)data;
2035 if(type == IMAGE_TYPE_JPEG)
2036 /* TODO: pass image_dpi to device instead */
2037 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2039 dev->fillbitmap(dev, &p1, &img, &m, 0);
2042 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2043 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2045 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2048 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2049 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2051 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2055 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2056 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2057 GBool inlineImg, int mask, int*maskColors,
2058 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2060 double x1,y1,x2,y2,x3,y3,x4,y4;
2061 ImageStream *imgStr;
2066 unsigned char* maskbitmap = 0;
2069 ncomps = colorMap->getNumPixelComps();
2070 bits = colorMap->getBits();
2075 unsigned char buf[8];
2076 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2078 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2079 imgMaskStr->reset();
2080 unsigned char pal[256];
2081 int n = 1 << colorMap->getBits();
2086 maskColorMap->getGray(pixBuf, &gray);
2087 pal[t] = colToByte(gray);
2089 for (y = 0; y < maskHeight; y++) {
2090 for (x = 0; x < maskWidth; x++) {
2091 imgMaskStr->getPixel(buf);
2092 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2097 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2098 imgMaskStr->reset();
2099 for (y = 0; y < maskHeight; y++) {
2100 for (x = 0; x < maskWidth; x++) {
2101 imgMaskStr->getPixel(buf);
2103 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2111 imgStr = new ImageStream(str, width, ncomps,bits);
2114 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2116 msg("<verbose> Ignoring %d by %d image", width, height);
2117 unsigned char buf[8];
2119 for (y = 0; y < height; ++y)
2120 for (x = 0; x < width; ++x) {
2121 imgStr->getPixel(buf);
2129 this->transformXY(state, 0, 1, &x1, &y1);
2130 this->transformXY(state, 0, 0, &x2, &y2);
2131 this->transformXY(state, 1, 0, &x3, &y3);
2132 this->transformXY(state, 1, 1, &x4, &y4);
2134 if(!pbminfo && !(str->getKind()==strDCT)) {
2136 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2140 msg("<verbose> drawing %d by %d masked picture", width, height);
2142 if(!jpeginfo && (str->getKind()==strDCT)) {
2143 msg("<notice> File contains jpeg pictures");
2148 unsigned char buf[8];
2150 unsigned char*pic = new unsigned char[width*height];
2151 gfxcolor_t pal[256];
2153 state->getFillRGB(&rgb);
2155 memset(pal,255,sizeof(pal));
2156 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2157 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2158 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2159 pal[0].a = 255; pal[1].a = 0;
2162 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2163 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2164 for (y = 0; y < height; ++y)
2165 for (x = 0; x < width; ++x)
2167 imgStr->getPixel(buf);
2170 pic[width*y+x] = buf[0];
2173 /* the size of the drawn image is added to the identifier
2174 as the same image may require different bitmaps if displayed
2175 at different sizes (due to antialiasing): */
2178 unsigned char*pic2 = 0;
2181 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2190 height = realheight;
2194 /* make a black/white palette */
2196 float r = 255./(float)(numpalette-1);
2198 for(t=0;t<numpalette;t++) {
2199 pal[t].r = colToByte(rgb.r);
2200 pal[t].g = colToByte(rgb.g);
2201 pal[t].b = colToByte(rgb.b);
2202 pal[t].a = (unsigned char)(t*r);
2206 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2207 for (y = 0; y < height; ++y) {
2208 for (x = 0; x < width; ++x) {
2209 pic2[width*y+x] = pal[pic[y*width+x]];
2212 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2216 if(maskbitmap) free(maskbitmap);
2222 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2223 gfxcolor_t*pic=new gfxcolor_t[width*height];
2224 for (y = 0; y < height; ++y) {
2225 for (x = 0; x < width; ++x) {
2226 imgStr->getPixel(pixBuf);
2227 colorMap->getRGB(pixBuf, &rgb);
2228 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2229 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2230 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2231 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2233 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2237 if(str->getKind()==strDCT)
2238 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2240 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2243 if(maskbitmap) free(maskbitmap);
2246 gfxcolor_t*pic=new gfxcolor_t[width*height];
2247 gfxcolor_t pal[256];
2248 int n = 1 << colorMap->getBits();
2250 for(t=0;t<256;t++) {
2252 colorMap->getRGB(pixBuf, &rgb);
2254 {/*if(maskColors && *maskColors==t) {
2255 msg("<notice> Color %d is transparent", t);
2256 if (imgData->maskColors) {
2258 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2259 if (pix[i] < imgData->maskColors[2*i] ||
2260 pix[i] > imgData->maskColors[2*i+1]) {
2275 pal[t].r = (unsigned char)(colToByte(rgb.r));
2276 pal[t].g = (unsigned char)(colToByte(rgb.g));
2277 pal[t].b = (unsigned char)(colToByte(rgb.b));
2278 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2281 for (y = 0; y < height; ++y) {
2282 for (x = 0; x < width; ++x) {
2283 imgStr->getPixel(pixBuf);
2284 pic[width*y+x] = pal[pixBuf[0]];
2288 if(maskWidth < width && maskHeight < height) {
2289 for(y = 0; y < height; y++) {
2290 for (x = 0; x < width; x++) {
2291 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2295 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2296 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2297 double dx = width / maskWidth;
2298 double dy = height / maskHeight;
2300 for(y = 0; y < maskHeight; y++) {
2302 for (x = 0; x < maskWidth; x++) {
2303 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2304 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2312 height = maskHeight;
2315 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2319 if(maskbitmap) free(maskbitmap);
2324 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2325 int width, int height, GBool invert,
2328 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2329 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2330 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2333 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2334 int width, int height, GfxImageColorMap *colorMap,
2335 int *maskColors, GBool inlineImg)
2337 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2338 colorMap?"colorMap":"no colorMap",
2339 maskColors?"maskColors":"no maskColors",
2341 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2342 colorMap?"colorMap":"no colorMap",
2343 maskColors?"maskColors":"no maskColors",
2346 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2347 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2348 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2351 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2352 int width, int height,
2353 GfxImageColorMap *colorMap,
2354 Stream *maskStr, int maskWidth, int maskHeight,
2357 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2358 colorMap?"colorMap":"no colorMap",
2359 maskWidth, maskHeight);
2360 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2361 colorMap?"colorMap":"no colorMap",
2362 maskWidth, maskHeight);
2364 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2365 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2366 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2369 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2370 int width, int height,
2371 GfxImageColorMap *colorMap,
2373 int maskWidth, int maskHeight,
2374 GfxImageColorMap *maskColorMap)
2376 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2377 colorMap?"colorMap":"no colorMap",
2378 maskWidth, maskHeight);
2379 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2380 colorMap?"colorMap":"no colorMap",
2381 maskWidth, maskHeight);
2383 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2384 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2385 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2388 void GFXOutputDev::stroke(GfxState *state)
2392 GfxPath * path = state->getPath();
2393 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2394 strokeGfxline(state, line, 0);
2398 void GFXOutputDev::fill(GfxState *state)
2400 gfxcolor_t col = getFillColor(state);
2401 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2403 GfxPath * path = state->getPath();
2404 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2405 if(config_optimize_polygons) {
2406 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2410 fillGfxLine(state, line);
2414 void GFXOutputDev::eoFill(GfxState *state)
2416 gfxcolor_t col = getFillColor(state);
2417 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2419 GfxPath * path = state->getPath();
2420 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2421 fillGfxLine(state, line);
2426 static const char* dirseparator()
2435 void addGlobalFont(const char*filename)
2437 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2438 memset(f, 0, sizeof(fontfile_t));
2439 f->filename = filename;
2440 int len = strlen(filename);
2441 char*r1 = strrchr(filename, '/');
2442 char*r2 = strrchr(filename, '\\');
2450 msg("<notice> Adding font \"%s\".", filename);
2451 if(global_fonts_next) {
2452 global_fonts_next->next = f;
2453 global_fonts_next = global_fonts_next->next;
2455 global_fonts_next = global_fonts = f;
2459 void addGlobalLanguageDir(const char*dir)
2461 msg("<notice> Adding %s to language pack directories", dir);
2464 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2465 strcpy(config_file, dir);
2466 strcat(config_file, dirseparator());
2467 strcat(config_file, "add-to-xpdfrc");
2469 fi = fopen(config_file, "rb");
2471 msg("<error> Could not open %s", config_file);
2474 globalParams->parseFile(new GString(config_file), fi);
2478 void addGlobalFontDir(const char*dirname)
2480 #ifdef HAVE_DIRENT_H
2481 msg("<notice> Adding %s to font directories", dirname);
2482 lastfontdir = strdup(dirname);
2483 DIR*dir = opendir(dirname);
2485 msg("<warning> Couldn't open directory %s", dirname);
2490 ent = readdir (dir);
2494 char*name = ent->d_name;
2500 if(!strncasecmp(&name[l-4], ".pfa", 4))
2502 if(!strncasecmp(&name[l-4], ".pfb", 4))
2504 if(!strncasecmp(&name[l-4], ".ttf", 4))
2507 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2508 strcpy(fontname, dirname);
2509 strcat(fontname, dirseparator());
2510 strcat(fontname, name);
2511 addGlobalFont(fontname);
2516 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2520 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2526 this->pagebuflen = 1024;
2527 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2528 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2530 while(pdfpage >= this->pagebuflen)
2532 int oldlen = this->pagebuflen;
2533 this->pagebuflen+=1024;
2534 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2535 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2538 this->pages[pdfpage] = outputpage;
2539 if(pdfpage>this->pagepos)
2540 this->pagepos = pdfpage;
2543 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2544 GfxColorSpace *blendingColorSpace,
2545 GBool isolated, GBool knockout,
2548 const char*colormodename = "";
2550 if(blendingColorSpace) {
2551 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2553 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);
2554 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);
2556 //states[statepos].createsoftmask |= forSoftMask;
2557 states[statepos].createsoftmask = forSoftMask;
2558 states[statepos].transparencygroup = !forSoftMask;
2559 states[statepos].isolated = isolated;
2561 states[statepos].olddevice = this->device;
2562 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2564 gfxdevice_record_init(this->device);
2566 /*if(!forSoftMask) { ////???
2567 state->setFillOpacity(0.0);
2572 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2575 gfxdevice_t*r = this->device;
2577 this->device = states[statepos].olddevice;
2579 gfxresult_t*recording = r->finish(r);
2581 dbg("endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2582 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2584 if(states[statepos].createsoftmask) {
2585 states[statepos-1].softmaskrecording = recording;
2587 states[statepos-1].grouprecording = recording;
2590 states[statepos].createsoftmask = 0;
2591 states[statepos].transparencygroup = 0;
2595 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2597 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2598 "colordodge","colorburn","hardlight","softlight","difference",
2599 "exclusion","hue","saturation","color","luminosity"};
2601 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2602 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2604 if(state->getBlendMode() == gfxBlendNormal)
2605 infofeature("transparency groups");
2608 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2609 warnfeature(buffer, 0);
2612 gfxresult_t*grouprecording = states[statepos].grouprecording;
2614 int blendmode = state->getBlendMode();
2615 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2616 int alpha = (int)(state->getFillOpacity()*255);
2617 if(blendmode == gfxBlendMultiply && alpha>200)
2620 gfxdevice_ops_init(&ops, this->device, alpha);
2621 gfxresult_record_replay(grouprecording, &ops);
2624 grouprecording->destroy(grouprecording);
2626 states[statepos].grouprecording = 0;
2629 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2631 /* alpha = 1: retrieve mask values from alpha layer
2632 alpha = 0: retrieve mask values from luminance */
2633 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2634 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2635 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2636 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2638 infofeature("soft masks");
2640 warnfeature("soft masks from alpha channel",0);
2642 states[statepos].olddevice = this->device;
2643 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2644 gfxdevice_record_init(this->device);
2646 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2648 states[statepos].softmask = 1;
2649 states[statepos].softmask_alpha = alpha;
2652 static inline Guchar div255(int x) {
2653 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2656 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2658 if(c < min) c = min;
2659 if(c > max) c = max;
2663 void GFXOutputDev::clearSoftMask(GfxState *state)
2665 if(!states[statepos].softmask)
2667 states[statepos].softmask = 0;
2668 dbg("clearSoftMask statepos=%d", statepos);
2669 msg("<verbose> clearSoftMask");
2671 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2672 msg("<error> Error in softmask/tgroup ordering");
2676 gfxresult_t*mask = states[statepos].softmaskrecording;
2677 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2678 this->device = states[statepos].olddevice;
2680 /* get outline of all objects below the soft mask */
2681 gfxdevice_t uniondev;
2682 gfxdevice_union_init(&uniondev, 0);
2683 gfxresult_record_replay(below, &uniondev);
2684 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2685 uniondev.finish(&uniondev);
2686 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2687 gfxline_free(belowoutline);belowoutline=0;
2689 this->device->startclip(this->device, belowoutline);
2690 gfxresult_record_replay(below, this->device);
2691 gfxresult_record_replay(mask, this->device);
2692 this->device->endclip(this->device);
2695 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2696 if(width<=0 || height<=0)
2699 gfxdevice_t belowrender;
2700 gfxdevice_render_init(&belowrender);
2701 if(states[statepos+1].isolated) {
2702 belowrender.setparameter(&belowrender, "fillwhite", "1");
2704 belowrender.setparameter(&belowrender, "antialize", "2");
2705 belowrender.startpage(&belowrender, width, height);
2706 gfxresult_record_replay(below, &belowrender);
2707 belowrender.endpage(&belowrender);
2708 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2709 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2710 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2712 gfxdevice_t maskrender;
2713 gfxdevice_render_init(&maskrender);
2714 maskrender.startpage(&maskrender, width, height);
2715 gfxresult_record_replay(mask, &maskrender);
2716 maskrender.endpage(&maskrender);
2717 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2718 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2720 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2721 msg("<fatal> Internal error in mask drawing");
2726 for(y=0;y<height;y++) {
2727 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2728 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2729 for(x=0;x<width;x++) {
2731 if(states[statepos].softmask_alpha) {
2734 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2737 l2->a = div255(alpha*l2->a);
2739 /* DON'T premultiply alpha- this is done by fillbitmap,
2740 depending on the output device */
2741 //l2->r = div255(alpha*l2->r);
2742 //l2->g = div255(alpha*l2->g);
2743 //l2->b = div255(alpha*l2->b);
2749 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2752 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2753 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2755 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2757 mask->destroy(mask);
2758 below->destroy(below);
2759 maskresult->destroy(maskresult);
2760 belowresult->destroy(belowresult);
2761 states[statepos].softmaskrecording = 0;
2766 // public: ~MemCheck()
2768 // delete globalParams;globalParams=0;
2769 // Object::memCheck(stderr);
2770 // gMemReport(stderr);