2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
28 #include "../../config.h"
33 #ifdef HAVE_SYS_STAT_H
36 #ifdef HAVE_FONTCONFIG
37 #include <fontconfig.h>
54 #include "OutputDev.h"
57 #include "CharCodeToUnicode.h"
58 #include "NameToUnicodeTable.h"
59 #include "GlobalParams.h"
60 #include "FoFiType1C.h"
61 #include "FoFiTrueType.h"
63 #include "GFXOutputDev.h"
65 // swftools header files
67 #include "../gfxdevice.h"
68 #include "../gfxtools.h"
69 #include "../gfxfont.h"
70 #include "../gfxpoly.h"
71 #include "../devices/record.h"
72 #include "../devices/ops.h"
73 #include "../devices/polyops.h"
74 #include "../devices/render.h"
81 #define SQRT2 1.41421356237309504880
83 typedef struct _fontfile
86 int len; // basename length
88 struct _fontfile*next;
93 static fontfile_t* global_fonts = 0;
94 static fontfile_t* global_fonts_next = 0;
96 static int fontnum = 0;
100 static char* lastfontdir = 0;
111 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
112 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
113 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
114 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
115 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
116 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
117 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
118 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
119 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
120 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
121 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
122 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
123 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
124 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
127 static int verbose = 0;
128 static int dbgindent = 0;
129 static void dbg(const char*format, ...)
136 va_start(arglist, format);
137 vsprintf(buf, format, arglist);
140 while(l && buf[l-1]=='\n') {
145 int indent = dbgindent;
155 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
157 feature_t*f = this->featurewarnings;
159 if(!strcmp(feature, f->string))
163 f = (feature_t*)malloc(sizeof(feature_t));
164 f->string = strdup(feature);
165 f->next = this->featurewarnings;
166 this->featurewarnings = f;
168 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
169 if(this->config_break_on_warning) {
170 msg("<fatal> Aborting conversion due to unsupported feature");
174 msg("<notice> File contains %s",feature);
177 void GFXOutputDev::warnfeature(const char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(const char*feature)
183 showfeature(feature,0,0);
186 GFXOutputState::GFXOutputState() {
188 this->createsoftmask = 0;
189 this->transparencygroup = 0;
191 this->grouprecording = 0;
195 GBool GFXOutputDev::interpretType3Chars()
200 typedef struct _drawnchar
218 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
219 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224 free(chars);chars = 0;
231 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
235 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
238 chars[num_chars].x = x;
239 chars[num_chars].y = y;
240 chars[num_chars].color = color;
241 chars[num_chars].charid = charid;
245 char* writeOutStdFont(fontentry* f)
250 char* tmpFileName = mktmpname(namebuf1);
252 sprintf(namebuf2, "%s.afm", tmpFileName);
253 fi = fopen(namebuf2, "wb");
256 fwrite(f->afm, 1, f->afmlen, fi);
259 sprintf(namebuf2, "%s.pfb", tmpFileName);
260 fi = fopen(namebuf2, "wb");
263 fwrite(f->pfb, 1, f->pfblen, fi);
265 return strdup(namebuf2);
267 void unlinkfont(char* filename)
272 msg("<verbose> Removing temporary font file %s", filename);
275 if(!strncmp(&filename[l-4],".afm",4)) {
276 memcpy(&filename[l-4],".pfb",4); unlink(filename);
277 memcpy(&filename[l-4],".pfa",4); unlink(filename);
278 memcpy(&filename[l-4],".afm",4);
281 if(!strncmp(&filename[l-4],".pfa",4)) {
282 memcpy(&filename[l-4],".afm",4); unlink(filename);
283 memcpy(&filename[l-4],".pfa",4);
286 if(!strncmp(&filename[l-4],".pfb",4)) {
287 memcpy(&filename[l-4],".afm",4); unlink(filename);
288 memcpy(&filename[l-4],".pfb",4);
293 static int config_use_fontconfig = 1;
294 static int fcinitcalled = 0;
296 GFXGlobalParams::GFXGlobalParams()
299 //setupBaseFonts(char *dir); //not tested yet
301 GFXGlobalParams::~GFXGlobalParams()
303 msg("<verbose> Performing cleanups");
305 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
306 if(pdf2t1map[t].fullfilename) {
307 unlinkfont(pdf2t1map[t].fullfilename);
310 #ifdef HAVE_FONTCONFIG
311 if(config_use_fontconfig && fcinitcalled)
315 #ifdef HAVE_FONTCONFIG
316 static char fc_ismatch(FcPattern*match, char*family, char*style)
318 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
319 FcBool scalable=FcFalse, outline=FcFalse;
320 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
321 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
322 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
323 FcPatternGetBool(match, "outline", 0, &outline);
324 FcPatternGetBool(match, "scalable", 0, &scalable);
326 if(scalable!=FcTrue || outline!=FcTrue)
329 if (!strcasecmp(fcfamily, family)) {
330 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
333 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
339 char* fontconfig_searchForFont(char*name)
341 #ifdef HAVE_FONTCONFIG
342 if(!config_use_fontconfig)
345 // call init ony once
349 // check whether we have a config file
350 char* configfile = (char*)FcConfigFilename(0);
351 int configexists = 0;
352 FILE*fi = fopen(configfile, "rb");
354 configexists = 1;fclose(fi);
355 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
357 msg("<debug> Initializing FontConfig (no configfile)");
361 /* A fontconfig instance which didn't find a configfile is unbelievably
362 cranky, so let's just write out a small xml file and make fontconfig
364 FcConfig*c = FcConfigCreate();
366 char* tmpFileName = mktmpname(namebuf);
367 FILE*fi = fopen(tmpFileName, "wb");
368 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
370 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
372 fprintf(fi, "<dir>~/.fonts</dir>\n");
374 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
376 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
377 fprintf(fi, "</fontconfig>\n");
379 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
380 FcConfigBuildFonts(c);
381 FcConfigSetCurrent(c);
385 msg("<debug> FontConfig Initialization failed. Disabling.");
386 config_use_fontconfig = 0;
389 FcConfig * config = FcConfigGetCurrent();
391 msg("<debug> FontConfig Config Initialization failed. Disabling.");
392 config_use_fontconfig = 0;
395 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
396 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
397 if(!set || !set->nfont) {
398 msg("<debug> FontConfig has zero fonts. Disabling.");
399 config_use_fontconfig = 0;
403 if(getLogLevel() >= LOGLEVEL_TRACE) {
405 for(t=0;t<set->nfont;t++) {
406 char*fcfamily=0,*fcstyle=0,*filename=0;
407 FcBool scalable=FcFalse, outline=FcFalse;
408 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
409 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
410 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
411 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
412 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
413 if(scalable && outline) {
414 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
420 char*family = strdup(name);
422 char*dash = strchr(family, '-');
423 FcPattern*pattern = 0;
427 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
428 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
430 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
431 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
435 FcConfigSubstitute(0, pattern, FcMatchPattern);
436 FcDefaultSubstitute(pattern);
438 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
441 for(t=0;t<set->nfont;t++) {
442 FcPattern*match = set->fonts[t];
443 //FcPattern*match = FcFontMatch(0, pattern, &result);
444 if(fc_ismatch(match, family, style)) {
446 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
447 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
450 //FcPatternDestroy(match);
451 msg("<debug> fontconfig: returning filename %s", filename);
453 FcPatternDestroy(pattern);
454 FcFontSetDestroy(set);
455 return filename?strdup(filename):0;
460 FcPatternDestroy(pattern);
461 FcFontSetDestroy(set);
468 static DisplayFontParamKind detectFontType(const char*filename)
470 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
471 return displayFontTT;
472 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
473 return displayFontT1;
474 return displayFontTT;
477 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
479 msg("<verbose> looking for font %s in global params", fontName->getCString());
481 char*name = fontName->getCString();
483 /* see if it is a pdf standard font */
485 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
486 if(!strcmp(name, pdf2t1map[t].pdffont)) {
487 if(!pdf2t1map[t].fullfilename) {
488 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
489 if(!pdf2t1map[t].fullfilename) {
490 msg("<error> Couldn't save default font- is the Temp Directory writable?");
492 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
495 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
496 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
501 int bestlen = 0x7fffffff;
502 const char*bestfilename = 0;
504 fontfile_t*f = global_fonts;
506 if(strstr(f->filename, name)) {
507 if(f->len < bestlen) {
509 bestfilename = f->filename;
515 /* if we didn't find anything up to now, try looking for the
516 font via fontconfig */
519 filename = fontconfig_searchForFont(name);
521 filename = strdup(bestfilename);
525 DisplayFontParamKind kind = detectFontType(filename);
526 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
527 if(kind == displayFontTT) {
528 dfp->tt.fileName = new GString(filename);
530 dfp->t1.fileName = new GString(filename);
535 return GlobalParams::getDisplayFont(fontName);
538 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
542 this->xref = doc->getXRef();
545 this->textmodeinfo = 0;
548 this->type3active = 0;
551 this->substitutepos = 0;
552 this->type3Warning = 0;
553 this->user_movex = 0;
554 this->user_movey = 0;
557 this->user_clipx1 = 0;
558 this->user_clipy1 = 0;
559 this->user_clipx2 = 0;
560 this->user_clipy2 = 0;
561 this->current_text_stroke = 0;
562 this->current_text_clip = 0;
563 this->outer_clip_box = 0;
565 this->pagebuflen = 0;
567 this->config_convertgradients=1;
568 this->config_break_on_warning=0;
569 this->config_remapunicode=0;
570 this->config_transparent=0;
571 this->config_extrafontdata = 0;
572 this->config_fontquality = 10;
573 this->config_optimize_polygons = 0;
575 this->gfxfontlist = gfxfontlist_create();
577 memset(states, 0, sizeof(states));
578 this->featurewarnings = 0;
581 void GFXOutputDev::setParameter(const char*key, const char*value)
583 if(!strcmp(key,"breakonwarning")) {
584 this->config_break_on_warning = atoi(value);
585 } else if(!strcmp(key,"remapunicode")) {
586 this->config_remapunicode = atoi(value);
587 } else if(!strcmp(key,"transparent")) {
588 this->config_transparent = atoi(value);
589 } else if(!strcmp(key,"extrafontdata")) {
590 this->config_extrafontdata = atoi(value);
591 } else if(!strcmp(key,"convertgradients")) {
592 this->config_convertgradients = atoi(value);
593 } else if(!strcmp(key,"optimize_polygons")) {
594 this->config_optimize_polygons = atoi(value);
595 } else if(!strcmp(key,"fontquality")) {
596 this->config_fontquality = atof(value);
597 if(this->config_fontquality<=1)
598 this->config_fontquality=1;
599 } else if(!strcmp(key,"help")) {
600 printf("\nPDF layer options:\n");
601 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
602 printf(" are not 100%% supported\n");
603 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
604 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
605 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
610 void GFXOutputDev::setDevice(gfxdevice_t*dev)
615 void GFXOutputDev::setMove(int x,int y)
617 this->user_movex = x;
618 this->user_movey = y;
621 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
623 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
624 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
626 this->user_clipx1 = x1;
627 this->user_clipy1 = y1;
628 this->user_clipx2 = x2;
629 this->user_clipy2 = y2;
632 static char*getFontName(GfxFont*font)
635 GString*gstr = font->getName();
636 char* fname = gstr==0?0:gstr->getCString();
640 sprintf(buf, "UFONT%d", r->num);
641 fontid = strdup(buf);
643 fontid = strdup(fname);
647 char* plus = strchr(fontid, '+');
648 if(plus && plus < &fontid[strlen(fontid)-1]) {
649 fontname = strdup(plus+1);
651 fontname = strdup(fontid);
657 static void dumpFontInfo(const char*loglevel, GfxFont*font);
658 static int lastdumps[1024];
659 static int lastdumppos = 0;
664 static void showFontError(GfxFont*font, int nr)
668 for(t=0;t<lastdumppos;t++)
669 if(lastdumps[t] == r->num)
673 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
674 lastdumps[lastdumppos++] = r->num;
676 msg("<warning> The following font caused problems:");
678 msg("<warning> The following font caused problems (substituting):");
680 msg("<warning> The following Type 3 Font will be rendered as graphics:");
681 dumpFontInfo("<warning>", font);
684 static void dumpFontInfo(const char*loglevel, GfxFont*font)
686 char* id = getFontID(font);
687 char* name = getFontName(font);
688 Ref* r=font->getID();
689 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
691 GString*gstr = font->getTag();
693 msg("%s| Tag: %s", loglevel, id);
695 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
697 GfxFontType type=font->getType();
699 case fontUnknownType:
700 msg("%s| Type: unknown",loglevel);
703 msg("%s| Type: 1",loglevel);
706 msg("%s| Type: 1C",loglevel);
709 msg("%s| Type: 3",loglevel);
712 msg("%s| Type: TrueType",loglevel);
715 msg("%s| Type: CIDType0",loglevel);
718 msg("%s| Type: CIDType0C",loglevel);
721 msg("%s| Type: CIDType2",loglevel);
726 GBool embedded = font->getEmbeddedFontID(&embRef);
728 if(font->getEmbeddedFontName()) {
729 embeddedName = font->getEmbeddedFontName()->getCString();
732 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
734 gstr = font->getExtFontFile();
736 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
738 // Get font descriptor flags.
739 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
740 if(font->isSerif()) msg("%s| is serif", loglevel);
741 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
742 if(font->isItalic()) msg("%s| is italic", loglevel);
743 if(font->isBold()) msg("%s| is bold", loglevel);
749 //void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
750 //void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
752 void dump_outline(gfxline_t*line)
755 if(line->type == gfx_moveTo) {
756 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
757 } else if(line->type == gfx_lineTo) {
758 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
759 } else if(line->type == gfx_splineTo) {
760 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
766 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
768 int num = path->getNumSubpaths();
771 double lastx=0,lasty=0,posx=0,posy=0;
774 msg("<warning> empty path");
778 gfxdrawer_target_gfxline(&draw);
780 for(t = 0; t < num; t++) {
781 GfxSubpath *subpath = path->getSubpath(t);
782 int subnum = subpath->getNumPoints();
783 double bx=0,by=0,cx=0,cy=0;
785 for(s=0;s<subnum;s++) {
788 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
791 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
792 draw.lineTo(&draw, lastx, lasty);
794 draw.moveTo(&draw, x,y);
799 } else if(subpath->getCurve(s) && cpos==0) {
803 } else if(subpath->getCurve(s) && cpos==1) {
811 draw.lineTo(&draw, x,y);
813 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
820 /* fix non-closed lines */
821 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
822 draw.lineTo(&draw, lastx, lasty);
824 gfxline_t*result = (gfxline_t*)draw.result(&draw);
826 gfxline_optimize(result);
831 GBool GFXOutputDev::useTilingPatternFill()
833 infofeature("tiled patterns");
834 // if(config_convertgradients)
838 GBool GFXOutputDev::useShadedFills()
840 infofeature("shaded fills");
841 if(config_convertgradients)
846 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
848 state->transform(x,y,nx,ny);
849 *nx += user_movex + clipmovex;
850 *ny += user_movey + clipmovey;
854 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
855 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
856 int paintType, Dict *resDict,
857 double *mat, double *bbox,
858 int x0, int y0, int x1, int y1,
859 double xStep, double yStep)
861 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
862 int paintType, Dict *resDict,
863 double *mat, double *bbox,
864 int x0, int y0, int x1, int y1,
865 double xStep, double yStep)
868 msg("<debug> tilingPatternFill");
869 infofeature("tiling pattern fills");
872 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
874 msg("<error> functionShadedFill not supported yet");
875 infofeature("function shaded fills");
878 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
882 colspace->getRGB(col, &rgb);
883 c.r = colToByte(rgb.r);
884 c.g = colToByte(rgb.g);
885 c.b = colToByte(rgb.b);
890 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
892 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
893 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
896 this->transformXY(state, x0,y0, &x0,&y0);
897 this->transformXY(state, x1,y1, &x1,&y1);
898 this->transformXY(state, x2,y2, &x2,&y2);
903 shading->getColor(0.0, &color0);
904 shading->getColor(0.5, &color1);
905 shading->getColor(1.0, &color2);
907 GfxColorSpace* colspace = shading->getColorSpace();
909 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
910 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
911 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
912 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
913 infofeature("radial shaded fills");
915 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
919 g[0].color = col2col(colspace, &color0);
920 g[1].color = col2col(colspace, &color1);
921 g[2].color = col2col(colspace, &color2);
926 gfxbbox_t b = states[statepos].clipbbox;
927 gfxline_t p1,p2,p3,p4,p5;
928 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
929 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
930 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
931 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
932 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
935 //m.m00 = (x3-x0); m.m10 = (x1-x0);
936 //m.m01 = (y3-y0); m.m11 = (y1-y0);
937 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
938 m.m00 = (x1-x0); m.m10 = (x2-x0);
939 m.m01 = (y1-y0); m.m11 = (y2-y0);
943 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
947 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
950 shading->getCoords(&x0,&y0,&x1,&y1);
951 this->transformXY(state, x0,y0,&x0,&y0);
952 this->transformXY(state, x1,y1,&x1,&y1);
957 shading->getColor(0.0, &color0);
958 shading->getColor(0.5, &color1);
959 shading->getColor(1.0, &color2);
961 GfxColorSpace* colspace = shading->getColorSpace();
963 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
964 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
965 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
966 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
968 infofeature("axial shaded fills");
970 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
974 g[0].color = col2col(colspace, &color0);
975 g[1].color = col2col(colspace, &color1);
976 g[2].color = col2col(colspace, &color2);
981 double xMin,yMin,xMax,yMax;
982 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
983 this->transformXY(state, xMin, yMin, &xMin, &yMin);
984 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
987 xMin = 1024; yMin = 1024;
989 gfxbbox_t b = states[statepos].clipbbox;
990 gfxline_t p1,p2,p3,p4,p5;
991 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
992 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
993 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
994 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
995 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
997 /* the gradient starts at (-1.0,0.0), so move (0,0) to
998 the middle of the two control points */
1000 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1001 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1002 m.tx = (x0 + x1)/2 - 0.5;
1003 m.ty = (y0 + y1)/2 - 0.5;
1005 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1009 GBool GFXOutputDev::useDrawForm()
1011 infofeature("forms");
1014 void GFXOutputDev::drawForm(Ref id)
1016 msg("<error> drawForm not implemented");
1018 GBool GFXOutputDev::needNonText()
1022 void GFXOutputDev::endPage()
1024 msg("<verbose> endPage (GfxOutputDev)");
1025 if(outer_clip_box) {
1026 device->endclip(device);
1029 this->dashPattern = 0;
1030 /* notice: we're not fully done yet with this page- there might still be
1031 a few calls to drawLink() yet to come */
1034 static inline double sqr(double x) {return x*x;}
1036 #define STROKE_FILL 1
1037 #define STROKE_CLIP 2
1038 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1040 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1041 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1042 double miterLimit = state->getMiterLimit();
1043 double width = state->getTransformedLineWidth();
1046 double opaq = state->getStrokeOpacity();
1048 state->getFillRGB(&rgb);
1050 state->getStrokeRGB(&rgb);
1052 col.r = colToByte(rgb.r);
1053 col.g = colToByte(rgb.g);
1054 col.b = colToByte(rgb.b);
1055 col.a = (unsigned char)(opaq*255);
1057 gfx_capType capType = gfx_capRound;
1058 if(lineCap == 0) capType = gfx_capButt;
1059 else if(lineCap == 1) capType = gfx_capRound;
1060 else if(lineCap == 2) capType = gfx_capSquare;
1062 gfx_joinType joinType = gfx_joinRound;
1063 if(lineJoin == 0) joinType = gfx_joinMiter;
1064 else if(lineJoin == 1) joinType = gfx_joinRound;
1065 else if(lineJoin == 2) joinType = gfx_joinBevel;
1067 gfxline_t*line2 = 0;
1069 if(this->dashLength && this->dashPattern) {
1070 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1073 /* try to find out how much the transformation matrix would
1074 stretch the dashes, and factor that into the dash lengths.
1075 This is not the entirely correct approach- it would be
1076 better to first convert the path to an unscaled version,
1077 then apply dashing, and then transform the path using
1078 the current transformation matrix. However there are few
1079 PDFs which actually stretch a dashed path in a non-orthonormal
1081 double tx1, ty1, tx2, ty2;
1082 this->transformXY(state, 0, 0, &tx1, &ty1);
1083 this->transformXY(state, 1, 1, &tx2, &ty2);
1084 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1086 msg("<trace> %d dashes", this->dashLength);
1087 msg("<trace> | phase: %f", this->dashStart);
1088 for(t=0;t<this->dashLength;t++) {
1089 dash[t] = (float)this->dashPattern[t] * f;
1090 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1092 dash[this->dashLength] = -1;
1093 if(getLogLevel() >= LOGLEVEL_TRACE) {
1097 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1100 msg("<trace> After dashing:");
1103 if(getLogLevel() >= LOGLEVEL_TRACE) {
1104 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1106 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1107 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1109 col.r,col.g,col.b,col.a
1114 if(flags&STROKE_FILL) {
1115 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1116 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1117 if(getLogLevel() >= LOGLEVEL_TRACE) {
1118 dump_outline(gfxline);
1121 msg("<warning> Empty polygon (resulting from stroked line)");
1123 if(flags&STROKE_CLIP) {
1124 device->startclip(device, gfxline);
1125 states[statepos].clipping++;
1127 device->fill(device, gfxline, &col);
1129 gfxline_free(gfxline);
1132 if(flags&STROKE_CLIP)
1133 msg("<error> Stroke&clip not supported at the same time");
1134 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1138 gfxline_free(line2);
1141 gfxcolor_t getFillColor(GfxState * state)
1144 double opaq = state->getFillOpacity();
1145 state->getFillRGB(&rgb);
1147 col.r = colToByte(rgb.r);
1148 col.g = colToByte(rgb.g);
1149 col.b = colToByte(rgb.b);
1150 col.a = (unsigned char)(opaq*255);
1154 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1156 gfxcolor_t col = getFillColor(state);
1158 if(getLogLevel() >= LOGLEVEL_TRACE) {
1159 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1162 device->fill(device, line, &col);
1165 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1167 if(getLogLevel() >= LOGLEVEL_TRACE) {
1170 gfxbbox_t bbox = gfxline_getbbox(line);
1171 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1173 device->startclip(device, line);
1174 states[statepos].clipping++;
1177 void GFXOutputDev::clip(GfxState *state)
1179 GfxPath * path = state->getPath();
1180 msg("<trace> clip");
1181 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1182 if(config_optimize_polygons) {
1183 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1187 clipToGfxLine(state, line);
1191 void GFXOutputDev::eoClip(GfxState *state)
1193 GfxPath * path = state->getPath();
1194 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1195 clipToGfxLine(state, line);
1198 void GFXOutputDev::clipToStrokePath(GfxState *state)
1200 GfxPath * path = state->getPath();
1201 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1203 if(getLogLevel() >= LOGLEVEL_TRACE) {
1204 double width = state->getTransformedLineWidth();
1205 msg("<trace> cliptostrokepath width=%f", width);
1209 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1213 void GFXOutputDev::finish()
1215 if(outer_clip_box) {
1217 device->endclip(device);
1223 GFXOutputDev::~GFXOutputDev()
1228 free(this->pages); this->pages = 0;
1231 feature_t*f = this->featurewarnings;
1233 feature_t*next = f->next;
1235 free(f->string);f->string =0;
1241 this->featurewarnings = 0;
1243 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1245 GBool GFXOutputDev::upsideDown()
1249 GBool GFXOutputDev::useDrawChar()
1254 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1255 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1257 static char tmp_printstr[4096];
1258 char* makeStringPrintable(char*str)
1260 int len = strlen(str);
1267 for(t=0;t<len;t++) {
1272 tmp_printstr[t] = c;
1275 tmp_printstr[len++] = '.';
1276 tmp_printstr[len++] = '.';
1277 tmp_printstr[len++] = '.';
1279 tmp_printstr[len] = 0;
1280 return tmp_printstr;
1282 #define INTERNAL_FONT_SIZE 1024.0
1283 void GFXOutputDev::updateFontMatrix(GfxState*state)
1285 double* ctm = state->getCTM();
1286 double fontSize = state->getFontSize();
1287 double*textMat = state->getTextMat();
1289 /* taking the absolute value of horizScaling seems to be required for
1290 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1291 double hscale = fabs(state->getHorizScaling());
1293 // from xpdf-3.02/SplashOutputDev:updateFont
1294 double mm11 = textMat[0] * fontSize * hscale;
1295 double mm12 = textMat[1] * fontSize * hscale;
1296 double mm21 = textMat[2] * fontSize;
1297 double mm22 = textMat[3] * fontSize;
1299 // multiply with ctm, like state->getFontTransMat() does
1300 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1301 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1302 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1303 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1304 this->current_font_matrix.tx = 0;
1305 this->current_font_matrix.ty = 0;
1308 void GFXOutputDev::beginString(GfxState *state, GString *s)
1310 int render = state->getRender();
1311 if(current_text_stroke) {
1312 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1315 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1318 static gfxline_t* mkEmptyGfxShape(double x, double y)
1320 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1321 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1325 static char isValidUnicode(int c)
1327 if(c>=32 && c<0x2fffe)
1332 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1333 double dx, double dy,
1334 double originX, double originY,
1335 CharCode charid, int nBytes, Unicode *_u, int uLen)
1337 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1338 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1342 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1344 int render = state->getRender();
1345 gfxcolor_t col = getFillColor(state);
1347 // check for invisible text -- this is used by Acrobat Capture
1348 if (render == RENDER_INVISIBLE) {
1350 if(!config_extrafontdata)
1354 GfxFont*font = state->getFont();
1356 if(font->getType() == fontType3) {
1357 /* type 3 chars are passed as graphics */
1358 msg("<debug> type3 char at %f/%f", x, y);
1362 Unicode u = uLen?(_u[0]):0;
1363 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);
1365 gfxmatrix_t m = this->current_font_matrix;
1366 this->transformXY(state, x, y, &m.tx, &m.ty);
1368 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1369 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1371 msg("<debug> Drawing glyph %d as shape", charid);
1373 msg("<notice> Some texts will be rendered as shape");
1376 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1377 gfxline_t*tglyph = gfxline_clone(glyph);
1378 gfxline_transform(tglyph, &m);
1379 if((render&3) != RENDER_INVISIBLE) {
1380 gfxline_t*add = gfxline_clone(tglyph);
1381 current_text_stroke = gfxline_append(current_text_stroke, add);
1383 if(render&RENDER_CLIP) {
1384 gfxline_t*add = gfxline_clone(tglyph);
1385 current_text_clip = gfxline_append(current_text_clip, add);
1386 if(!current_text_clip) {
1387 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1390 gfxline_free(tglyph);
1394 void GFXOutputDev::endString(GfxState *state)
1396 int render = state->getRender();
1397 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1399 if(current_text_stroke) {
1400 /* fillstroke and stroke text rendering objects we can process right
1401 now (as there may be texts of other rendering modes in this
1402 text object)- clipping objects have to wait until endTextObject,
1404 device->setparameter(device, "mark","TXT");
1405 if((render&3) == RENDER_FILL) {
1406 fillGfxLine(state, current_text_stroke);
1407 gfxline_free(current_text_stroke);
1408 current_text_stroke = 0;
1409 } else if((render&3) == RENDER_FILLSTROKE) {
1410 fillGfxLine(state, current_text_stroke);
1411 strokeGfxline(state, current_text_stroke,0);
1412 gfxline_free(current_text_stroke);
1413 current_text_stroke = 0;
1414 } else if((render&3) == RENDER_STROKE) {
1415 strokeGfxline(state, current_text_stroke,0);
1416 gfxline_free(current_text_stroke);
1417 current_text_stroke = 0;
1419 device->setparameter(device, "mark","");
1423 void GFXOutputDev::endTextObject(GfxState *state)
1425 int render = state->getRender();
1426 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1428 if(current_text_clip) {
1429 device->setparameter(device, "mark","TXT");
1430 clipToGfxLine(state, current_text_clip);
1431 device->setparameter(device, "mark","");
1432 gfxline_free(current_text_clip);
1433 current_text_clip = 0;
1437 /* the logic seems to be as following:
1438 first, beginType3Char is called, with the charcode and the coordinates.
1439 if this function returns true, it already knew about the char and has now drawn it.
1440 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1441 called with some parameters.
1442 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1443 at the position first passed to beginType3Char). the char ends with endType3Char.
1445 The drawing operations between beginType3Char and endType3Char are somewhat different to
1446 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1447 color determines the color of a font)
1450 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1452 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1455 if(config_extrafontdata && current_fontinfo) {
1457 gfxmatrix_t m = this->current_font_matrix;
1458 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1459 m.m00*=INTERNAL_FONT_SIZE;
1460 m.m01*=INTERNAL_FONT_SIZE;
1461 m.m10*=INTERNAL_FONT_SIZE;
1462 m.m11*=INTERNAL_FONT_SIZE;
1464 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1465 msg("<error> Invalid charid %d for font", charid);
1468 gfxcolor_t col={0,0,0,0};
1469 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1470 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1474 /* the character itself is going to be passed using the draw functions */
1475 return gFalse; /* gTrue= is_in_cache? */
1478 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1480 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1483 void GFXOutputDev::endType3Char(GfxState *state)
1486 msg("<debug> endType3Char");
1489 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1491 this->currentpage = pageNum;
1493 int rot = doc->getPageRotate(1);
1494 gfxcolor_t white = {255,255,255,255};
1495 gfxcolor_t black = {255,0,0,0};
1497 gfxline_t clippath[5];
1499 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1500 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1501 Use CropBox, not MediaBox, as page size
1508 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1509 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1511 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1512 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1514 this->clipmovex = -(int)x1;
1515 this->clipmovey = -(int)y1;
1517 /* apply user clip box */
1518 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1519 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1520 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1521 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1522 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1523 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1526 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1528 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);
1530 msg("<verbose> page is rotated %d degrees", rot);
1532 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1533 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1534 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1535 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1536 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1537 device->startclip(device, clippath); outer_clip_box = 1;
1538 if(!config_transparent) {
1539 device->fill(device, clippath, &white);
1541 states[statepos].clipbbox.xmin = x1;
1542 states[statepos].clipbbox.ymin = x1;
1543 states[statepos].clipbbox.xmax = x2;
1544 states[statepos].clipbbox.ymax = y2;
1546 this->dashPattern = 0;
1547 this->dashLength = 0;
1548 this->dashStart = 0;
1552 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1554 double x1, y1, x2, y2;
1555 gfxline_t points[5];
1558 msg("<debug> drawlink");
1560 link->getRect(&x1, &y1, &x2, &y2);
1561 cvtUserToDev(x1, y1, &x, &y);
1562 points[0].type = gfx_moveTo;
1563 points[0].x = points[4].x = x + user_movex + clipmovex;
1564 points[0].y = points[4].y = y + user_movey + clipmovey;
1565 points[0].next = &points[1];
1566 cvtUserToDev(x2, y1, &x, &y);
1567 points[1].type = gfx_lineTo;
1568 points[1].x = x + user_movex + clipmovex;
1569 points[1].y = y + user_movey + clipmovey;
1570 points[1].next = &points[2];
1571 cvtUserToDev(x2, y2, &x, &y);
1572 points[2].type = gfx_lineTo;
1573 points[2].x = x + user_movex + clipmovex;
1574 points[2].y = y + user_movey + clipmovey;
1575 points[2].next = &points[3];
1576 cvtUserToDev(x1, y2, &x, &y);
1577 points[3].type = gfx_lineTo;
1578 points[3].x = x + user_movex + clipmovex;
1579 points[3].y = y + user_movey + clipmovey;
1580 points[3].next = &points[4];
1581 cvtUserToDev(x1, y1, &x, &y);
1582 points[4].type = gfx_lineTo;
1583 points[4].x = x + user_movex + clipmovex;
1584 points[4].y = y + user_movey + clipmovey;
1587 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1588 points[0].x, points[0].y,
1589 points[1].x, points[1].y,
1590 points[2].x, points[2].y,
1591 points[3].x, points[3].y);
1593 if(getLogLevel() >= LOGLEVEL_TRACE) {
1594 dump_outline(points);
1597 LinkAction*action=link->getAction();
1600 const char*type = "-?-";
1603 msg("<trace> drawlink action=%d", action->getKind());
1604 switch(action->getKind())
1608 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1609 LinkDest *dest=NULL;
1610 if (ha->getDest()==NULL)
1611 dest=catalog->findDest(ha->getNamedDest());
1612 else dest=ha->getDest();
1614 if (dest->isPageRef()){
1615 Ref pageref=dest->getPageRef();
1616 page=catalog->findPage(pageref.num,pageref.gen);
1618 else page=dest->getPageNum();
1619 sprintf(buf, "%d", page);
1626 LinkGoToR*l = (LinkGoToR*)action;
1627 GString*g = l->getFileName();
1629 s = strdup(g->getCString());
1631 /* if the GoToR link has no filename, then
1632 try to find a refernce in the *local*
1634 GString*g = l->getNamedDest();
1636 s = strdup(g->getCString());
1642 LinkNamed*l = (LinkNamed*)action;
1643 GString*name = l->getName();
1645 s = strdup(name->lowerCase()->getCString());
1646 named = name->getCString();
1649 if(strstr(s, "next") || strstr(s, "forward"))
1651 page = currentpage + 1;
1653 else if(strstr(s, "prev") || strstr(s, "back"))
1655 page = currentpage - 1;
1657 else if(strstr(s, "last") || strstr(s, "end"))
1659 if(pages && pagepos>0)
1660 page = pages[pagepos-1];
1662 else if(strstr(s, "first") || strstr(s, "top"))
1670 case actionLaunch: {
1672 LinkLaunch*l = (LinkLaunch*)action;
1673 GString * str = new GString(l->getFileName());
1674 GString * params = l->getParams();
1676 str->append(params);
1677 s = strdup(str->getCString());
1684 LinkURI*l = (LinkURI*)action;
1685 GString*g = l->getURI();
1687 url = g->getCString();
1692 case actionUnknown: {
1694 LinkUnknown*l = (LinkUnknown*)action;
1699 msg("<error> Unknown link type!");
1704 if(!s) s = strdup("-?-");
1706 msg("<trace> drawlink s=%s", s);
1708 if(!linkinfo && (page || s))
1710 msg("<notice> File contains links");
1718 for(t=1;t<=pagepos;t++) {
1719 if(pages[t]==page) {
1728 sprintf(buf, "page%d", lpage);
1729 device->drawlink(device, points, buf);
1733 device->drawlink(device, points, s);
1736 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1740 void GFXOutputDev::saveState(GfxState *state) {
1741 dbg("saveState"); dbgindent+=2;
1743 msg("<trace> saveState");
1746 msg("<error> Too many nested states in pdf.");
1750 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1751 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1752 states[statepos].clipping = 0;
1753 states[statepos].clipbbox = states[statepos-1].clipbbox;
1756 void GFXOutputDev::restoreState(GfxState *state) {
1757 dbgindent-=2; dbg("restoreState");
1760 msg("<error> Invalid restoreState");
1763 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1764 states[statepos].clipping?" (end clipping)":"");
1765 if(states[statepos].softmask) {
1766 clearSoftMask(state);
1769 while(states[statepos].clipping) {
1770 device->endclip(device);
1771 states[statepos].clipping--;
1776 void GFXOutputDev::updateLineDash(GfxState *state)
1778 state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1779 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1780 if(!this->dashLength) {
1781 this->dashPattern = 0;
1785 void GFXOutputDev::updateLineWidth(GfxState *state)
1787 double width = state->getTransformedLineWidth();
1790 void GFXOutputDev::updateLineCap(GfxState *state)
1792 int c = state->getLineCap();
1795 void GFXOutputDev::updateLineJoin(GfxState *state)
1797 int j = state->getLineJoin();
1800 void GFXOutputDev::updateFillColor(GfxState *state)
1803 double opaq = state->getFillOpacity();
1804 state->getFillRGB(&rgb);
1806 void GFXOutputDev::updateFillOpacity(GfxState *state)
1809 double opaq = state->getFillOpacity();
1810 state->getFillRGB(&rgb);
1811 dbg("update fillopaq %f", opaq);
1813 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1815 double opaq = state->getFillOpacity();
1816 dbg("update strokeopaq %f", opaq);
1818 void GFXOutputDev::updateFillOverprint(GfxState *state)
1820 double opaq = state->getFillOverprint();
1821 dbg("update filloverprint %f", opaq);
1823 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1825 double opaq = state->getStrokeOverprint();
1826 dbg("update strokeoverprint %f", opaq);
1828 void GFXOutputDev::updateTransfer(GfxState *state)
1830 dbg("update transfer");
1834 void GFXOutputDev::updateStrokeColor(GfxState *state)
1837 double opaq = state->getStrokeOpacity();
1838 state->getStrokeRGB(&rgb);
1842 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1844 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1845 memset(font, 0, sizeof(gfxfont_t));
1847 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1848 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1852 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1854 //printf("%d glyphs\n", font->num_glyphs);
1855 font->num_glyphs = 0;
1856 for(t=0;t<src->num_glyphs;t++) {
1857 if(src->glyphs[t]) {
1858 SplashPath*path = src->glyphs[t]->path;
1859 int len = path?path->getLength():0;
1860 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1861 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1862 src->glyphs[t]->glyphid = font->num_glyphs;
1863 glyph->unicode = src->glyphs[t]->unicode;
1864 if(glyph->unicode >= font->max_unicode)
1865 font->max_unicode = glyph->unicode+1;
1867 gfxdrawer_target_gfxline(&drawer);
1871 for(s=0;s<len;s++) {
1874 path->getPoint(s, &x, &y, &f);
1877 if(f&splashPathFirst) {
1878 drawer.moveTo(&drawer, x*scale, y*scale);
1880 if(f&splashPathCurve) {
1882 path->getPoint(++s, &x2, &y2, &f);
1883 if(f&splashPathCurve) {
1885 path->getPoint(++s, &x3, &y3, &f);
1886 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1888 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1891 drawer.lineTo(&drawer, x*scale, y*scale);
1893 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1894 // (f&splashPathFirst)?"first":"",
1895 // (f&splashPathLast)?"last":"");
1897 glyph->line = (gfxline_t*)drawer.result(&drawer);
1898 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1902 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1903 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1904 for(t=0;t<font->num_glyphs;t++) {
1905 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1906 font->unicode2glyph[font->glyphs[t].unicode] = t;
1910 msg("<trace> %d glyphs.", t, font->num_glyphs);
1914 void GFXOutputDev::updateFont(GfxState *state)
1916 GfxFont* gfxFont = state->getFont();
1920 char*id = getFontID(gfxFont);
1921 msg("<verbose> Updating font to %s", id);
1922 if(gfxFont->getType() == fontType3) {
1923 infofeature("Type3 fonts");
1924 if(!config_extrafontdata) {
1929 msg("<error> Internal Error: FontID is null");
1933 this->current_fontinfo = this->info->getFont(id);
1934 if(!this->current_fontinfo) {
1935 msg("<error> Internal Error: no fontinfo for font %s", id);
1938 if(!this->current_fontinfo->seen) {
1939 dumpFontInfo("<verbose>", gfxFont);
1942 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1944 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1945 font->id = strdup(id);
1946 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1948 device->addfont(device, font);
1950 current_gfxfont = font;
1953 updateFontMatrix(state);
1956 #define SQR(x) ((x)*(x))
1958 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1960 if((newwidth<2 || newheight<2) ||
1961 (width<=newwidth || height<=newheight))
1963 unsigned char*newdata;
1965 newdata= (unsigned char*)malloc(newwidth*newheight);
1966 double fx = ((double)width)/newwidth;
1967 double fy = ((double)height)/newheight;
1969 int blocksize = (int)(8192/(fx*fy));
1970 int r = 8192*256/palettesize;
1971 for(x=0;x<newwidth;x++) {
1972 double ex = px + fx;
1973 int fromx = (int)px;
1975 int xweight1 = (int)((1-(px-fromx))*256);
1976 int xweight2 = (int)((ex-tox)*256);
1978 for(y=0;y<newheight;y++) {
1979 double ey = py + fy;
1980 int fromy = (int)py;
1982 int yweight1 = (int)((1-(py-fromy))*256);
1983 int yweight2 = (int)((ey-toy)*256);
1990 for(xx=fromx;xx<=tox;xx++)
1991 for(yy=fromy;yy<=toy;yy++) {
1992 int b = 1-data[width*yy+xx];
1994 if(xx==fromx) weight = (weight*xweight1)/256;
1995 if(xx==tox) weight = (weight*xweight2)/256;
1996 if(yy==fromy) weight = (weight*yweight1)/256;
1997 if(yy==toy) weight = (weight*yweight2)/256;
2000 //if(a) a=(palettesize-1)*r/blocksize;
2001 newdata[y*newwidth+x] = (a*blocksize)/r;
2009 #define IMAGE_TYPE_JPEG 0
2010 #define IMAGE_TYPE_LOSSLESS 1
2012 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2013 double x1,double y1,
2014 double x2,double y2,
2015 double x3,double y3,
2016 double x4,double y4, int type)
2018 gfxcolor_t*newpic=0;
2020 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2021 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2023 gfxline_t p1,p2,p3,p4,p5;
2024 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2025 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2026 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2027 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2028 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2030 {p1.x = (int)(p1.x*20)/20.0;
2031 p1.y = (int)(p1.y*20)/20.0;
2032 p2.x = (int)(p2.x*20)/20.0;
2033 p2.y = (int)(p2.y*20)/20.0;
2034 p3.x = (int)(p3.x*20)/20.0;
2035 p3.y = (int)(p3.y*20)/20.0;
2036 p4.x = (int)(p4.x*20)/20.0;
2037 p4.y = (int)(p4.y*20)/20.0;
2038 p5.x = (int)(p5.x*20)/20.0;
2039 p5.y = (int)(p5.y*20)/20.0;
2043 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2044 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2049 img.data = (gfxcolor_t*)data;
2053 if(type == IMAGE_TYPE_JPEG)
2054 /* TODO: pass image_dpi to device instead */
2055 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2058 dev->fillbitmap(dev, &p1, &img, &m, 0);
2061 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2062 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2064 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2067 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2068 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2070 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2074 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2075 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2076 GBool inlineImg, int mask, int*maskColors,
2077 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2079 double x1,y1,x2,y2,x3,y3,x4,y4;
2080 ImageStream *imgStr;
2085 unsigned char* maskbitmap = 0;
2088 ncomps = colorMap->getNumPixelComps();
2089 bits = colorMap->getBits();
2094 unsigned char buf[8];
2095 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2097 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2098 imgMaskStr->reset();
2099 unsigned char pal[256];
2100 int n = 1 << colorMap->getBits();
2105 maskColorMap->getGray(pixBuf, &gray);
2106 pal[t] = colToByte(gray);
2108 for (y = 0; y < maskHeight; y++) {
2109 for (x = 0; x < maskWidth; x++) {
2110 imgMaskStr->getPixel(buf);
2111 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2116 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2117 imgMaskStr->reset();
2118 for (y = 0; y < maskHeight; y++) {
2119 for (x = 0; x < maskWidth; x++) {
2120 imgMaskStr->getPixel(buf);
2122 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2130 imgStr = new ImageStream(str, width, ncomps,bits);
2133 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2135 msg("<verbose> Ignoring %d by %d image", width, height);
2136 unsigned char buf[8];
2138 for (y = 0; y < height; ++y)
2139 for (x = 0; x < width; ++x) {
2140 imgStr->getPixel(buf);
2148 this->transformXY(state, 0, 1, &x1, &y1);
2149 this->transformXY(state, 0, 0, &x2, &y2);
2150 this->transformXY(state, 1, 0, &x3, &y3);
2151 this->transformXY(state, 1, 1, &x4, &y4);
2154 /* as type 3 bitmaps are antialized, we need to place them
2155 at integer coordinates, otherwise flash player's antializing
2156 will kick in and make everything blurry */
2157 x1 = (int)(x1);y1 = (int)(y1);
2158 x2 = (int)(x2);y2 = (int)(y2);
2159 x3 = (int)(x3);y3 = (int)(y3);
2160 x4 = (int)(x4);y4 = (int)(y4);
2163 if(!pbminfo && !(str->getKind()==strDCT)) {
2165 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2169 msg("<verbose> drawing %d by %d masked picture", width, height);
2171 if(!jpeginfo && (str->getKind()==strDCT)) {
2172 msg("<notice> File contains jpeg pictures");
2177 unsigned char buf[8];
2179 unsigned char*pic = new unsigned char[width*height];
2180 gfxcolor_t pal[256];
2182 state->getFillRGB(&rgb);
2184 memset(pal,255,sizeof(pal));
2185 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2186 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2187 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2188 pal[0].a = 255; pal[1].a = 0;
2191 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2192 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2193 for (y = 0; y < height; ++y)
2194 for (x = 0; x < width; ++x)
2196 imgStr->getPixel(buf);
2199 pic[width*y+x] = buf[0];
2202 /* the size of the drawn image is added to the identifier
2203 as the same image may require different bitmaps if displayed
2204 at different sizes (due to antialiasing): */
2207 unsigned char*pic2 = 0;
2210 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2219 height = realheight;
2223 /* make a black/white palette */
2225 float r = 255./(float)(numpalette-1);
2227 for(t=0;t<numpalette;t++) {
2228 pal[t].r = colToByte(rgb.r);
2229 pal[t].g = colToByte(rgb.g);
2230 pal[t].b = colToByte(rgb.b);
2231 pal[t].a = (unsigned char)(t*r);
2236 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2237 for (y = 0; y < height; ++y) {
2238 for (x = 0; x < width; ++x) {
2239 pic2[width*y+x] = pal[pic[y*width+x]];
2242 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2246 if(maskbitmap) free(maskbitmap);
2252 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2253 gfxcolor_t*pic=new gfxcolor_t[width*height];
2254 for (y = 0; y < height; ++y) {
2255 for (x = 0; x < width; ++x) {
2256 imgStr->getPixel(pixBuf);
2257 colorMap->getRGB(pixBuf, &rgb);
2258 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2259 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2260 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2261 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2263 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2267 if(str->getKind()==strDCT)
2268 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2270 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2273 if(maskbitmap) free(maskbitmap);
2276 gfxcolor_t*pic=new gfxcolor_t[width*height];
2277 gfxcolor_t pal[256];
2278 int n = 1 << colorMap->getBits();
2280 for(t=0;t<256;t++) {
2282 colorMap->getRGB(pixBuf, &rgb);
2284 {/*if(maskColors && *maskColors==t) {
2285 msg("<notice> Color %d is transparent", t);
2286 if (imgData->maskColors) {
2288 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2289 if (pix[i] < imgData->maskColors[2*i] ||
2290 pix[i] > imgData->maskColors[2*i+1]) {
2305 pal[t].r = (unsigned char)(colToByte(rgb.r));
2306 pal[t].g = (unsigned char)(colToByte(rgb.g));
2307 pal[t].b = (unsigned char)(colToByte(rgb.b));
2308 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2311 for (y = 0; y < height; ++y) {
2312 for (x = 0; x < width; ++x) {
2313 imgStr->getPixel(pixBuf);
2314 pic[width*y+x] = pal[pixBuf[0]];
2318 if(maskWidth < width && maskHeight < height) {
2319 for(y = 0; y < height; y++) {
2320 for (x = 0; x < width; x++) {
2321 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2325 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2326 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2327 double dx = width / maskWidth;
2328 double dy = height / maskHeight;
2330 for(y = 0; y < maskHeight; y++) {
2332 for (x = 0; x < maskWidth; x++) {
2333 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2334 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2342 height = maskHeight;
2345 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2349 if(maskbitmap) free(maskbitmap);
2354 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2355 int width, int height, GBool invert,
2358 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2359 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2360 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2363 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2364 int width, int height, GfxImageColorMap *colorMap,
2365 int *maskColors, GBool inlineImg)
2367 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2368 colorMap?"colorMap":"no colorMap",
2369 maskColors?"maskColors":"no maskColors",
2371 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2372 colorMap?"colorMap":"no colorMap",
2373 maskColors?"maskColors":"no maskColors",
2376 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2377 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2378 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2381 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2382 int width, int height,
2383 GfxImageColorMap *colorMap,
2384 Stream *maskStr, int maskWidth, int maskHeight,
2387 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2388 colorMap?"colorMap":"no colorMap",
2389 maskWidth, maskHeight);
2390 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2391 colorMap?"colorMap":"no colorMap",
2392 maskWidth, maskHeight);
2394 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2395 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2396 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2399 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2400 int width, int height,
2401 GfxImageColorMap *colorMap,
2403 int maskWidth, int maskHeight,
2404 GfxImageColorMap *maskColorMap)
2406 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2407 colorMap?"colorMap":"no colorMap",
2408 maskWidth, maskHeight);
2409 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2410 colorMap?"colorMap":"no colorMap",
2411 maskWidth, maskHeight);
2413 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2414 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2415 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2418 void GFXOutputDev::stroke(GfxState *state)
2422 GfxPath * path = state->getPath();
2423 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2424 strokeGfxline(state, line, 0);
2428 void GFXOutputDev::fill(GfxState *state)
2430 gfxcolor_t col = getFillColor(state);
2431 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2433 GfxPath * path = state->getPath();
2434 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2435 if(config_optimize_polygons) {
2436 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2440 fillGfxLine(state, line);
2444 void GFXOutputDev::eoFill(GfxState *state)
2446 gfxcolor_t col = getFillColor(state);
2447 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2449 GfxPath * path = state->getPath();
2450 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2451 fillGfxLine(state, line);
2456 static const char* dirseparator()
2465 void addGlobalFont(const char*filename)
2467 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2468 memset(f, 0, sizeof(fontfile_t));
2469 f->filename = filename;
2470 int len = strlen(filename);
2471 char*r1 = strrchr(filename, '/');
2472 char*r2 = strrchr(filename, '\\');
2480 msg("<notice> Adding font \"%s\".", filename);
2481 if(global_fonts_next) {
2482 global_fonts_next->next = f;
2483 global_fonts_next = global_fonts_next->next;
2485 global_fonts_next = global_fonts = f;
2489 void addGlobalLanguageDir(const char*dir)
2491 msg("<notice> Adding %s to language pack directories", dir);
2494 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2495 strcpy(config_file, dir);
2496 strcat(config_file, dirseparator());
2497 strcat(config_file, "add-to-xpdfrc");
2499 fi = fopen(config_file, "rb");
2501 msg("<error> Could not open %s", config_file);
2504 globalParams->parseFile(new GString(config_file), fi);
2508 void addGlobalFontDir(const char*dirname)
2510 #ifdef HAVE_DIRENT_H
2511 msg("<notice> Adding %s to font directories", dirname);
2512 lastfontdir = strdup(dirname);
2513 DIR*dir = opendir(dirname);
2515 msg("<warning> Couldn't open directory %s", dirname);
2520 ent = readdir (dir);
2524 char*name = ent->d_name;
2530 if(!strncasecmp(&name[l-4], ".pfa", 4))
2532 if(!strncasecmp(&name[l-4], ".pfb", 4))
2534 if(!strncasecmp(&name[l-4], ".ttf", 4))
2537 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2538 strcpy(fontname, dirname);
2539 strcat(fontname, dirseparator());
2540 strcat(fontname, name);
2541 addGlobalFont(fontname);
2546 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2550 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2556 this->pagebuflen = 1024;
2557 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2558 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2560 while(pdfpage >= this->pagebuflen)
2562 int oldlen = this->pagebuflen;
2563 this->pagebuflen+=1024;
2564 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2565 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2568 this->pages[pdfpage] = outputpage;
2569 if(pdfpage>this->pagepos)
2570 this->pagepos = pdfpage;
2573 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2574 GfxColorSpace *blendingColorSpace,
2575 GBool isolated, GBool knockout,
2578 const char*colormodename = "";
2580 if(blendingColorSpace) {
2581 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2583 dbg("beginTransparencyGroup device=%08x %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2584 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);
2586 //states[statepos].createsoftmask |= forSoftMask;
2587 states[statepos].createsoftmask = forSoftMask;
2588 states[statepos].transparencygroup = !forSoftMask;
2589 states[statepos].isolated = isolated;
2591 states[statepos].olddevice = this->device;
2592 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2594 gfxdevice_record_init(this->device);
2596 /*if(!forSoftMask) { ////???
2597 state->setFillOpacity(0.0);
2602 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2605 gfxdevice_t*r = this->device;
2607 this->device = states[statepos].olddevice;
2609 gfxresult_t*recording = r->finish(r);
2611 dbg("endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2612 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2614 if(states[statepos].createsoftmask) {
2615 states[statepos-1].softmaskrecording = recording;
2617 states[statepos-1].grouprecording = recording;
2620 states[statepos].createsoftmask = 0;
2621 states[statepos].transparencygroup = 0;
2625 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2627 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2628 "colordodge","colorburn","hardlight","softlight","difference",
2629 "exclusion","hue","saturation","color","luminosity"};
2631 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2632 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2634 if(state->getBlendMode() == gfxBlendNormal)
2635 infofeature("transparency groups");
2638 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2639 warnfeature(buffer, 0);
2642 gfxresult_t*grouprecording = states[statepos].grouprecording;
2644 int blendmode = state->getBlendMode();
2645 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2646 int alpha = (int)(state->getFillOpacity()*255);
2647 if(blendmode == gfxBlendMultiply && alpha>200)
2650 gfxdevice_ops_init(&ops, this->device, alpha);
2651 gfxresult_record_replay(grouprecording, &ops);
2654 grouprecording->destroy(grouprecording);
2656 states[statepos].grouprecording = 0;
2659 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2661 if(states[statepos].softmask) {
2662 /* shouldn't happen, but *does* happen */
2663 clearSoftMask(state);
2666 /* alpha = 1: retrieve mask values from alpha layer
2667 alpha = 0: retrieve mask values from luminance */
2669 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2670 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2671 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2672 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2674 infofeature("soft masks");
2676 warnfeature("soft masks from alpha channel",0);
2678 states[statepos].olddevice = this->device;
2679 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2680 gfxdevice_record_init(this->device);
2682 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2684 states[statepos].softmask = 1;
2685 states[statepos].softmask_alpha = alpha;
2688 static inline Guchar div255(int x) {
2689 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2692 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2694 if(c < min) c = min;
2695 if(c > max) c = max;
2699 void GFXOutputDev::clearSoftMask(GfxState *state)
2701 if(!states[statepos].softmask)
2703 states[statepos].softmask = 0;
2704 dbg("clearSoftMask statepos=%d", statepos);
2705 msg("<verbose> clearSoftMask statepos=%d", statepos);
2707 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2708 msg("<error> Error in softmask/tgroup ordering");
2712 gfxresult_t*mask = states[statepos].softmaskrecording;
2713 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2714 this->device = states[statepos].olddevice;
2716 /* get outline of all objects below the soft mask */
2717 gfxdevice_t uniondev;
2718 gfxdevice_union_init(&uniondev, 0);
2719 gfxresult_record_replay(below, &uniondev);
2720 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2721 uniondev.finish(&uniondev);
2722 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2723 gfxline_free(belowoutline);belowoutline=0;
2725 this->device->startclip(this->device, belowoutline);
2726 gfxresult_record_replay(below, this->device);
2727 gfxresult_record_replay(mask, this->device);
2728 this->device->endclip(this->device);
2731 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2732 if(width<=0 || height<=0)
2735 gfxdevice_t belowrender;
2736 gfxdevice_render_init(&belowrender);
2737 if(states[statepos+1].isolated) {
2738 belowrender.setparameter(&belowrender, "fillwhite", "1");
2740 belowrender.setparameter(&belowrender, "antialize", "2");
2741 belowrender.startpage(&belowrender, width, height);
2742 gfxresult_record_replay(below, &belowrender);
2743 belowrender.endpage(&belowrender);
2744 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2745 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2746 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2748 gfxdevice_t maskrender;
2749 gfxdevice_render_init(&maskrender);
2750 maskrender.startpage(&maskrender, width, height);
2751 gfxresult_record_replay(mask, &maskrender);
2752 maskrender.endpage(&maskrender);
2753 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2754 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2756 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2757 msg("<fatal> Internal error in mask drawing");
2762 for(y=0;y<height;y++) {
2763 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2764 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2765 for(x=0;x<width;x++) {
2767 if(states[statepos].softmask_alpha) {
2770 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2773 l2->a = div255(alpha*l2->a);
2775 /* DON'T premultiply alpha- this is done by fillbitmap,
2776 depending on the output device */
2777 //l2->r = div255(alpha*l2->r);
2778 //l2->g = div255(alpha*l2->g);
2779 //l2->b = div255(alpha*l2->b);
2785 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2788 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2789 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2791 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2793 mask->destroy(mask);
2794 below->destroy(below);
2795 maskresult->destroy(maskresult);
2796 belowresult->destroy(belowresult);
2797 states[statepos].softmaskrecording = 0;
2802 // public: ~MemCheck()
2804 // delete globalParams;globalParams=0;
2805 // Object::memCheck(stderr);
2806 // gMemReport(stderr);