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>
42 #include <goo/GooString.h>
43 #include <goo/gfile.h>
58 #include "OutputDev.h"
61 //#include "NameToUnicodeTable.h"
62 #include "GlobalParams.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;
109 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
110 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
111 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
112 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
113 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
114 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
115 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
116 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
117 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
118 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
119 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
120 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
121 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
122 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
125 static int verbose = 0;
126 static int dbgindent = 1;
127 static void dbg(const char*format, ...)
134 va_start(arglist, format);
135 vsnprintf(buf, sizeof(buf)-1, format, arglist);
138 while(l && buf[l-1]=='\n') {
143 int indent = dbgindent;
152 GFXOutputGlobals*gfxglobals=0;
154 GFXOutputGlobals::GFXOutputGlobals()
156 this->featurewarnings = 0;
158 this->textmodeinfo = 0;
162 GFXOutputGlobals::~GFXOutputGlobals()
164 feature_t*f = this->featurewarnings;
166 feature_t*next = f->next;
168 free(f->string);f->string =0;
174 this->featurewarnings = 0;
177 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
179 feature_t*f = gfxglobals->featurewarnings;
181 if(!strcmp(feature, f->string))
185 f = (feature_t*)malloc(sizeof(feature_t));
186 f->string = strdup(feature);
187 f->next = gfxglobals->featurewarnings;
188 gfxglobals->featurewarnings = f;
190 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
191 if(this->config_break_on_warning) {
192 msg("<fatal> Aborting conversion due to unsupported feature");
196 msg("<notice> File contains %s",feature);
199 void GFXOutputDev::warnfeature(const char*feature,char fully)
201 showfeature(feature,fully,1);
203 void GFXOutputDev::infofeature(const char*feature)
205 showfeature(feature,0,0);
208 GFXOutputState::GFXOutputState() {
210 this->createsoftmask = 0;
211 this->transparencygroup = 0;
213 this->grouprecording = 0;
217 GBool GFXOutputDev::interpretType3Chars()
222 typedef struct _drawnchar
240 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
241 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
246 free(chars);chars = 0;
253 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
257 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
260 chars[num_chars].x = x;
261 chars[num_chars].y = y;
262 chars[num_chars].color = color;
263 chars[num_chars].charid = charid;
267 char* writeOutStdFont(fontentry* f)
272 char* tmpFileName = mktmpname(namebuf1);
274 sprintf(namebuf2, "%s.afm", tmpFileName);
275 fi = fopen(namebuf2, "wb");
278 fwrite(f->afm, 1, f->afmlen, fi);
281 sprintf(namebuf2, "%s.pfb", tmpFileName);
282 fi = fopen(namebuf2, "wb");
285 fwrite(f->pfb, 1, f->pfblen, fi);
287 return strdup(namebuf2);
289 void unlinkfont(char* filename)
294 msg("<verbose> Removing temporary font file %s", filename);
297 if(!strncmp(&filename[l-4],".afm",4)) {
298 memcpy(&filename[l-4],".pfb",4); unlink(filename);
299 memcpy(&filename[l-4],".pfa",4); unlink(filename);
300 memcpy(&filename[l-4],".afm",4);
303 if(!strncmp(&filename[l-4],".pfa",4)) {
304 memcpy(&filename[l-4],".afm",4); unlink(filename);
305 memcpy(&filename[l-4],".pfa",4);
308 if(!strncmp(&filename[l-4],".pfb",4)) {
309 memcpy(&filename[l-4],".afm",4); unlink(filename);
310 memcpy(&filename[l-4],".pfb",4);
315 static int config_use_fontconfig = 1;
316 static int fcinitcalled = 0;
318 GFXGlobalParams::GFXGlobalParams()
319 : GlobalParams((char*)"")
321 //setupBaseFonts(char *dir); //not tested yet
323 GFXGlobalParams::~GFXGlobalParams()
325 msg("<verbose> Performing cleanups");
327 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
328 if(pdf2t1map[t].fullfilename) {
329 unlinkfont(pdf2t1map[t].fullfilename);
332 #ifdef HAVE_FONTCONFIG
333 if(config_use_fontconfig && fcinitcalled)
337 #ifdef HAVE_FONTCONFIG
338 static char fc_ismatch(FcPattern*match, char*family, char*style)
340 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
341 FcBool scalable=FcFalse, outline=FcFalse;
342 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
343 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
344 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
345 FcPatternGetBool(match, "outline", 0, &outline);
346 FcPatternGetBool(match, "scalable", 0, &scalable);
348 if(scalable!=FcTrue || outline!=FcTrue)
351 if (!strcasecmp(fcfamily, family)) {
352 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
355 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
361 char* fontconfig_searchForFont(char*name)
363 #ifdef HAVE_FONTCONFIG
364 if(!config_use_fontconfig)
367 // call init ony once
371 // check whether we have a config file
372 char* configfile = (char*)FcConfigFilename(0);
373 int configexists = 0;
374 FILE*fi = fopen(configfile, "rb");
376 configexists = 1;fclose(fi);
377 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
379 msg("<debug> Initializing FontConfig (no configfile)");
383 /* A fontconfig instance which didn't find a configfile is unbelievably
384 cranky, so let's just write out a small xml file and make fontconfig
386 FcConfig*c = FcConfigCreate();
388 char* tmpFileName = mktmpname(namebuf);
389 FILE*fi = fopen(tmpFileName, "wb");
390 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
392 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
394 fprintf(fi, "<dir>~/.fonts</dir>\n");
396 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
398 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
399 fprintf(fi, "</fontconfig>\n");
401 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
402 FcConfigBuildFonts(c);
403 FcConfigSetCurrent(c);
407 msg("<debug> FontConfig Initialization failed. Disabling.");
408 config_use_fontconfig = 0;
411 FcConfig * config = FcConfigGetCurrent();
413 msg("<debug> FontConfig Config Initialization failed. Disabling.");
414 config_use_fontconfig = 0;
418 /* add external fonts to fontconfig's config, too. */
419 fontfile_t*fd = global_fonts;
421 FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
425 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
426 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
427 if(!set || !set->nfont) {
428 msg("<debug> FontConfig has zero fonts. Disabling.");
429 config_use_fontconfig = 0;
433 if(getLogLevel() >= LOGLEVEL_TRACE) {
435 for(t=0;t<set->nfont;t++) {
436 char*fcfamily=0,*fcstyle=0,*filename=0;
437 FcBool scalable=FcFalse, outline=FcFalse;
438 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
439 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
440 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
441 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
442 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
443 if(scalable && outline) {
444 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
450 char*family = strdup(name);
452 char*dash = strchr(family, '-');
453 FcPattern*pattern = 0;
457 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
458 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
460 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
461 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
465 FcConfigSubstitute(0, pattern, FcMatchPattern);
466 FcDefaultSubstitute(pattern);
468 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
471 for(t=0;t<set->nfont;t++) {
472 FcPattern*match = set->fonts[t];
473 //FcPattern*match = FcFontMatch(0, pattern, &result);
474 if(fc_ismatch(match, family, style)) {
476 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
477 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
480 //FcPatternDestroy(match);
481 msg("<debug> fontconfig: returning filename %s", filename);
483 FcPatternDestroy(pattern);
484 FcFontSetDestroy(set);
485 return filename?strdup(filename):0;
490 FcPatternDestroy(pattern);
491 FcFontSetDestroy(set);
498 static DisplayFontParamKind detectFontType(const char*filename)
500 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
501 return displayFontTT;
502 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
503 return displayFontT1;
504 return displayFontTT;
507 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
509 msg("<verbose> looking for font %s in global params", fontName->getCString());
511 char*name = fontName->getCString();
513 /* see if it is a pdf standard font */
515 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
516 if(!strcmp(name, pdf2t1map[t].pdffont)) {
517 if(!pdf2t1map[t].fullfilename) {
518 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
519 if(!pdf2t1map[t].fullfilename) {
520 msg("<error> Couldn't save default font- is the Temp Directory writable?");
522 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
525 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
526 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
531 int bestlen = 0x7fffffff;
532 const char*bestfilename = 0;
534 #ifndef HAVE_FONTCONFIG
535 /* if we don't have fontconfig, try a simple filename-comparison approach */
536 fontfile_t*f = global_fonts;
538 if(strstr(f->filename, name)) {
539 if(f->len < bestlen) {
541 bestfilename = f->filename;
548 /* if we didn't find anything up to now, try looking for the
549 font via fontconfig */
552 filename = fontconfig_searchForFont(name);
554 filename = strdup(bestfilename);
558 msg("<verbose> Font %s maps to %s\n", name, filename);
559 DisplayFontParamKind kind = detectFontType(filename);
560 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
561 if(kind == displayFontTT) {
562 dfp->tt.fileName = new GString(filename);
564 dfp->t1.fileName = new GString(filename);
569 return GlobalParams::getDisplayFont(fontName);
572 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
575 gfxglobals = new GFXOutputGlobals();
579 this->xref = doc->getXRef();
581 this->type3active = 0;
584 this->user_movex = 0;
585 this->user_movey = 0;
588 this->user_clipx1 = 0;
589 this->user_clipy1 = 0;
590 this->user_clipx2 = 0;
591 this->user_clipy2 = 0;
592 this->current_fontinfo = 0;
593 this->current_text_stroke = 0;
594 this->current_text_clip = 0;
595 this->outer_clip_box = 0;
596 this->config_bigchar=0;
597 this->config_convertgradients=1;
598 this->config_break_on_warning=0;
599 this->config_remapunicode=0;
600 this->config_transparent=0;
601 this->config_extrafontdata = 0;
602 this->config_optimize_polygons = 0;
603 this->config_multiply = 1;
607 memset(states, 0, sizeof(states));
610 void GFXOutputDev::setParameter(const char*key, const char*value)
612 if(!strcmp(key,"breakonwarning")) {
613 this->config_break_on_warning = atoi(value);
614 } else if(!strcmp(key,"remapunicode")) {
615 this->config_remapunicode = atoi(value);
616 } else if(!strcmp(key,"transparent")) {
617 this->config_transparent = atoi(value);
618 } else if(!strcmp(key,"extrafontdata")) {
619 this->config_extrafontdata = atoi(value);
620 } else if(!strcmp(key,"convertgradients")) {
621 this->config_convertgradients = atoi(value);
622 } else if(!strcmp(key,"multiply")) {
623 this->config_multiply = atoi(value);
624 if(this->config_multiply<1)
625 this->config_multiply=1;
626 } else if(!strcmp(key,"optimize_polygons")) {
627 this->config_optimize_polygons = atoi(value);
631 void GFXOutputDev::setDevice(gfxdevice_t*dev)
636 void GFXOutputDev::setMove(int x,int y)
638 this->user_movex = x;
639 this->user_movey = y;
642 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
644 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
645 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
647 this->user_clipx1 = x1;
648 this->user_clipy1 = y1;
649 this->user_clipx2 = x2;
650 this->user_clipy2 = y2;
653 static char*getFontName(GfxFont*font)
656 GString*gstr = font->getName();
657 char* fname = gstr==0?0:gstr->getCString();
661 sprintf(buf, "UFONT%d", r->num);
662 fontid = strdup(buf);
664 fontid = strdup(fname);
668 char* plus = strchr(fontid, '+');
669 if(plus && plus < &fontid[strlen(fontid)-1]) {
670 fontname = strdup(plus+1);
672 fontname = strdup(fontid);
678 static void dumpFontInfo(const char*loglevel, GfxFont*font);
679 static int lastdumps[1024];
680 static int lastdumppos = 0;
685 static void showFontError(GfxFont*font, int nr)
689 for(t=0;t<lastdumppos;t++)
690 if(lastdumps[t] == r->num)
694 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
695 lastdumps[lastdumppos++] = r->num;
697 msg("<warning> The following font caused problems:");
699 msg("<warning> The following font caused problems (substituting):");
701 msg("<warning> The following Type 3 Font will be rendered as graphics:");
702 dumpFontInfo("<warning>", font);
705 static void dumpFontInfo(const char*loglevel, GfxFont*font)
707 char* id = getFontID(font);
708 char* name = getFontName(font);
709 Ref* r=font->getID();
710 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
712 GString*gstr = font->getTag();
714 msg("%s| Tag: %s", loglevel, id);
716 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
718 GfxFontType type=font->getType();
720 case fontUnknownType:
721 msg("%s| Type: unknown",loglevel);
724 msg("%s| Type: 1",loglevel);
727 msg("%s| Type: 1C",loglevel);
730 msg("%s| Type: 3",loglevel);
733 msg("%s| Type: TrueType",loglevel);
736 msg("%s| Type: CIDType0",loglevel);
739 msg("%s| Type: CIDType0C",loglevel);
742 msg("%s| Type: CIDType2",loglevel);
747 GBool embedded = font->getEmbeddedFontID(&embRef);
749 if(font->getEmbeddedFontName()) {
750 embeddedName = font->getEmbeddedFontName()->getCString();
753 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
755 gstr = font->getExtFontFile();
757 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
759 // Get font descriptor flags.
760 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
761 if(font->isSerif()) msg("%s| is serif", loglevel);
762 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
763 if(font->isItalic()) msg("%s| is italic", loglevel);
764 if(font->isBold()) msg("%s| is bold", loglevel);
770 //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");}
771 //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");}
773 void dump_outline(gfxline_t*line)
775 /*gfxbbox_t*r = gfxline_isrectangle(line);
777 printf("is not a rectangle\n");
779 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
783 if(line->type == gfx_moveTo) {
784 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
785 } else if(line->type == gfx_lineTo) {
786 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
787 } else if(line->type == gfx_splineTo) {
788 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
794 void gfxPath_dump(GfxPath*path)
796 int num = path->getNumSubpaths();
799 for(t = 0; t < num; t++) {
800 GfxSubpath *subpath = path->getSubpath(t);
801 int subnum = subpath->getNumPoints();
803 for(s=0;s<subnum;s++) {
804 double x=subpath->getX(s);
805 double y=subpath->getY(s);
806 if(s==0 && !subpath->getCurve(s)) {
807 printf("M %f %f\n", x, y);
808 } else if(s==0 && subpath->getCurve(s)) {
809 printf("E %f %f\n", x, y);
810 } else if(subpath->getCurve(s)) {
811 printf("C %f %f\n", x, y);
813 printf("T %f %f\n", x, y);
819 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
821 int num = path->getNumSubpaths();
824 double lastx=0,lasty=0,posx=0,posy=0;
827 msg("<warning> empty path");
831 gfxdrawer_target_gfxline(&draw);
833 for(t = 0; t < num; t++) {
834 GfxSubpath *subpath = path->getSubpath(t);
835 int subnum = subpath->getNumPoints();
836 double bx=0,by=0,cx=0,cy=0;
838 for(s=0;s<subnum;s++) {
841 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
844 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
845 draw.lineTo(&draw, lastx, lasty);
847 draw.moveTo(&draw, x,y);
852 } else if(subpath->getCurve(s) && cpos==0) {
856 } else if(subpath->getCurve(s) && cpos==1) {
864 draw.lineTo(&draw, x,y);
866 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
873 /* fix non-closed lines */
874 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
875 draw.lineTo(&draw, lastx, lasty);
877 gfxline_t*result = (gfxline_t*)draw.result(&draw);
879 gfxline_optimize(result);
884 GBool GFXOutputDev::useTilingPatternFill()
886 infofeature("tiled patterns");
887 // if(config_convertgradients)
891 GBool GFXOutputDev::useShadedFills()
893 infofeature("shaded fills");
894 if(config_convertgradients)
899 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
901 state->transform(x,y,nx,ny);
902 *nx += user_movex + clipmovex;
903 *ny += user_movey + clipmovey;
907 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
908 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
909 int paintType, Dict *resDict,
910 double *mat, double *bbox,
911 int x0, int y0, int x1, int y1,
912 double xStep, double yStep)
914 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
915 int paintType, Dict *resDict,
916 double *mat, double *bbox,
917 int x0, int y0, int x1, int y1,
918 double xStep, double yStep)
921 msg("<debug> tilingPatternFill");
922 infofeature("tiling pattern fills");
925 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
927 msg("<error> functionShadedFill not supported yet");
928 infofeature("function shaded fills");
931 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
935 colspace->getRGB(col, &rgb);
936 c.r = colToByte(rgb.r);
937 c.g = colToByte(rgb.g);
938 c.b = colToByte(rgb.b);
943 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
945 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
946 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
949 this->transformXY(state, x0,y0, &x0,&y0);
950 this->transformXY(state, x1,y1, &x1,&y1);
951 this->transformXY(state, x2,y2, &x2,&y2);
956 shading->getColor(0.0, &color0);
957 shading->getColor(0.5, &color1);
958 shading->getColor(1.0, &color2);
960 GfxColorSpace* colspace = shading->getColorSpace();
962 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
963 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
964 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
965 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
966 infofeature("radial shaded fills");
968 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
972 g[0].color = col2col(colspace, &color0);
973 g[1].color = col2col(colspace, &color1);
974 g[2].color = col2col(colspace, &color2);
979 gfxbbox_t b = states[statepos].clipbbox;
980 gfxline_t p1,p2,p3,p4,p5;
981 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
982 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
983 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
984 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
985 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
988 //m.m00 = (x3-x0); m.m10 = (x1-x0);
989 //m.m01 = (y3-y0); m.m11 = (y1-y0);
990 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
991 m.m00 = (x1-x0); m.m10 = (x2-x0);
992 m.m01 = (y1-y0); m.m11 = (y2-y0);
996 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1000 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1003 shading->getCoords(&x0,&y0,&x1,&y1);
1004 this->transformXY(state, x0,y0,&x0,&y0);
1005 this->transformXY(state, x1,y1,&x1,&y1);
1010 shading->getColor(0.0, &color0);
1011 shading->getColor(0.5, &color1);
1012 shading->getColor(1.0, &color2);
1014 GfxColorSpace* colspace = shading->getColorSpace();
1016 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1017 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1018 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1019 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1021 infofeature("axial shaded fills");
1023 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1027 g[0].color = col2col(colspace, &color0);
1028 g[1].color = col2col(colspace, &color1);
1029 g[2].color = col2col(colspace, &color2);
1034 double xMin,yMin,xMax,yMax;
1035 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1036 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1037 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1040 xMin = 1024; yMin = 1024;
1042 gfxbbox_t b = states[statepos].clipbbox;
1043 gfxline_t p1,p2,p3,p4,p5;
1044 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1045 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1046 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1047 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1048 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1050 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1051 the middle of the two control points */
1053 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1054 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1055 m.tx = (x0 + x1)/2 - 0.5;
1056 m.ty = (y0 + y1)/2 - 0.5;
1058 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1064 GBool GFXOutputDev::useDrawForm()
1066 infofeature("forms");
1069 void GFXOutputDev::drawForm(Ref id)
1071 msg("<error> drawForm not implemented");
1073 GBool GFXOutputDev::needNonText()
1077 void GFXOutputDev::endPage()
1079 msg("<verbose> endPage (GfxOutputDev)");
1080 if(outer_clip_box) {
1081 device->endclip(device);
1084 /* notice: we're not fully done yet with this page- there might still be
1085 a few calls to drawLink() yet to come */
1088 static inline double sqr(double x) {return x*x;}
1090 #define STROKE_FILL 1
1091 #define STROKE_CLIP 2
1092 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1094 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1095 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1096 double miterLimit = state->getMiterLimit();
1097 double width = state->getTransformedLineWidth();
1100 double opaq = state->getStrokeOpacity();
1102 state->getFillRGB(&rgb);
1104 state->getStrokeRGB(&rgb);
1106 col.r = colToByte(rgb.r);
1107 col.g = colToByte(rgb.g);
1108 col.b = colToByte(rgb.b);
1109 col.a = (unsigned char)(opaq*255);
1111 gfx_capType capType = gfx_capRound;
1112 if(lineCap == 0) capType = gfx_capButt;
1113 else if(lineCap == 1) capType = gfx_capRound;
1114 else if(lineCap == 2) capType = gfx_capSquare;
1115 else msg("<error> Invalid line cap type");
1117 gfx_joinType joinType = gfx_joinRound;
1118 if(lineJoin == 0) joinType = gfx_joinMiter;
1119 else if(lineJoin == 1) joinType = gfx_joinRound;
1120 else if(lineJoin == 2) joinType = gfx_joinBevel;
1121 else msg("<error> Invalid line join type");
1123 gfxline_t*line2 = 0;
1125 int dashLength = states[statepos].dashLength;
1126 double*dashPattern = states[statepos].dashPattern;
1127 double dashStart = states[statepos].dashStart;
1128 if(dashLength && dashPattern) {
1129 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1132 /* try to find out how much the transformation matrix would
1133 stretch the dashes, and factor that into the dash lengths.
1134 This is not the entirely correct approach- it would be
1135 better to first convert the path to an unscaled version,
1136 then apply dashing, and then transform the path using
1137 the current transformation matrix. However there are few
1138 PDFs which actually stretch a dashed path in a non-orthonormal
1140 double tx1, ty1, tx2, ty2, tx3, ty3;
1141 this->transformXY(state, 0, 0, &tx1, &ty1);
1142 this->transformXY(state, 0, 1, &tx2, &ty2);
1143 this->transformXY(state, 1, 0, &tx3, &ty3);
1144 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1145 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1147 warnfeature("non-ortogonally dashed strokes", 0);
1148 double f = (d1+d2)/2;
1150 msg("<trace> %d dashes", dashLength);
1151 msg("<trace> | phase: %f", dashStart);
1152 for(t=0;t<dashLength;t++) {
1153 dash[t] = (float)dashPattern[t] * f;
1157 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1159 dash[dashLength] = -1;
1160 if(getLogLevel() >= LOGLEVEL_TRACE) {
1164 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1168 msg("<trace> After dashing:");
1171 if(getLogLevel() >= LOGLEVEL_TRACE) {
1172 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1174 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1175 lineCap==0?"butt": (lineCap==1?"round":"square"),
1177 col.r,col.g,col.b,col.a
1182 if(flags&STROKE_FILL) {
1183 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1184 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1185 if(getLogLevel() >= LOGLEVEL_TRACE) {
1186 dump_outline(gfxline);
1189 msg("<warning> Empty polygon (resulting from stroked line)");
1191 if(flags&STROKE_CLIP) {
1192 device->startclip(device, gfxline);
1193 states[statepos].clipping++;
1195 device->fill(device, gfxline, &col);
1197 gfxline_free(gfxline);
1200 if(flags&STROKE_CLIP)
1201 msg("<error> Stroke&clip not supported at the same time");
1202 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1206 gfxline_free(line2);
1209 gfxcolor_t getFillColor(GfxState * state)
1212 double opaq = state->getFillOpacity();
1213 state->getFillRGB(&rgb);
1215 col.r = colToByte(rgb.r);
1216 col.g = colToByte(rgb.g);
1217 col.b = colToByte(rgb.b);
1218 col.a = (unsigned char)(opaq*255);
1222 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1224 gfxcolor_t col = getFillColor(state);
1226 if(getLogLevel() >= LOGLEVEL_TRACE) {
1227 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1230 device->fill(device, line, &col);
1233 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1235 if(getLogLevel() >= LOGLEVEL_TRACE) {
1238 gfxbbox_t bbox = gfxline_getbbox(line);
1239 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1241 device->startclip(device, line);
1242 states[statepos].clipping++;
1245 void GFXOutputDev::clip(GfxState *state)
1247 GfxPath * path = state->getPath();
1248 msg("<trace> clip");
1249 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1250 if(config_optimize_polygons) {
1251 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1255 clipToGfxLine(state, line);
1259 void GFXOutputDev::eoClip(GfxState *state)
1261 GfxPath * path = state->getPath();
1262 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1263 clipToGfxLine(state, line);
1266 void GFXOutputDev::clipToStrokePath(GfxState *state)
1268 GfxPath * path = state->getPath();
1269 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1271 if(getLogLevel() >= LOGLEVEL_TRACE) {
1272 double width = state->getTransformedLineWidth();
1273 msg("<trace> cliptostrokepath width=%f", width);
1277 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1281 void GFXOutputDev::finish()
1283 if(outer_clip_box) {
1285 device->endclip(device);
1291 GFXOutputDev::~GFXOutputDev()
1295 GBool GFXOutputDev::upsideDown()
1299 GBool GFXOutputDev::useDrawChar()
1304 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1305 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1307 static char tmp_printstr[4096];
1308 char* makeStringPrintable(char*str)
1310 int len = strlen(str);
1317 for(t=0;t<len;t++) {
1322 tmp_printstr[t] = c;
1325 tmp_printstr[len++] = '.';
1326 tmp_printstr[len++] = '.';
1327 tmp_printstr[len++] = '.';
1329 tmp_printstr[len] = 0;
1330 return tmp_printstr;
1332 void GFXOutputDev::updateFontMatrix(GfxState*state)
1334 double* ctm = state->getCTM();
1335 double fontSize = state->getFontSize();
1336 double*textMat = state->getTextMat();
1338 /* taking the absolute value of horizScaling seems to be required for
1339 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1340 double hscale = fabs(state->getHorizScaling());
1342 // from xpdf-3.02/SplashOutputDev:updateFont
1343 double mm11 = textMat[0] * fontSize * hscale;
1344 double mm12 = textMat[1] * fontSize * hscale;
1345 double mm21 = textMat[2] * fontSize;
1346 double mm22 = textMat[3] * fontSize;
1348 // multiply with ctm, like state->getFontTransMat() does
1349 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1350 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1351 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1352 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1353 this->current_font_matrix.tx = 0;
1354 this->current_font_matrix.ty = 0;
1357 void GFXOutputDev::beginString(GfxState *state, GString *s)
1359 int render = state->getRender();
1360 if(current_text_stroke) {
1361 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1363 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1366 static gfxline_t* mkEmptyGfxShape(double x, double y)
1368 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1369 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1373 static char isValidUnicode(int c)
1375 if(c>=32 && c<0x2fffe)
1380 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1381 double dx, double dy,
1382 double originX, double originY,
1383 CharCode charid, int nBytes, Unicode *_u, int uLen)
1385 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1386 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1390 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1392 int render = state->getRender();
1393 gfxcolor_t col = getFillColor(state);
1395 // check for invisible text -- this is used by Acrobat Capture
1396 if (render == RENDER_INVISIBLE) {
1398 if(!config_extrafontdata)
1402 GfxFont*font = state->getFont();
1404 if(font->getType() == fontType3) {
1405 /* type 3 chars are passed as graphics */
1406 msg("<debug> type3 char at %f/%f", x, y);
1410 Unicode u = uLen?(_u[0]):0;
1412 gfxmatrix_t m = this->current_font_matrix;
1413 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1414 //m.tx += originX; m.ty += originY;
1416 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d font=%08x",m.tx,m.ty,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
1418 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1419 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1421 msg("<debug> Drawing glyph %d as shape", charid);
1422 if(!gfxglobals->textmodeinfo) {
1423 msg("<notice> Some texts will be rendered as shape");
1424 gfxglobals->textmodeinfo = 1;
1426 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1427 gfxline_t*tglyph = gfxline_clone(glyph);
1428 gfxline_transform(tglyph, &m);
1429 if((render&3) != RENDER_INVISIBLE) {
1430 gfxline_t*add = gfxline_clone(tglyph);
1431 current_text_stroke = gfxline_append(current_text_stroke, add);
1433 if(render&RENDER_CLIP) {
1434 gfxline_t*add = gfxline_clone(tglyph);
1435 current_text_clip = gfxline_append(current_text_clip, add);
1436 if(!current_text_clip) {
1437 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1440 gfxline_free(tglyph);
1444 void GFXOutputDev::endString(GfxState *state)
1446 int render = state->getRender();
1447 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1449 if(current_text_stroke) {
1450 /* fillstroke and stroke text rendering objects we can process right
1451 now (as there may be texts of other rendering modes in this
1452 text object)- clipping objects have to wait until endTextObject,
1454 device->setparameter(device, "mark","TXT");
1455 if((render&3) == RENDER_FILL) {
1456 fillGfxLine(state, current_text_stroke);
1457 gfxline_free(current_text_stroke);
1458 current_text_stroke = 0;
1459 } else if((render&3) == RENDER_FILLSTROKE) {
1460 fillGfxLine(state, current_text_stroke);
1461 strokeGfxline(state, current_text_stroke,0);
1462 gfxline_free(current_text_stroke);
1463 current_text_stroke = 0;
1464 } else if((render&3) == RENDER_STROKE) {
1465 strokeGfxline(state, current_text_stroke,0);
1466 gfxline_free(current_text_stroke);
1467 current_text_stroke = 0;
1469 device->setparameter(device, "mark","");
1473 void GFXOutputDev::endTextObject(GfxState *state)
1475 int render = state->getRender();
1476 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1478 if(current_text_clip) {
1479 device->setparameter(device, "mark","TXT");
1480 clipToGfxLine(state, current_text_clip);
1481 device->setparameter(device, "mark","");
1482 gfxline_free(current_text_clip);
1483 current_text_clip = 0;
1487 /* the logic seems to be as following:
1488 first, beginType3Char is called, with the charcode and the coordinates.
1489 if this function returns true, it already knew about the char and has now drawn it.
1490 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1491 called with some parameters.
1492 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1493 at the position first passed to beginType3Char). the char ends with endType3Char.
1495 The drawing operations between beginType3Char and endType3Char are somewhat different to
1496 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1497 color determines the color of a font)
1500 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1502 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1505 if(config_extrafontdata && current_fontinfo) {
1507 gfxmatrix_t m = this->current_font_matrix;
1508 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1509 m.m00*=INTERNAL_FONT_SIZE;
1510 m.m01*=INTERNAL_FONT_SIZE;
1511 m.m10*=INTERNAL_FONT_SIZE;
1512 m.m11*=INTERNAL_FONT_SIZE;
1514 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1515 msg("<error> Invalid charid %d for font", charid);
1518 gfxcolor_t col={0,0,0,0};
1519 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1520 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1524 /* the character itself is going to be passed using the draw functions */
1525 return gFalse; /* gTrue= is_in_cache? */
1528 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1530 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1533 void GFXOutputDev::endType3Char(GfxState *state)
1536 msg("<debug> endType3Char");
1539 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1541 this->currentpage = pageNum;
1543 int rot = doc->getPageRotate(1);
1544 gfxcolor_t white = {255,255,255,255};
1545 gfxcolor_t black = {255,0,0,0};
1547 gfxline_t clippath[5];
1549 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1550 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1551 Use CropBox, not MediaBox, as page size
1558 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1559 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1561 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1562 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1564 this->clipmovex = -(int)x1;
1565 this->clipmovey = -(int)y1;
1567 /* apply user clip box */
1568 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1569 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1570 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1571 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1572 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1573 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1575 x1 += this->clipmovex;
1576 y1 += this->clipmovey;
1577 x2 += this->clipmovex;
1578 y2 += this->clipmovey;
1581 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1583 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);
1585 msg("<verbose> page is rotated %d degrees", rot);
1587 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1588 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1589 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1590 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1591 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1592 device->startclip(device, clippath); outer_clip_box = 1;
1593 if(!config_transparent) {
1594 device->fill(device, clippath, &white);
1596 states[statepos].clipbbox.xmin = x1;
1597 states[statepos].clipbbox.ymin = x1;
1598 states[statepos].clipbbox.xmax = x2;
1599 states[statepos].clipbbox.ymax = y2;
1601 states[statepos].dashPattern = 0;
1602 states[statepos].dashLength = 0;
1603 states[statepos].dashStart = 0;
1607 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1609 double x1, y1, x2, y2;
1610 gfxline_t points[5];
1613 msg("<debug> drawlink");
1615 link->getRect(&x1, &y1, &x2, &y2);
1616 cvtUserToDev(x1, y1, &x, &y);
1617 points[0].type = gfx_moveTo;
1618 points[0].x = points[4].x = x + user_movex + clipmovex;
1619 points[0].y = points[4].y = y + user_movey + clipmovey;
1620 points[0].next = &points[1];
1621 cvtUserToDev(x2, y1, &x, &y);
1622 points[1].type = gfx_lineTo;
1623 points[1].x = x + user_movex + clipmovex;
1624 points[1].y = y + user_movey + clipmovey;
1625 points[1].next = &points[2];
1626 cvtUserToDev(x2, y2, &x, &y);
1627 points[2].type = gfx_lineTo;
1628 points[2].x = x + user_movex + clipmovex;
1629 points[2].y = y + user_movey + clipmovey;
1630 points[2].next = &points[3];
1631 cvtUserToDev(x1, y2, &x, &y);
1632 points[3].type = gfx_lineTo;
1633 points[3].x = x + user_movex + clipmovex;
1634 points[3].y = y + user_movey + clipmovey;
1635 points[3].next = &points[4];
1636 cvtUserToDev(x1, y1, &x, &y);
1637 points[4].type = gfx_lineTo;
1638 points[4].x = x + user_movex + clipmovex;
1639 points[4].y = y + user_movey + clipmovey;
1642 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1643 points[0].x, points[0].y,
1644 points[1].x, points[1].y,
1645 points[2].x, points[2].y,
1646 points[3].x, points[3].y);
1648 if(getLogLevel() >= LOGLEVEL_TRACE) {
1649 dump_outline(points);
1652 LinkAction*action=link->getAction();
1655 const char*type = "-?-";
1658 msg("<trace> drawlink action=%d", action->getKind());
1659 switch(action->getKind())
1663 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1664 LinkDest *dest=NULL;
1665 if (ha->getDest()==NULL)
1666 dest=catalog->findDest(ha->getNamedDest());
1668 dest=ha->getDest()->copy();
1670 if (dest->isPageRef()){
1671 Ref pageref=dest->getPageRef();
1672 page=catalog->findPage(pageref.num,pageref.gen);
1674 else page=dest->getPageNum();
1675 sprintf(buf, "%d", page);
1683 LinkGoToR*l = (LinkGoToR*)action;
1684 GString*g = l->getFileName();
1686 s = strdup(g->getCString());
1688 /* if the GoToR link has no filename, then
1689 try to find a refernce in the *local*
1691 GString*g = l->getNamedDest();
1693 s = strdup(g->getCString());
1699 LinkNamed*l = (LinkNamed*)action;
1700 GString*name = l->getName();
1702 s = strdup(name->lowerCase()->getCString());
1703 named = name->getCString();
1706 if(strstr(s, "next") || strstr(s, "forward"))
1708 page = currentpage + 1;
1710 else if(strstr(s, "prev") || strstr(s, "back"))
1712 page = currentpage - 1;
1714 else if(strstr(s, "last") || strstr(s, "end"))
1716 if(this->page2page && this->num_pages) {
1717 page = this->page2page[this->num_pages-1];
1720 else if(strstr(s, "first") || strstr(s, "top"))
1728 case actionLaunch: {
1730 LinkLaunch*l = (LinkLaunch*)action;
1731 GString * str = new GString(l->getFileName());
1732 GString * params = l->getParams();
1734 str->append(params);
1735 s = strdup(str->getCString());
1742 LinkURI*l = (LinkURI*)action;
1743 GString*g = l->getURI();
1745 url = g->getCString();
1750 case actionUnknown: {
1752 LinkUnknown*l = (LinkUnknown*)action;
1757 msg("<error> Unknown link type!");
1762 if(!s) s = strdup("-?-");
1764 msg("<trace> drawlink s=%s", s);
1766 if(!gfxglobals->linkinfo && (page || s))
1768 msg("<notice> File contains links");
1769 gfxglobals->linkinfo = 1;
1775 for(t=1;t<=this->num_pages;t++) {
1776 if(this->page2page[t]==page) {
1786 sprintf(buf, "page%d", lpage);
1787 device->drawlink(device, points, buf);
1791 device->drawlink(device, points, s);
1794 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1798 void GFXOutputDev::saveState(GfxState *state) {
1799 dbg("saveState %08x", state); dbgindent+=2;
1801 msg("<trace> saveState %08x", state);
1804 msg("<fatal> Too many nested states in pdf.");
1808 states[statepos].state = state;
1809 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1810 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1811 states[statepos].clipping = 0;
1812 states[statepos].olddevice = 0;
1813 states[statepos].clipbbox = states[statepos-1].clipbbox;
1815 states[statepos].dashPattern = states[statepos-1].dashPattern;
1816 states[statepos].dashStart = states[statepos-1].dashStart;
1817 states[statepos].dashLength = states[statepos-1].dashLength;
1820 void GFXOutputDev::restoreState(GfxState *state) {
1821 dbgindent-=2; dbg("restoreState %08x", state);
1824 msg("<fatal> Invalid restoreState");
1827 msg("<trace> restoreState %08x%s%s", state,
1828 states[statepos].softmask?" (end softmask)":"",
1829 states[statepos].clipping?" (end clipping)":"");
1830 if(states[statepos].softmask) {
1831 clearSoftMask(state);
1834 if(states[statepos].dashPattern) {
1835 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1836 free(states[statepos].dashPattern);
1837 states[statepos].dashPattern = 0;
1843 while(states[statepos].clipping) {
1844 device->endclip(device);
1845 states[statepos].clipping--;
1847 if(states[statepos].state!=state) {
1848 msg("<fatal> bad state nesting");
1851 states[statepos].state=0;
1855 void GFXOutputDev::updateLineDash(GfxState *state)
1857 if(states[statepos].dashPattern &&
1858 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1859 free(states[statepos].dashPattern);
1860 states[statepos].dashPattern = 0;
1862 double *pattern = 0;
1865 state->getLineDash(&pattern, &dashLength, &dashStart);
1866 msg("<debug> updateLineDash, %d dashes", dashLength);
1868 states[statepos].dashPattern = 0;
1869 states[statepos].dashLength = 0;
1871 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1872 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1873 states[statepos].dashPattern = p;
1874 states[statepos].dashLength = dashLength;
1875 states[statepos].dashStart = dashStart;
1879 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1881 this->page2page = page2page;
1882 this->num_pages = num_pages;
1885 void GFXOutputDev::updateLineWidth(GfxState *state)
1887 double width = state->getTransformedLineWidth();
1890 void GFXOutputDev::updateLineCap(GfxState *state)
1892 int c = state->getLineCap();
1895 void GFXOutputDev::updateLineJoin(GfxState *state)
1897 int j = state->getLineJoin();
1900 void GFXOutputDev::updateFillColor(GfxState *state)
1903 double opaq = state->getFillOpacity();
1904 state->getFillRGB(&rgb);
1906 void GFXOutputDev::updateFillOpacity(GfxState *state)
1909 double opaq = state->getFillOpacity();
1910 state->getFillRGB(&rgb);
1911 dbg("update fillopaq %f", opaq);
1913 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1915 double opaq = state->getFillOpacity();
1916 dbg("update strokeopaq %f", opaq);
1918 void GFXOutputDev::updateFillOverprint(GfxState *state)
1920 double opaq = state->getFillOverprint();
1921 dbg("update filloverprint %f", opaq);
1923 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1925 double opaq = state->getStrokeOverprint();
1926 dbg("update strokeoverprint %f", opaq);
1928 void GFXOutputDev::updateTransfer(GfxState *state)
1930 dbg("update transfer");
1934 void GFXOutputDev::updateStrokeColor(GfxState *state)
1937 double opaq = state->getStrokeOpacity();
1938 state->getStrokeRGB(&rgb);
1941 void GFXOutputDev::updateFont(GfxState *state)
1943 GfxFont* gfxFont = state->getFont();
1947 char*id = getFontID(gfxFont);
1948 msg("<verbose> Updating font to %s", id);
1949 if(gfxFont->getType() == fontType3) {
1950 infofeature("Type3 fonts");
1951 if(!config_extrafontdata) {
1956 msg("<error> Internal Error: FontID is null");
1960 this->current_fontinfo = this->info->getFont(id);
1962 if(!this->current_fontinfo) {
1963 msg("<error> Internal Error: no fontinfo for font %s", id);
1966 if(!this->current_fontinfo->seen) {
1967 dumpFontInfo("<verbose>", gfxFont);
1970 current_gfxfont = this->current_fontinfo->getGfxFont();
1971 device->addfont(device, current_gfxfont);
1974 updateFontMatrix(state);
1977 #define SQR(x) ((x)*(x))
1979 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1981 if((newwidth<1 || newheight<1) ||
1982 (width<=newwidth || height<=newheight))
1984 unsigned char*newdata;
1986 newdata= (unsigned char*)malloc(newwidth*newheight);
1987 double fx = ((double)width)/newwidth;
1988 double fy = ((double)height)/newheight;
1990 int blocksize = (int)(8192/(fx*fy));
1991 int r = 8192*256/palettesize;
1992 for(x=0;x<newwidth;x++) {
1993 double ex = px + fx;
1994 int fromx = (int)px;
1996 int xweight1 = (int)((1-(px-fromx))*256);
1997 int xweight2 = (int)((ex-tox)*256);
1999 for(y=0;y<newheight;y++) {
2000 double ey = py + fy;
2001 int fromy = (int)py;
2003 int yweight1 = (int)((1-(py-fromy))*256);
2004 int yweight2 = (int)((ey-toy)*256);
2011 for(xx=fromx;xx<=tox;xx++)
2012 for(yy=fromy;yy<=toy;yy++) {
2013 int b = 1-data[width*yy+xx];
2015 if(xx==fromx) weight = (weight*xweight1)/256;
2016 if(xx==tox) weight = (weight*xweight2)/256;
2017 if(yy==fromy) weight = (weight*yweight1)/256;
2018 if(yy==toy) weight = (weight*yweight2)/256;
2021 //if(a) a=(palettesize-1)*r/blocksize;
2022 newdata[y*newwidth+x] = (a*blocksize)/r;
2030 #define IMAGE_TYPE_JPEG 0
2031 #define IMAGE_TYPE_LOSSLESS 1
2033 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2034 double x1,double y1,
2035 double x2,double y2,
2036 double x3,double y3,
2037 double x4,double y4, int type, int multiply)
2039 gfxcolor_t*newpic=0;
2041 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2042 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2044 gfxline_t p1,p2,p3,p4,p5;
2045 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2046 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2047 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2048 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2049 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2051 {p1.x = (int)(p1.x*20)/20.0;
2052 p1.y = (int)(p1.y*20)/20.0;
2053 p2.x = (int)(p2.x*20)/20.0;
2054 p2.y = (int)(p2.y*20)/20.0;
2055 p3.x = (int)(p3.x*20)/20.0;
2056 p3.y = (int)(p3.y*20)/20.0;
2057 p4.x = (int)(p4.x*20)/20.0;
2058 p4.y = (int)(p4.y*20)/20.0;
2059 p5.x = (int)(p5.x*20)/20.0;
2060 p5.y = (int)(p5.y*20)/20.0;
2064 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2065 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2067 m.tx = p1.x - 0.5*multiply;
2068 m.ty = p1.y - 0.5*multiply;
2071 img.data = (gfxcolor_t*)data;
2075 if(type == IMAGE_TYPE_JPEG)
2076 /* TODO: pass image_dpi to device instead */
2077 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2080 dev->fillbitmap(dev, &p1, &img, &m, 0);
2083 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2084 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2086 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2089 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2090 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2092 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2096 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2097 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2098 GBool inlineImg, int mask, int*maskColors,
2099 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2101 /* the code in this function is *old*. It's not pretty, but it works. */
2103 double x1,y1,x2,y2,x3,y3,x4,y4;
2104 ImageStream *imgStr;
2109 unsigned char* maskbitmap = 0;
2112 ncomps = colorMap->getNumPixelComps();
2113 bits = colorMap->getBits();
2118 unsigned char buf[8];
2119 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2121 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2122 imgMaskStr->reset();
2123 unsigned char pal[256];
2124 int n = 1 << colorMap->getBits();
2129 maskColorMap->getGray(pixBuf, &gray);
2130 pal[t] = colToByte(gray);
2132 for (y = 0; y < maskHeight; y++) {
2133 for (x = 0; x < maskWidth; x++) {
2134 imgMaskStr->getPixel(buf);
2135 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2140 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2141 imgMaskStr->reset();
2142 for (y = 0; y < maskHeight; y++) {
2143 for (x = 0; x < maskWidth; x++) {
2144 imgMaskStr->getPixel(buf);
2146 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2154 imgStr = new ImageStream(str, width, ncomps,bits);
2157 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2159 msg("<verbose> Ignoring %d by %d image", width, height);
2160 unsigned char buf[8];
2162 for (y = 0; y < height; ++y)
2163 for (x = 0; x < width; ++x) {
2164 imgStr->getPixel(buf);
2172 this->transformXY(state, 0, 1, &x1, &y1);
2173 this->transformXY(state, 0, 0, &x2, &y2);
2174 this->transformXY(state, 1, 0, &x3, &y3);
2175 this->transformXY(state, 1, 1, &x4, &y4);
2178 /* as type 3 bitmaps are antialized, we need to place them
2179 at integer coordinates, otherwise flash player's antializing
2180 will kick in and make everything blurry */
2181 x1 = (int)(x1);y1 = (int)(y1);
2182 x2 = (int)(x2);y2 = (int)(y2);
2183 x3 = (int)(x3);y3 = (int)(y3);
2184 x4 = (int)(x4);y4 = (int)(y4);
2187 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2189 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2190 gfxglobals->pbminfo = 1;
2193 msg("<verbose> drawing %d by %d masked picture", width, height);
2195 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2196 msg("<notice> File contains jpeg pictures");
2197 gfxglobals->jpeginfo = 1;
2201 unsigned char buf[8];
2203 unsigned char*pic = new unsigned char[width*height];
2204 gfxcolor_t pal[256];
2206 state->getFillRGB(&rgb);
2208 memset(pal,255,sizeof(pal));
2209 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2210 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2211 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2212 pal[0].a = 255; pal[1].a = 0;
2215 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2216 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2217 for (y = 0; y < height; ++y)
2218 for (x = 0; x < width; ++x)
2220 imgStr->getPixel(buf);
2223 pic[width*y+x] = buf[0];
2227 unsigned char*pic2 = 0;
2230 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2239 height = realheight;
2243 /* make a black/white palette */
2245 float r = 255./(float)(numpalette-1);
2247 for(t=0;t<numpalette;t++) {
2248 pal[t].r = colToByte(rgb.r);
2249 pal[t].g = colToByte(rgb.g);
2250 pal[t].b = colToByte(rgb.b);
2251 pal[t].a = (unsigned char)(t*r);
2256 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2257 for (y = 0; y < height; ++y) {
2258 for (x = 0; x < width; ++x) {
2259 pic2[width*y+x] = pal[pic[y*width+x]];
2262 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2266 if(maskbitmap) free(maskbitmap);
2272 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2273 gfxcolor_t*pic=new gfxcolor_t[width*height];
2274 for (y = 0; y < height; ++y) {
2275 for (x = 0; x < width; ++x) {
2276 imgStr->getPixel(pixBuf);
2277 colorMap->getRGB(pixBuf, &rgb);
2278 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2279 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2280 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2281 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2283 int x1 = x*maskWidth/width;
2284 int y1 = y*maskHeight/height;
2285 int x2 = (x+1)*maskWidth/width;
2286 int y2 = (y+1)*maskHeight/height;
2288 unsigned int alpha=0;
2289 unsigned int count=0;
2290 for(xx=x1;xx<x2;xx++)
2291 for(yy=y1;yy<y2;yy++) {
2292 alpha += maskbitmap[yy*maskWidth+xx];
2296 pic[width*y+x].a = alpha / count;
2298 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2303 if(str->getKind()==strDCT)
2304 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2306 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2309 if(maskbitmap) free(maskbitmap);
2312 gfxcolor_t*pic=new gfxcolor_t[width*height];
2313 gfxcolor_t pal[256];
2314 int n = 1 << colorMap->getBits();
2316 for(t=0;t<256;t++) {
2318 colorMap->getRGB(pixBuf, &rgb);
2320 {/*if(maskColors && *maskColors==t) {
2321 msg("<notice> Color %d is transparent", t);
2322 if (imgData->maskColors) {
2324 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2325 if (pix[i] < imgData->maskColors[2*i] ||
2326 pix[i] > imgData->maskColors[2*i+1]) {
2341 pal[t].r = (unsigned char)(colToByte(rgb.r));
2342 pal[t].g = (unsigned char)(colToByte(rgb.g));
2343 pal[t].b = (unsigned char)(colToByte(rgb.b));
2344 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2347 for (y = 0; y < height; ++y) {
2348 for (x = 0; x < width; ++x) {
2349 imgStr->getPixel(pixBuf);
2350 pic[width*y+x] = pal[pixBuf[0]];
2354 if(maskWidth < width && maskHeight < height) {
2355 for(y = 0; y < height; y++) {
2356 for (x = 0; x < width; x++) {
2357 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2361 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2362 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2363 double dx = width / maskWidth;
2364 double dy = height / maskHeight;
2366 for(y = 0; y < maskHeight; y++) {
2368 for (x = 0; x < maskWidth; x++) {
2369 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2370 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2378 height = maskHeight;
2381 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2385 if(maskbitmap) free(maskbitmap);
2390 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2391 int width, int height, GBool invert,
2394 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2395 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2396 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2399 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2400 int width, int height, GfxImageColorMap *colorMap,
2401 int *maskColors, GBool inlineImg)
2403 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2404 colorMap?"colorMap":"no colorMap",
2405 maskColors?"maskColors":"no maskColors",
2407 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2408 colorMap?"colorMap":"no colorMap",
2409 maskColors?"maskColors":"no maskColors",
2412 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2413 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2414 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2417 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2418 int width, int height,
2419 GfxImageColorMap *colorMap,
2420 Stream *maskStr, int maskWidth, int maskHeight,
2423 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2424 colorMap?"colorMap":"no colorMap",
2425 maskWidth, maskHeight);
2426 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2427 colorMap?"colorMap":"no colorMap",
2428 maskWidth, maskHeight);
2430 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2431 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2432 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2435 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2436 int width, int height,
2437 GfxImageColorMap *colorMap,
2439 int maskWidth, int maskHeight,
2440 GfxImageColorMap *maskColorMap)
2442 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2443 colorMap?"colorMap":"no colorMap",
2444 maskWidth, maskHeight);
2445 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2446 colorMap?"colorMap":"no colorMap",
2447 maskWidth, maskHeight);
2449 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2450 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2451 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2454 void GFXOutputDev::stroke(GfxState *state)
2458 GfxPath * path = state->getPath();
2459 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2460 strokeGfxline(state, line, 0);
2464 void GFXOutputDev::fill(GfxState *state)
2466 gfxcolor_t col = getFillColor(state);
2467 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2469 GfxPath * path = state->getPath();
2470 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2471 if(config_optimize_polygons) {
2472 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2476 fillGfxLine(state, line);
2480 void GFXOutputDev::eoFill(GfxState *state)
2482 gfxcolor_t col = getFillColor(state);
2483 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2485 GfxPath * path = state->getPath();
2486 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2487 fillGfxLine(state, line);
2492 static const char* dirseparator()
2501 void addGlobalFont(const char*filename)
2503 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2504 memset(f, 0, sizeof(fontfile_t));
2505 f->filename = filename;
2506 int len = strlen(filename);
2507 char*r1 = strrchr(filename, '/');
2508 char*r2 = strrchr(filename, '\\');
2516 msg("<verbose> Adding font \"%s\".", filename);
2517 if(global_fonts_next) {
2518 global_fonts_next->next = f;
2519 global_fonts_next = global_fonts_next->next;
2521 global_fonts_next = global_fonts = f;
2525 void addGlobalLanguageDir(const char*dir)
2527 msg("<notice> Adding %s to language pack directories", dir);
2530 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2531 strcpy(config_file, dir);
2532 strcat(config_file, dirseparator());
2533 strcat(config_file, "add-to-xpdfrc");
2535 fi = fopen(config_file, "rb");
2537 msg("<error> Could not open %s", config_file);
2540 globalParams->parseFile(new GString(config_file), fi);
2544 void addGlobalFontDir(const char*dirname)
2546 #ifdef HAVE_DIRENT_H
2547 DIR*dir = opendir(dirname);
2549 msg("<warning> Couldn't open directory %s", dirname);
2555 ent = readdir (dir);
2559 char*name = ent->d_name;
2565 if(!strncasecmp(&name[l-4], ".pfa", 4))
2567 if(!strncasecmp(&name[l-4], ".pfb", 4))
2569 if(!strncasecmp(&name[l-4], ".ttf", 4))
2572 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2573 strcpy(fontname, dirname);
2574 strcat(fontname, dirseparator());
2575 strcat(fontname, name);
2576 addGlobalFont(fontname);
2580 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2583 msg("<warning> No dirent.h");
2587 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2588 GfxColorSpace *blendingColorSpace,
2589 GBool isolated, GBool knockout,
2592 const char*colormodename = "";
2594 if(blendingColorSpace) {
2595 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2597 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);
2598 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);
2600 //states[statepos].createsoftmask |= forSoftMask;
2601 states[statepos].createsoftmask = forSoftMask;
2602 states[statepos].transparencygroup = !forSoftMask;
2603 states[statepos].isolated = isolated;
2605 states[statepos].olddevice = this->device;
2606 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2607 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2609 gfxdevice_record_init(this->device);
2611 /*if(!forSoftMask) { ////???
2612 state->setFillOpacity(0.0);
2617 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2620 gfxdevice_t*r = this->device;
2622 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2624 this->device = states[statepos].olddevice;
2626 msg("<fatal> Bad state nesting in transparency group");
2627 msg("<fatal> Notice: this is a known problem, which will be fixed in 0.9.1");
2628 msg("<fatal> In the meantime, please convert the file with -s poly2bitmap");
2629 restoreState(state);
2630 this->device = states[statepos].olddevice;
2632 states[statepos].olddevice = 0;
2634 gfxresult_t*recording = r->finish(r);
2636 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2637 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2639 if(states[statepos].createsoftmask) {
2640 states[statepos-1].softmaskrecording = recording;
2642 states[statepos-1].grouprecording = recording;
2645 states[statepos].createsoftmask = 0;
2646 states[statepos].transparencygroup = 0;
2650 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2652 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2653 "colordodge","colorburn","hardlight","softlight","difference",
2654 "exclusion","hue","saturation","color","luminosity"};
2656 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2657 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2659 if(state->getBlendMode() == gfxBlendNormal)
2660 infofeature("transparency groups");
2663 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2664 warnfeature(buffer, 0);
2667 gfxresult_t*grouprecording = states[statepos].grouprecording;
2669 int blendmode = state->getBlendMode();
2670 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2671 int alpha = (int)(state->getFillOpacity()*255);
2672 if(blendmode == gfxBlendMultiply && alpha>200)
2675 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2676 gfxdevice_ops_init(&ops, this->device, alpha);
2677 gfxresult_record_replay(grouprecording, &ops);
2680 grouprecording->destroy(grouprecording);
2682 states[statepos].grouprecording = 0;
2685 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2687 if(states[statepos].softmask) {
2688 /* shouldn't happen, but *does* happen */
2689 clearSoftMask(state);
2692 /* alpha = 1: retrieve mask values from alpha layer
2693 alpha = 0: retrieve mask values from luminance */
2695 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2696 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2697 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2698 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2700 infofeature("soft masks");
2702 warnfeature("soft masks from alpha channel",0);
2704 if(states[statepos].olddevice) {
2705 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2708 states[statepos].olddevice = this->device;
2709 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2710 gfxdevice_record_init(this->device);
2712 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2714 states[statepos].softmask = 1;
2715 states[statepos].softmask_alpha = alpha;
2718 static inline Guchar div255(int x) {
2719 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2722 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2724 if(c < min) c = min;
2725 if(c > max) c = max;
2729 void GFXOutputDev::clearSoftMask(GfxState *state)
2731 if(!states[statepos].softmask)
2733 states[statepos].softmask = 0;
2734 dbg("clearSoftMask statepos=%d", statepos);
2735 msg("<verbose> clearSoftMask statepos=%d", statepos);
2737 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2738 msg("<error> Error in softmask/tgroup ordering");
2742 gfxresult_t*mask = states[statepos].softmaskrecording;
2743 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2744 this->device = states[statepos].olddevice;
2746 /* get outline of all objects below the soft mask */
2747 gfxdevice_t uniondev;
2748 gfxdevice_union_init(&uniondev, 0);
2749 gfxresult_record_replay(below, &uniondev);
2750 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2751 uniondev.finish(&uniondev);
2752 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2753 gfxline_free(belowoutline);belowoutline=0;
2755 this->device->startclip(this->device, belowoutline);
2756 gfxresult_record_replay(below, this->device);
2757 gfxresult_record_replay(mask, this->device);
2758 this->device->endclip(this->device);
2761 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2762 if(width<=0 || height<=0)
2765 gfxdevice_t belowrender;
2766 gfxdevice_render_init(&belowrender);
2767 if(states[statepos+1].isolated) {
2768 belowrender.setparameter(&belowrender, "fillwhite", "1");
2770 belowrender.setparameter(&belowrender, "antialize", "2");
2771 belowrender.startpage(&belowrender, width, height);
2772 gfxresult_record_replay(below, &belowrender);
2773 belowrender.endpage(&belowrender);
2774 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2775 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2776 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2778 gfxdevice_t maskrender;
2779 gfxdevice_render_init(&maskrender);
2780 maskrender.startpage(&maskrender, width, height);
2781 gfxresult_record_replay(mask, &maskrender);
2782 maskrender.endpage(&maskrender);
2783 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2784 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2786 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2787 msg("<fatal> Internal error in mask drawing");
2792 for(y=0;y<height;y++) {
2793 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2794 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2795 for(x=0;x<width;x++) {
2797 if(states[statepos].softmask_alpha) {
2800 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2803 l2->a = div255(alpha*l2->a);
2805 /* DON'T premultiply alpha- this is done by fillbitmap,
2806 depending on the output device */
2807 //l2->r = div255(alpha*l2->r);
2808 //l2->g = div255(alpha*l2->g);
2809 //l2->b = div255(alpha*l2->b);
2815 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2818 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2819 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2821 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2823 mask->destroy(mask);
2824 below->destroy(below);
2825 maskresult->destroy(maskresult);
2826 belowresult->destroy(belowresult);
2827 states[statepos].softmaskrecording = 0;
2832 // public: ~MemCheck()
2834 // delete globalParams;globalParams=0;
2835 // Object::memCheck(stderr);
2836 // gMemReport(stderr);