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 /* notice: we're not fully done yet with this page- there might still be
1030 a few calls to drawLink() yet to come */
1033 #define STROKE_FILL 1
1034 #define STROKE_CLIP 2
1035 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1037 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1038 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1039 double miterLimit = state->getMiterLimit();
1040 double width = state->getTransformedLineWidth();
1043 double opaq = state->getStrokeOpacity();
1045 state->getFillRGB(&rgb);
1047 state->getStrokeRGB(&rgb);
1049 col.r = colToByte(rgb.r);
1050 col.g = colToByte(rgb.g);
1051 col.b = colToByte(rgb.b);
1052 col.a = (unsigned char)(opaq*255);
1054 gfx_capType capType = gfx_capRound;
1055 if(lineCap == 0) capType = gfx_capButt;
1056 else if(lineCap == 1) capType = gfx_capRound;
1057 else if(lineCap == 2) capType = gfx_capSquare;
1059 gfx_joinType joinType = gfx_joinRound;
1060 if(lineJoin == 0) joinType = gfx_joinMiter;
1061 else if(lineJoin == 1) joinType = gfx_joinRound;
1062 else if(lineJoin == 2) joinType = gfx_joinBevel;
1065 double dashphase = 0;
1067 state->getLineDash(&ldash, &dashnum, &dashphase);
1069 gfxline_t*line2 = 0;
1071 if(dashnum && ldash) {
1072 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
1074 msg("<trace> %d dashes", dashnum);
1075 msg("<trace> | phase: %f", dashphase);
1076 for(t=0;t<dashnum;t++) {
1077 dash[t] = (float)ldash[t];
1078 msg("<trace> | d%-3d: %f", t, ldash[t]);
1081 if(getLogLevel() >= LOGLEVEL_TRACE) {
1085 line2 = gfxtool_dash_line(line, dash, (float)dashphase);
1088 msg("<trace> After dashing:");
1091 if(getLogLevel() >= LOGLEVEL_TRACE) {
1092 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1094 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1095 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1097 col.r,col.g,col.b,col.a
1102 if(flags&STROKE_FILL) {
1103 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1104 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1105 if(getLogLevel() >= LOGLEVEL_TRACE) {
1106 dump_outline(gfxline);
1109 msg("<warning> Empty polygon (resulting from stroked line)");
1111 if(flags&STROKE_CLIP) {
1112 device->startclip(device, gfxline);
1113 states[statepos].clipping++;
1115 device->fill(device, gfxline, &col);
1120 if(flags&STROKE_CLIP)
1121 msg("<error> Stroke&clip not supported at the same time");
1122 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1126 gfxline_free(line2);
1129 gfxcolor_t getFillColor(GfxState * state)
1132 double opaq = state->getFillOpacity();
1133 state->getFillRGB(&rgb);
1135 col.r = colToByte(rgb.r);
1136 col.g = colToByte(rgb.g);
1137 col.b = colToByte(rgb.b);
1138 col.a = (unsigned char)(opaq*255);
1142 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1144 gfxcolor_t col = getFillColor(state);
1146 if(getLogLevel() >= LOGLEVEL_TRACE) {
1147 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1150 device->fill(device, line, &col);
1153 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1155 if(getLogLevel() >= LOGLEVEL_TRACE) {
1158 gfxbbox_t bbox = gfxline_getbbox(line);
1159 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1161 device->startclip(device, line);
1162 states[statepos].clipping++;
1165 void GFXOutputDev::clip(GfxState *state)
1167 GfxPath * path = state->getPath();
1168 msg("<trace> clip");
1169 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1170 if(config_optimize_polygons) {
1171 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1175 clipToGfxLine(state, line);
1179 void GFXOutputDev::eoClip(GfxState *state)
1181 GfxPath * path = state->getPath();
1182 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1183 clipToGfxLine(state, line);
1186 void GFXOutputDev::clipToStrokePath(GfxState *state)
1188 GfxPath * path = state->getPath();
1189 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1191 if(getLogLevel() >= LOGLEVEL_TRACE) {
1192 msg("<trace> cliptostrokepath");
1196 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1200 void GFXOutputDev::finish()
1202 if(outer_clip_box) {
1204 device->endclip(device);
1210 GFXOutputDev::~GFXOutputDev()
1215 free(this->pages); this->pages = 0;
1218 feature_t*f = this->featurewarnings;
1220 feature_t*next = f->next;
1222 free(f->string);f->string =0;
1228 this->featurewarnings = 0;
1230 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1232 GBool GFXOutputDev::upsideDown()
1236 GBool GFXOutputDev::useDrawChar()
1241 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1242 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1244 static char tmp_printstr[4096];
1245 char* makeStringPrintable(char*str)
1247 int len = strlen(str);
1254 for(t=0;t<len;t++) {
1259 tmp_printstr[t] = c;
1262 tmp_printstr[len++] = '.';
1263 tmp_printstr[len++] = '.';
1264 tmp_printstr[len++] = '.';
1266 tmp_printstr[len] = 0;
1267 return tmp_printstr;
1269 #define INTERNAL_FONT_SIZE 1024.0
1270 void GFXOutputDev::updateFontMatrix(GfxState*state)
1272 double* ctm = state->getCTM();
1273 double fontSize = state->getFontSize();
1274 double*textMat = state->getTextMat();
1276 /* taking the absolute value of horizScaling seems to be required for
1277 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1278 double hscale = fabs(state->getHorizScaling());
1280 // from xpdf-3.02/SplashOutputDev:updateFont
1281 double mm11 = textMat[0] * fontSize * hscale;
1282 double mm12 = textMat[1] * fontSize * hscale;
1283 double mm21 = textMat[2] * fontSize;
1284 double mm22 = textMat[3] * fontSize;
1286 // multiply with ctm, like state->getFontTransMat() does
1287 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1288 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1289 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1290 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1291 this->current_font_matrix.tx = 0;
1292 this->current_font_matrix.ty = 0;
1295 void GFXOutputDev::beginString(GfxState *state, GString *s)
1297 int render = state->getRender();
1298 if(current_text_stroke) {
1299 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1302 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1305 static gfxline_t* mkEmptyGfxShape(double x, double y)
1307 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1308 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1312 static char isValidUnicode(int c)
1314 if(c>=32 && c<0x2fffe)
1319 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1320 double dx, double dy,
1321 double originX, double originY,
1322 CharCode charid, int nBytes, Unicode *_u, int uLen)
1324 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1325 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1329 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1331 int render = state->getRender();
1332 gfxcolor_t col = getFillColor(state);
1334 // check for invisible text -- this is used by Acrobat Capture
1335 if (render == RENDER_INVISIBLE) {
1337 if(!config_extrafontdata)
1341 GfxFont*font = state->getFont();
1343 if(font->getType() == fontType3) {
1344 /* type 3 chars are passed as graphics */
1345 msg("<debug> type3 char at %f/%f", x, y);
1349 Unicode u = uLen?(_u[0]):0;
1350 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);
1352 gfxmatrix_t m = this->current_font_matrix;
1353 this->transformXY(state, x, y, &m.tx, &m.ty);
1355 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1356 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1358 msg("<debug> Drawing glyph %d as shape", charid);
1360 msg("<notice> Some texts will be rendered as shape");
1363 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1364 gfxline_t*tglyph = gfxline_clone(glyph);
1365 gfxline_transform(tglyph, &m);
1366 if((render&3) != RENDER_INVISIBLE) {
1367 gfxline_t*add = gfxline_clone(tglyph);
1368 current_text_stroke = gfxline_append(current_text_stroke, add);
1370 if(render&RENDER_CLIP) {
1371 gfxline_t*add = gfxline_clone(tglyph);
1372 current_text_clip = gfxline_append(current_text_clip, add);
1373 if(!current_text_clip) {
1374 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1377 gfxline_free(tglyph);
1381 void GFXOutputDev::endString(GfxState *state)
1383 int render = state->getRender();
1384 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1386 if(current_text_stroke) {
1387 /* fillstroke and stroke text rendering objects we can process right
1388 now (as there may be texts of other rendering modes in this
1389 text object)- clipping objects have to wait until endTextObject,
1391 device->setparameter(device, "mark","TXT");
1392 if((render&3) == RENDER_FILL) {
1393 fillGfxLine(state, current_text_stroke);
1394 gfxline_free(current_text_stroke);
1395 current_text_stroke = 0;
1396 } else if((render&3) == RENDER_FILLSTROKE) {
1397 fillGfxLine(state, current_text_stroke);
1398 strokeGfxline(state, current_text_stroke,0);
1399 gfxline_free(current_text_stroke);
1400 current_text_stroke = 0;
1401 } else if((render&3) == RENDER_STROKE) {
1402 strokeGfxline(state, current_text_stroke,0);
1403 gfxline_free(current_text_stroke);
1404 current_text_stroke = 0;
1406 device->setparameter(device, "mark","");
1410 void GFXOutputDev::endTextObject(GfxState *state)
1412 int render = state->getRender();
1413 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1415 if(current_text_clip) {
1416 device->setparameter(device, "mark","TXT");
1417 clipToGfxLine(state, current_text_clip);
1418 device->setparameter(device, "mark","");
1419 gfxline_free(current_text_clip);
1420 current_text_clip = 0;
1424 /* the logic seems to be as following:
1425 first, beginType3Char is called, with the charcode and the coordinates.
1426 if this function returns true, it already knew about the char and has now drawn it.
1427 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1428 called with some parameters.
1429 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1430 at the position first passed to beginType3Char). the char ends with endType3Char.
1432 The drawing operations between beginType3Char and endType3Char are somewhat different to
1433 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1434 color determines the color of a font)
1437 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1439 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1442 if(config_extrafontdata && current_fontinfo) {
1444 gfxmatrix_t m = this->current_font_matrix;
1445 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1446 m.m00*=INTERNAL_FONT_SIZE;
1447 m.m01*=INTERNAL_FONT_SIZE;
1448 m.m10*=INTERNAL_FONT_SIZE;
1449 m.m11*=INTERNAL_FONT_SIZE;
1451 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1452 msg("<error> Invalid charid %d for font", charid);
1455 gfxcolor_t col={0,0,0,0};
1456 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1457 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1461 /* the character itself is going to be passed using the draw functions */
1462 return gFalse; /* gTrue= is_in_cache? */
1465 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1467 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1470 void GFXOutputDev::endType3Char(GfxState *state)
1473 msg("<debug> endType3Char");
1476 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1478 this->currentpage = pageNum;
1480 int rot = doc->getPageRotate(1);
1481 gfxcolor_t white = {255,255,255,255};
1482 gfxcolor_t black = {255,0,0,0};
1484 gfxline_t clippath[5];
1486 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1487 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1488 Use CropBox, not MediaBox, as page size
1495 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1496 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1498 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1499 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1501 this->clipmovex = -(int)x1;
1502 this->clipmovey = -(int)y1;
1504 /* apply user clip box */
1505 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1506 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1507 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1508 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1509 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1510 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1513 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1515 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);
1517 msg("<verbose> page is rotated %d degrees", rot);
1519 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1520 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1521 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1522 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1523 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1524 device->startclip(device, clippath); outer_clip_box = 1;
1525 if(!config_transparent) {
1526 device->fill(device, clippath, &white);
1528 states[statepos].clipbbox.xmin = x1;
1529 states[statepos].clipbbox.ymin = x1;
1530 states[statepos].clipbbox.xmax = x2;
1531 states[statepos].clipbbox.ymax = y2;
1535 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1537 double x1, y1, x2, y2;
1538 gfxline_t points[5];
1541 msg("<debug> drawlink");
1543 link->getRect(&x1, &y1, &x2, &y2);
1544 cvtUserToDev(x1, y1, &x, &y);
1545 points[0].type = gfx_moveTo;
1546 points[0].x = points[4].x = x + user_movex + clipmovex;
1547 points[0].y = points[4].y = y + user_movey + clipmovey;
1548 points[0].next = &points[1];
1549 cvtUserToDev(x2, y1, &x, &y);
1550 points[1].type = gfx_lineTo;
1551 points[1].x = x + user_movex + clipmovex;
1552 points[1].y = y + user_movey + clipmovey;
1553 points[1].next = &points[2];
1554 cvtUserToDev(x2, y2, &x, &y);
1555 points[2].type = gfx_lineTo;
1556 points[2].x = x + user_movex + clipmovex;
1557 points[2].y = y + user_movey + clipmovey;
1558 points[2].next = &points[3];
1559 cvtUserToDev(x1, y2, &x, &y);
1560 points[3].type = gfx_lineTo;
1561 points[3].x = x + user_movex + clipmovex;
1562 points[3].y = y + user_movey + clipmovey;
1563 points[3].next = &points[4];
1564 cvtUserToDev(x1, y1, &x, &y);
1565 points[4].type = gfx_lineTo;
1566 points[4].x = x + user_movex + clipmovex;
1567 points[4].y = y + user_movey + clipmovey;
1570 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1571 points[0].x, points[0].y,
1572 points[1].x, points[1].y,
1573 points[2].x, points[2].y,
1574 points[3].x, points[3].y);
1576 if(getLogLevel() >= LOGLEVEL_TRACE) {
1577 dump_outline(points);
1580 LinkAction*action=link->getAction();
1583 const char*type = "-?-";
1586 msg("<trace> drawlink action=%d", action->getKind());
1587 switch(action->getKind())
1591 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1592 LinkDest *dest=NULL;
1593 if (ha->getDest()==NULL)
1594 dest=catalog->findDest(ha->getNamedDest());
1595 else dest=ha->getDest();
1597 if (dest->isPageRef()){
1598 Ref pageref=dest->getPageRef();
1599 page=catalog->findPage(pageref.num,pageref.gen);
1601 else page=dest->getPageNum();
1602 sprintf(buf, "%d", page);
1609 LinkGoToR*l = (LinkGoToR*)action;
1610 GString*g = l->getFileName();
1612 s = strdup(g->getCString());
1614 /* if the GoToR link has no filename, then
1615 try to find a refernce in the *local*
1617 GString*g = l->getNamedDest();
1619 s = strdup(g->getCString());
1625 LinkNamed*l = (LinkNamed*)action;
1626 GString*name = l->getName();
1628 s = strdup(name->lowerCase()->getCString());
1629 named = name->getCString();
1632 if(strstr(s, "next") || strstr(s, "forward"))
1634 page = currentpage + 1;
1636 else if(strstr(s, "prev") || strstr(s, "back"))
1638 page = currentpage - 1;
1640 else if(strstr(s, "last") || strstr(s, "end"))
1642 if(pages && pagepos>0)
1643 page = pages[pagepos-1];
1645 else if(strstr(s, "first") || strstr(s, "top"))
1653 case actionLaunch: {
1655 LinkLaunch*l = (LinkLaunch*)action;
1656 GString * str = new GString(l->getFileName());
1657 GString * params = l->getParams();
1659 str->append(params);
1660 s = strdup(str->getCString());
1667 LinkURI*l = (LinkURI*)action;
1668 GString*g = l->getURI();
1670 url = g->getCString();
1675 case actionUnknown: {
1677 LinkUnknown*l = (LinkUnknown*)action;
1682 msg("<error> Unknown link type!");
1687 if(!s) s = strdup("-?-");
1689 msg("<trace> drawlink s=%s", s);
1691 if(!linkinfo && (page || s))
1693 msg("<notice> File contains links");
1701 for(t=1;t<=pagepos;t++) {
1702 if(pages[t]==page) {
1711 sprintf(buf, "page%d", lpage);
1712 device->drawlink(device, points, buf);
1716 device->drawlink(device, points, s);
1719 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1723 void GFXOutputDev::saveState(GfxState *state) {
1724 dbg("saveState");dbgindent+=2;
1726 msg("<trace> saveState");
1729 msg("<error> Too many nested states in pdf.");
1733 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1734 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1735 states[statepos].clipping = 0;
1736 states[statepos].clipbbox = states[statepos-1].clipbbox;
1739 void GFXOutputDev::restoreState(GfxState *state) {
1740 dbgindent-=2; dbg("restoreState");
1743 msg("<error> Invalid restoreState");
1746 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1747 states[statepos].clipping?" (end clipping)":"");
1748 if(states[statepos].softmask) {
1749 clearSoftMask(state);
1752 while(states[statepos].clipping) {
1753 device->endclip(device);
1754 states[statepos].clipping--;
1759 void GFXOutputDev::updateLineWidth(GfxState *state)
1761 double width = state->getTransformedLineWidth();
1762 //swfoutput_setlinewidth(&device, width);
1765 void GFXOutputDev::updateLineCap(GfxState *state)
1767 int c = state->getLineCap();
1770 void GFXOutputDev::updateLineJoin(GfxState *state)
1772 int j = state->getLineJoin();
1775 void GFXOutputDev::updateFillColor(GfxState *state)
1778 double opaq = state->getFillOpacity();
1779 state->getFillRGB(&rgb);
1781 void GFXOutputDev::updateFillOpacity(GfxState *state)
1784 double opaq = state->getFillOpacity();
1785 state->getFillRGB(&rgb);
1786 dbg("update fillopaq %f", opaq);
1788 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1790 double opaq = state->getFillOpacity();
1791 dbg("update strokeopaq %f", opaq);
1793 void GFXOutputDev::updateFillOverprint(GfxState *state)
1795 double opaq = state->getFillOverprint();
1796 dbg("update filloverprint %f", opaq);
1798 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1800 double opaq = state->getStrokeOverprint();
1801 dbg("update strokeoverprint %f", opaq);
1803 void GFXOutputDev::updateTransfer(GfxState *state)
1805 dbg("update transfer");
1809 void GFXOutputDev::updateStrokeColor(GfxState *state)
1812 double opaq = state->getStrokeOpacity();
1813 state->getStrokeRGB(&rgb);
1817 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1819 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1820 memset(font, 0, sizeof(gfxfont_t));
1822 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1823 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1827 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1829 //printf("%d glyphs\n", font->num_glyphs);
1830 font->num_glyphs = 0;
1831 for(t=0;t<src->num_glyphs;t++) {
1832 if(src->glyphs[t]) {
1833 SplashPath*path = src->glyphs[t]->path;
1834 int len = path?path->getLength():0;
1835 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1836 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1837 src->glyphs[t]->glyphid = font->num_glyphs;
1838 glyph->unicode = src->glyphs[t]->unicode;
1839 if(glyph->unicode >= font->max_unicode)
1840 font->max_unicode = glyph->unicode+1;
1842 gfxdrawer_target_gfxline(&drawer);
1846 for(s=0;s<len;s++) {
1849 path->getPoint(s, &x, &y, &f);
1852 if(f&splashPathFirst) {
1853 drawer.moveTo(&drawer, x*scale, y*scale);
1855 if(f&splashPathCurve) {
1857 path->getPoint(++s, &x2, &y2, &f);
1858 if(f&splashPathCurve) {
1860 path->getPoint(++s, &x3, &y3, &f);
1861 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1863 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1866 drawer.lineTo(&drawer, x*scale, y*scale);
1868 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1869 // (f&splashPathFirst)?"first":"",
1870 // (f&splashPathLast)?"last":"");
1872 glyph->line = (gfxline_t*)drawer.result(&drawer);
1873 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1877 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1878 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1879 for(t=0;t<font->num_glyphs;t++) {
1880 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1881 font->unicode2glyph[font->glyphs[t].unicode] = t;
1885 msg("<trace> %d glyphs.", t, font->num_glyphs);
1889 void GFXOutputDev::updateFont(GfxState *state)
1891 GfxFont* gfxFont = state->getFont();
1895 char*id = getFontID(gfxFont);
1896 msg("<verbose> Updating font to %s", id);
1897 if(gfxFont->getType() == fontType3) {
1898 infofeature("Type3 fonts");
1899 if(!config_extrafontdata) {
1904 msg("<error> Internal Error: FontID is null");
1908 this->current_fontinfo = this->info->getFont(id);
1909 if(!this->current_fontinfo) {
1910 msg("<error> Internal Error: no fontinfo for font %s", id);
1913 if(!this->current_fontinfo->seen) {
1914 dumpFontInfo("<verbose>", gfxFont);
1917 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1919 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1920 font->id = strdup(id);
1921 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1923 device->addfont(device, font);
1925 current_gfxfont = font;
1928 updateFontMatrix(state);
1931 #define SQR(x) ((x)*(x))
1933 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1935 if((newwidth<2 || newheight<2) ||
1936 (width<=newwidth || height<=newheight))
1938 unsigned char*newdata;
1940 newdata= (unsigned char*)malloc(newwidth*newheight);
1941 double fx = (double)(width)/newwidth;
1942 double fy = (double)(height)/newheight;
1944 int blocksize = (int)(8192/(fx*fy));
1945 int r = 8192*256/palettesize;
1946 for(x=0;x<newwidth;x++) {
1947 double ex = px + fx;
1948 int fromx = (int)px;
1950 int xweight1 = (int)(((fromx+1)-px)*256);
1951 int xweight2 = (int)((ex-tox)*256);
1953 for(y=0;y<newheight;y++) {
1954 double ey = py + fy;
1955 int fromy = (int)py;
1957 int yweight1 = (int)(((fromy+1)-py)*256);
1958 int yweight2 = (int)((ey-toy)*256);
1961 for(xx=fromx;xx<=tox;xx++)
1962 for(yy=fromy;yy<=toy;yy++) {
1963 int b = 1-data[width*yy+xx];
1965 if(xx==fromx) weight = (weight*xweight1)/256;
1966 if(xx==tox) weight = (weight*xweight2)/256;
1967 if(yy==fromy) weight = (weight*yweight1)/256;
1968 if(yy==toy) weight = (weight*yweight2)/256;
1971 //if(a) a=(palettesize-1)*r/blocksize;
1972 newdata[y*newwidth+x] = (a*blocksize)/r;
1980 #define IMAGE_TYPE_JPEG 0
1981 #define IMAGE_TYPE_LOSSLESS 1
1983 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1984 double x1,double y1,
1985 double x2,double y2,
1986 double x3,double y3,
1987 double x4,double y4, int type)
1989 gfxcolor_t*newpic=0;
1991 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1992 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1994 gfxline_t p1,p2,p3,p4,p5;
1995 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1996 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1997 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1998 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1999 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2001 {p1.x = (int)(p1.x*20)/20.0;
2002 p1.y = (int)(p1.y*20)/20.0;
2003 p2.x = (int)(p2.x*20)/20.0;
2004 p2.y = (int)(p2.y*20)/20.0;
2005 p3.x = (int)(p3.x*20)/20.0;
2006 p3.y = (int)(p3.y*20)/20.0;
2007 p4.x = (int)(p4.x*20)/20.0;
2008 p4.y = (int)(p4.y*20)/20.0;
2009 p5.x = (int)(p5.x*20)/20.0;
2010 p5.y = (int)(p5.y*20)/20.0;
2014 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2015 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2020 img.data = (gfxcolor_t*)data;
2024 if(type == IMAGE_TYPE_JPEG)
2025 /* TODO: pass image_dpi to device instead */
2026 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2028 dev->fillbitmap(dev, &p1, &img, &m, 0);
2031 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2032 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2034 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2037 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2038 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2040 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2044 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2045 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2046 GBool inlineImg, int mask, int*maskColors,
2047 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2049 double x1,y1,x2,y2,x3,y3,x4,y4;
2050 ImageStream *imgStr;
2055 unsigned char* maskbitmap = 0;
2058 ncomps = colorMap->getNumPixelComps();
2059 bits = colorMap->getBits();
2064 unsigned char buf[8];
2065 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2067 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2068 imgMaskStr->reset();
2069 unsigned char pal[256];
2070 int n = 1 << colorMap->getBits();
2075 maskColorMap->getGray(pixBuf, &gray);
2076 pal[t] = colToByte(gray);
2078 for (y = 0; y < maskHeight; y++) {
2079 for (x = 0; x < maskWidth; x++) {
2080 imgMaskStr->getPixel(buf);
2081 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2086 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2087 imgMaskStr->reset();
2088 for (y = 0; y < maskHeight; y++) {
2089 for (x = 0; x < maskWidth; x++) {
2090 imgMaskStr->getPixel(buf);
2092 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2100 imgStr = new ImageStream(str, width, ncomps,bits);
2103 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2105 msg("<verbose> Ignoring %d by %d image", width, height);
2106 unsigned char buf[8];
2108 for (y = 0; y < height; ++y)
2109 for (x = 0; x < width; ++x) {
2110 imgStr->getPixel(buf);
2118 this->transformXY(state, 0, 1, &x1, &y1);
2119 this->transformXY(state, 0, 0, &x2, &y2);
2120 this->transformXY(state, 1, 0, &x3, &y3);
2121 this->transformXY(state, 1, 1, &x4, &y4);
2123 if(!pbminfo && !(str->getKind()==strDCT)) {
2125 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2129 msg("<verbose> drawing %d by %d masked picture", width, height);
2131 if(!jpeginfo && (str->getKind()==strDCT)) {
2132 msg("<notice> File contains jpeg pictures");
2137 unsigned char buf[8];
2139 unsigned char*pic = new unsigned char[width*height];
2140 gfxcolor_t pal[256];
2142 state->getFillRGB(&rgb);
2144 memset(pal,255,sizeof(pal));
2145 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2146 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2147 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2148 pal[0].a = 255; pal[1].a = 0;
2151 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2152 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2153 for (y = 0; y < height; ++y)
2154 for (x = 0; x < width; ++x)
2156 imgStr->getPixel(buf);
2159 pic[width*y+x] = buf[0];
2162 /* the size of the drawn image is added to the identifier
2163 as the same image may require different bitmaps if displayed
2164 at different sizes (due to antialiasing): */
2167 unsigned char*pic2 = 0;
2170 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2179 height = realheight;
2183 /* make a black/white palette */
2185 float r = 255./(float)(numpalette-1);
2187 for(t=0;t<numpalette;t++) {
2188 pal[t].r = colToByte(rgb.r);
2189 pal[t].g = colToByte(rgb.g);
2190 pal[t].b = colToByte(rgb.b);
2191 pal[t].a = (unsigned char)(t*r);
2195 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2196 for (y = 0; y < height; ++y) {
2197 for (x = 0; x < width; ++x) {
2198 pic2[width*y+x] = pal[pic[y*width+x]];
2201 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2205 if(maskbitmap) free(maskbitmap);
2211 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2212 gfxcolor_t*pic=new gfxcolor_t[width*height];
2213 for (y = 0; y < height; ++y) {
2214 for (x = 0; x < width; ++x) {
2215 imgStr->getPixel(pixBuf);
2216 colorMap->getRGB(pixBuf, &rgb);
2217 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2218 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2219 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2220 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2222 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2226 if(str->getKind()==strDCT)
2227 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2229 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2232 if(maskbitmap) free(maskbitmap);
2235 gfxcolor_t*pic=new gfxcolor_t[width*height];
2236 gfxcolor_t pal[256];
2237 int n = 1 << colorMap->getBits();
2239 for(t=0;t<256;t++) {
2241 colorMap->getRGB(pixBuf, &rgb);
2243 {/*if(maskColors && *maskColors==t) {
2244 msg("<notice> Color %d is transparent", t);
2245 if (imgData->maskColors) {
2247 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2248 if (pix[i] < imgData->maskColors[2*i] ||
2249 pix[i] > imgData->maskColors[2*i+1]) {
2264 pal[t].r = (unsigned char)(colToByte(rgb.r));
2265 pal[t].g = (unsigned char)(colToByte(rgb.g));
2266 pal[t].b = (unsigned char)(colToByte(rgb.b));
2267 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2270 for (y = 0; y < height; ++y) {
2271 for (x = 0; x < width; ++x) {
2272 imgStr->getPixel(pixBuf);
2273 pic[width*y+x] = pal[pixBuf[0]];
2277 if(maskWidth < width && maskHeight < height) {
2278 for(y = 0; y < height; y++) {
2279 for (x = 0; x < width; x++) {
2280 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2284 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2285 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2286 double dx = width / maskWidth;
2287 double dy = height / maskHeight;
2289 for(y = 0; y < maskHeight; y++) {
2291 for (x = 0; x < maskWidth; x++) {
2292 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2293 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2301 height = maskHeight;
2304 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2308 if(maskbitmap) free(maskbitmap);
2313 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2314 int width, int height, GBool invert,
2317 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2318 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2319 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2322 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2323 int width, int height, GfxImageColorMap *colorMap,
2324 int *maskColors, GBool inlineImg)
2326 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2327 colorMap?"colorMap":"no colorMap",
2328 maskColors?"maskColors":"no maskColors",
2330 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2331 colorMap?"colorMap":"no colorMap",
2332 maskColors?"maskColors":"no maskColors",
2335 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2336 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2337 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2340 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2341 int width, int height,
2342 GfxImageColorMap *colorMap,
2343 Stream *maskStr, int maskWidth, int maskHeight,
2346 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2347 colorMap?"colorMap":"no colorMap",
2348 maskWidth, maskHeight);
2349 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2350 colorMap?"colorMap":"no colorMap",
2351 maskWidth, maskHeight);
2353 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2354 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2355 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2358 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2359 int width, int height,
2360 GfxImageColorMap *colorMap,
2362 int maskWidth, int maskHeight,
2363 GfxImageColorMap *maskColorMap)
2365 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2366 colorMap?"colorMap":"no colorMap",
2367 maskWidth, maskHeight);
2368 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2369 colorMap?"colorMap":"no colorMap",
2370 maskWidth, maskHeight);
2372 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2373 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2374 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2377 void GFXOutputDev::stroke(GfxState *state)
2381 GfxPath * path = state->getPath();
2382 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2383 strokeGfxline(state, line, 0);
2387 void GFXOutputDev::fill(GfxState *state)
2389 gfxcolor_t col = getFillColor(state);
2390 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2392 GfxPath * path = state->getPath();
2393 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2394 if(config_optimize_polygons) {
2395 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2399 fillGfxLine(state, line);
2403 void GFXOutputDev::eoFill(GfxState *state)
2405 gfxcolor_t col = getFillColor(state);
2406 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2408 GfxPath * path = state->getPath();
2409 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2410 fillGfxLine(state, line);
2415 static const char* dirseparator()
2424 void addGlobalFont(const char*filename)
2426 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2427 memset(f, 0, sizeof(fontfile_t));
2428 f->filename = filename;
2429 int len = strlen(filename);
2430 char*r1 = strrchr(filename, '/');
2431 char*r2 = strrchr(filename, '\\');
2439 msg("<notice> Adding font \"%s\".", filename);
2440 if(global_fonts_next) {
2441 global_fonts_next->next = f;
2442 global_fonts_next = global_fonts_next->next;
2444 global_fonts_next = global_fonts = f;
2448 void addGlobalLanguageDir(const char*dir)
2450 msg("<notice> Adding %s to language pack directories", dir);
2453 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2454 strcpy(config_file, dir);
2455 strcat(config_file, dirseparator());
2456 strcat(config_file, "add-to-xpdfrc");
2458 fi = fopen(config_file, "rb");
2460 msg("<error> Could not open %s", config_file);
2463 globalParams->parseFile(new GString(config_file), fi);
2467 void addGlobalFontDir(const char*dirname)
2469 #ifdef HAVE_DIRENT_H
2470 msg("<notice> Adding %s to font directories", dirname);
2471 lastfontdir = strdup(dirname);
2472 DIR*dir = opendir(dirname);
2474 msg("<warning> Couldn't open directory %s", dirname);
2479 ent = readdir (dir);
2483 char*name = ent->d_name;
2489 if(!strncasecmp(&name[l-4], ".pfa", 4))
2491 if(!strncasecmp(&name[l-4], ".pfb", 4))
2493 if(!strncasecmp(&name[l-4], ".ttf", 4))
2496 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2497 strcpy(fontname, dirname);
2498 strcat(fontname, dirseparator());
2499 strcat(fontname, name);
2500 addGlobalFont(fontname);
2505 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2509 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2515 this->pagebuflen = 1024;
2516 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2517 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2519 while(pdfpage >= this->pagebuflen)
2521 int oldlen = this->pagebuflen;
2522 this->pagebuflen+=1024;
2523 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2524 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2527 this->pages[pdfpage] = outputpage;
2528 if(pdfpage>this->pagepos)
2529 this->pagepos = pdfpage;
2532 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2533 GfxColorSpace *blendingColorSpace,
2534 GBool isolated, GBool knockout,
2537 const char*colormodename = "";
2539 if(blendingColorSpace) {
2540 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2542 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);
2543 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);
2545 //states[statepos].createsoftmask |= forSoftMask;
2546 states[statepos].createsoftmask = forSoftMask;
2547 states[statepos].transparencygroup = !forSoftMask;
2548 states[statepos].isolated = isolated;
2550 states[statepos].olddevice = this->device;
2551 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2553 gfxdevice_record_init(this->device);
2555 /*if(!forSoftMask) { ////???
2556 state->setFillOpacity(0.0);
2561 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2564 gfxdevice_t*r = this->device;
2566 this->device = states[statepos].olddevice;
2568 gfxresult_t*recording = r->finish(r);
2570 dbg("endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2571 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2573 if(states[statepos].createsoftmask) {
2574 states[statepos-1].softmaskrecording = recording;
2576 states[statepos-1].grouprecording = recording;
2579 states[statepos].createsoftmask = 0;
2580 states[statepos].transparencygroup = 0;
2584 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2586 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2587 "colordodge","colorburn","hardlight","softlight","difference",
2588 "exclusion","hue","saturation","color","luminosity"};
2590 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2591 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2593 if(state->getBlendMode() == gfxBlendNormal)
2594 infofeature("transparency groups");
2597 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2598 warnfeature(buffer, 0);
2601 gfxresult_t*grouprecording = states[statepos].grouprecording;
2603 int blendmode = state->getBlendMode();
2604 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2605 int alpha = (int)(state->getFillOpacity()*255);
2606 if(blendmode == gfxBlendMultiply && alpha>200)
2609 gfxdevice_ops_init(&ops, this->device, alpha);
2610 gfxresult_record_replay(grouprecording, &ops);
2613 grouprecording->destroy(grouprecording);
2615 states[statepos].grouprecording = 0;
2618 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2620 /* alpha = 1: retrieve mask values from alpha layer
2621 alpha = 0: retrieve mask values from luminance */
2622 dbg("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]));
2624 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2625 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2627 infofeature("soft masks");
2629 warnfeature("soft masks from alpha channel",0);
2631 states[statepos].olddevice = this->device;
2632 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2633 gfxdevice_record_init(this->device);
2635 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2637 states[statepos].softmask = 1;
2638 states[statepos].softmask_alpha = alpha;
2641 static inline Guchar div255(int x) {
2642 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2645 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2647 if(c < min) c = min;
2648 if(c > max) c = max;
2652 void GFXOutputDev::clearSoftMask(GfxState *state)
2654 if(!states[statepos].softmask)
2656 states[statepos].softmask = 0;
2657 dbg("clearSoftMask statepos=%d", statepos);
2658 msg("<verbose> clearSoftMask");
2660 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2661 msg("<error> Error in softmask/tgroup ordering");
2665 gfxresult_t*mask = states[statepos].softmaskrecording;
2666 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2667 this->device = states[statepos].olddevice;
2669 /* get outline of all objects below the soft mask */
2670 gfxdevice_t uniondev;
2671 gfxdevice_union_init(&uniondev, 0);
2672 gfxresult_record_replay(below, &uniondev);
2673 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2674 uniondev.finish(&uniondev);
2675 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2676 gfxline_free(belowoutline);belowoutline=0;
2678 this->device->startclip(this->device, belowoutline);
2679 gfxresult_record_replay(below, this->device);
2680 gfxresult_record_replay(mask, this->device);
2681 this->device->endclip(this->device);
2684 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2685 if(width<=0 || height<=0)
2688 gfxdevice_t belowrender;
2689 gfxdevice_render_init(&belowrender);
2690 if(states[statepos+1].isolated) {
2691 belowrender.setparameter(&belowrender, "fillwhite", "1");
2693 belowrender.setparameter(&belowrender, "antialize", "2");
2694 belowrender.startpage(&belowrender, width, height);
2695 gfxresult_record_replay(below, &belowrender);
2696 belowrender.endpage(&belowrender);
2697 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2698 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2699 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2701 gfxdevice_t maskrender;
2702 gfxdevice_render_init(&maskrender);
2703 maskrender.startpage(&maskrender, width, height);
2704 gfxresult_record_replay(mask, &maskrender);
2705 maskrender.endpage(&maskrender);
2706 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2707 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2709 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2710 msg("<fatal> Internal error in mask drawing");
2715 for(y=0;y<height;y++) {
2716 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2717 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2718 for(x=0;x<width;x++) {
2720 if(states[statepos].softmask_alpha) {
2723 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2726 l2->a = div255(alpha*l2->a);
2728 /* DON'T premultiply alpha- this is done by fillbitmap,
2729 depending on the output device */
2730 //l2->r = div255(alpha*l2->r);
2731 //l2->g = div255(alpha*l2->g);
2732 //l2->b = div255(alpha*l2->b);
2738 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2741 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2742 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2744 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2746 mask->destroy(mask);
2747 below->destroy(below);
2748 maskresult->destroy(maskresult);
2749 belowresult->destroy(belowresult);
2750 states[statepos].softmaskrecording = 0;
2755 // public: ~MemCheck()
2757 // delete globalParams;globalParams=0;
2758 // Object::memCheck(stderr);
2759 // gMemReport(stderr);