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_gfxfont = 0;
593 this->current_fontinfo = 0;
594 this->current_text_stroke = 0;
595 this->current_text_clip = 0;
596 this->outer_clip_box = 0;
597 this->config_bigchar=0;
598 this->config_convertgradients=1;
599 this->config_break_on_warning=0;
600 this->config_remapunicode=0;
601 this->config_transparent=0;
602 this->config_extrafontdata = 0;
603 this->config_drawonlyshapes = 0;
604 this->config_disable_polygon_conversion = 0;
605 this->config_multiply = 1;
609 memset(states, 0, sizeof(states));
612 void GFXOutputDev::setParameter(const char*key, const char*value)
614 if(!strcmp(key,"breakonwarning")) {
615 this->config_break_on_warning = atoi(value);
616 } else if(!strcmp(key,"remapunicode")) {
617 this->config_remapunicode = atoi(value);
618 } else if(!strcmp(key,"transparent")) {
619 this->config_transparent = atoi(value);
620 } else if(!strcmp(key,"drawonlyshapes")) {
621 this->config_drawonlyshapes = atoi(value);
622 } else if(!strcmp(key,"extrafontdata")) {
623 this->config_extrafontdata = atoi(value);
624 } else if(!strcmp(key,"convertgradients")) {
625 this->config_convertgradients = atoi(value);
626 } else if(!strcmp(key,"multiply")) {
627 this->config_multiply = atoi(value);
628 if(this->config_multiply<1)
629 this->config_multiply=1;
630 } else if(!strcmp(key,"disable_polygon_conversion")) {
631 this->config_disable_polygon_conversion = atoi(value);
635 void GFXOutputDev::setDevice(gfxdevice_t*dev)
640 void GFXOutputDev::setMove(int x,int y)
642 this->user_movex = x;
643 this->user_movey = y;
646 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
648 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
649 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
651 this->user_clipx1 = x1;
652 this->user_clipy1 = y1;
653 this->user_clipx2 = x2;
654 this->user_clipy2 = y2;
657 static char*getFontName(GfxFont*font)
660 GString*gstr = font->getName();
661 char* fname = gstr==0?0:gstr->getCString();
665 sprintf(buf, "UFONT%d", r->num);
666 fontid = strdup(buf);
668 fontid = strdup(fname);
672 char* plus = strchr(fontid, '+');
673 if(plus && plus < &fontid[strlen(fontid)-1]) {
674 fontname = strdup(plus+1);
676 fontname = strdup(fontid);
682 static void dumpFontInfo(const char*loglevel, GfxFont*font);
683 static int lastdumps[1024];
684 static int lastdumppos = 0;
689 static void showFontError(GfxFont*font, int nr)
693 for(t=0;t<lastdumppos;t++)
694 if(lastdumps[t] == r->num)
698 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
699 lastdumps[lastdumppos++] = r->num;
701 msg("<warning> The following font caused problems:");
703 msg("<warning> The following font caused problems (substituting):");
705 msg("<warning> The following Type 3 Font will be rendered as graphics:");
706 dumpFontInfo("<warning>", font);
709 static void dumpFontInfo(const char*loglevel, GfxFont*font)
711 char* id = getFontID(font);
712 char* name = getFontName(font);
713 Ref* r=font->getID();
714 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
716 GString*gstr = font->getTag();
718 msg("%s| Tag: %s", loglevel, id);
720 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
722 GfxFontType type=font->getType();
724 case fontUnknownType:
725 msg("%s| Type: unknown",loglevel);
728 msg("%s| Type: 1",loglevel);
731 msg("%s| Type: 1C",loglevel);
734 msg("%s| Type: 3",loglevel);
737 msg("%s| Type: TrueType",loglevel);
740 msg("%s| Type: CIDType0",loglevel);
743 msg("%s| Type: CIDType0C",loglevel);
746 msg("%s| Type: CIDType2",loglevel);
751 GBool embedded = font->getEmbeddedFontID(&embRef);
753 if(font->getEmbeddedFontName()) {
754 embeddedName = font->getEmbeddedFontName()->getCString();
757 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
759 gstr = font->getExtFontFile();
761 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
763 // Get font descriptor flags.
764 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
765 if(font->isSerif()) msg("%s| is serif", loglevel);
766 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
767 if(font->isItalic()) msg("%s| is italic", loglevel);
768 if(font->isBold()) msg("%s| is bold", loglevel);
774 //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");}
775 //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");}
777 void dump_outline(gfxline_t*line)
779 /*gfxbbox_t*r = gfxline_isrectangle(line);
781 printf("is not a rectangle\n");
783 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
787 if(line->type == gfx_moveTo) {
788 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
789 } else if(line->type == gfx_lineTo) {
790 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
791 } else if(line->type == gfx_splineTo) {
792 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
798 void gfxPath_dump(GfxPath*path)
800 int num = path->getNumSubpaths();
803 for(t = 0; t < num; t++) {
804 GfxSubpath *subpath = path->getSubpath(t);
805 int subnum = subpath->getNumPoints();
807 for(s=0;s<subnum;s++) {
808 double x=subpath->getX(s);
809 double y=subpath->getY(s);
810 if(s==0 && !subpath->getCurve(s)) {
811 printf("M %f %f\n", x, y);
812 } else if(s==0 && subpath->getCurve(s)) {
813 printf("E %f %f\n", x, y);
814 } else if(subpath->getCurve(s)) {
815 printf("C %f %f\n", x, y);
817 printf("T %f %f\n", x, y);
823 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
825 int num = path->getNumSubpaths();
828 double lastx=0,lasty=0,posx=0,posy=0;
831 msg("<warning> empty path");
835 gfxdrawer_target_gfxline(&draw);
837 for(t = 0; t < num; t++) {
838 GfxSubpath *subpath = path->getSubpath(t);
839 int subnum = subpath->getNumPoints();
840 double bx=0,by=0,cx=0,cy=0;
842 for(s=0;s<subnum;s++) {
845 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
848 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
849 draw.lineTo(&draw, lastx, lasty);
851 draw.moveTo(&draw, x,y);
856 } else if(subpath->getCurve(s) && cpos==0) {
860 } else if(subpath->getCurve(s) && cpos==1) {
868 draw.lineTo(&draw, x,y);
870 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
877 /* fix non-closed lines */
878 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
879 draw.lineTo(&draw, lastx, lasty);
881 gfxline_t*result = (gfxline_t*)draw.result(&draw);
883 gfxline_optimize(result);
888 GBool GFXOutputDev::useTilingPatternFill()
890 infofeature("tiled patterns");
891 // if(config_convertgradients)
895 GBool GFXOutputDev::useShadedFills()
897 infofeature("shaded fills");
898 if(config_convertgradients)
903 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
905 state->transform(x,y,nx,ny);
906 *nx += user_movex + clipmovex;
907 *ny += user_movey + clipmovey;
911 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
912 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
913 int paintType, Dict *resDict,
914 double *mat, double *bbox,
915 int x0, int y0, int x1, int y1,
916 double xStep, double yStep)
918 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
919 int paintType, Dict *resDict,
920 double *mat, double *bbox,
921 int x0, int y0, int x1, int y1,
922 double xStep, double yStep)
925 msg("<debug> tilingPatternFill");
926 infofeature("tiling pattern fills");
929 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
931 msg("<error> functionShadedFill not supported yet");
932 infofeature("function shaded fills");
935 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
939 colspace->getRGB(col, &rgb);
940 c.r = colToByte(rgb.r);
941 c.g = colToByte(rgb.g);
942 c.b = colToByte(rgb.b);
947 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
949 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
950 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
953 this->transformXY(state, x0,y0, &x0,&y0);
954 this->transformXY(state, x1,y1, &x1,&y1);
955 this->transformXY(state, x2,y2, &x2,&y2);
960 shading->getColor(0.0, &color0);
961 shading->getColor(0.5, &color1);
962 shading->getColor(1.0, &color2);
964 GfxColorSpace* colspace = shading->getColorSpace();
966 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
967 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
968 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
969 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
970 infofeature("radial shaded fills");
972 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
976 g[0].color = col2col(colspace, &color0);
977 g[1].color = col2col(colspace, &color1);
978 g[2].color = col2col(colspace, &color2);
983 gfxbbox_t b = states[statepos].clipbbox;
984 gfxline_t p1,p2,p3,p4,p5;
985 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
986 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
987 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
988 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
989 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
992 //m.m00 = (x3-x0); m.m10 = (x1-x0);
993 //m.m01 = (y3-y0); m.m11 = (y1-y0);
994 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
995 m.m00 = (x1-x0); m.m10 = (x2-x0);
996 m.m01 = (y1-y0); m.m11 = (y2-y0);
1000 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1004 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1007 shading->getCoords(&x0,&y0,&x1,&y1);
1008 this->transformXY(state, x0,y0,&x0,&y0);
1009 this->transformXY(state, x1,y1,&x1,&y1);
1014 shading->getColor(0.0, &color0);
1015 shading->getColor(0.5, &color1);
1016 shading->getColor(1.0, &color2);
1018 GfxColorSpace* colspace = shading->getColorSpace();
1020 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1021 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1022 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1023 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1025 infofeature("axial shaded fills");
1027 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1031 g[0].color = col2col(colspace, &color0);
1032 g[1].color = col2col(colspace, &color1);
1033 g[2].color = col2col(colspace, &color2);
1038 double xMin,yMin,xMax,yMax;
1039 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1040 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1041 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1044 xMin = 1024; yMin = 1024;
1046 gfxbbox_t b = states[statepos].clipbbox;
1047 gfxline_t p1,p2,p3,p4,p5;
1048 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1049 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1050 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1051 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1052 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1054 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1055 the middle of the two control points */
1057 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1058 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1059 m.tx = (x0 + x1)/2 - 0.5;
1060 m.ty = (y0 + y1)/2 - 0.5;
1062 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1068 GBool GFXOutputDev::useDrawForm()
1070 infofeature("forms");
1073 void GFXOutputDev::drawForm(Ref id)
1075 msg("<error> drawForm not implemented");
1077 GBool GFXOutputDev::needNonText()
1081 void GFXOutputDev::endPage()
1083 msg("<verbose> endPage (GfxOutputDev)");
1084 if(outer_clip_box) {
1085 device->endclip(device);
1088 /* notice: we're not fully done yet with this page- there might still be
1089 a few calls to drawLink() yet to come */
1092 static inline double sqr(double x) {return x*x;}
1094 #define STROKE_FILL 1
1095 #define STROKE_CLIP 2
1096 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1098 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1099 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1100 double miterLimit = state->getMiterLimit();
1101 double width = state->getTransformedLineWidth();
1104 double opaq = state->getStrokeOpacity();
1106 state->getFillRGB(&rgb);
1108 state->getStrokeRGB(&rgb);
1110 col.r = colToByte(rgb.r);
1111 col.g = colToByte(rgb.g);
1112 col.b = colToByte(rgb.b);
1113 col.a = (unsigned char)(opaq*255);
1115 gfx_capType capType = gfx_capRound;
1116 if(lineCap == 0) capType = gfx_capButt;
1117 else if(lineCap == 1) capType = gfx_capRound;
1118 else if(lineCap == 2) capType = gfx_capSquare;
1119 else msg("<error> Invalid line cap type");
1121 gfx_joinType joinType = gfx_joinRound;
1122 if(lineJoin == 0) joinType = gfx_joinMiter;
1123 else if(lineJoin == 1) joinType = gfx_joinRound;
1124 else if(lineJoin == 2) joinType = gfx_joinBevel;
1125 else msg("<error> Invalid line join type");
1127 gfxline_t*line2 = 0;
1129 int dashLength = states[statepos].dashLength;
1130 double*dashPattern = states[statepos].dashPattern;
1131 double dashStart = states[statepos].dashStart;
1132 if(dashLength && dashPattern) {
1133 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1136 /* try to find out how much the transformation matrix would
1137 stretch the dashes, and factor that into the dash lengths.
1138 This is not the entirely correct approach- it would be
1139 better to first convert the path to an unscaled version,
1140 then apply dashing, and then transform the path using
1141 the current transformation matrix. However there are few
1142 PDFs which actually stretch a dashed path in a non-orthonormal
1144 double tx1, ty1, tx2, ty2, tx3, ty3;
1145 this->transformXY(state, 0, 0, &tx1, &ty1);
1146 this->transformXY(state, 0, 1, &tx2, &ty2);
1147 this->transformXY(state, 1, 0, &tx3, &ty3);
1148 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1149 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1151 warnfeature("non-ortogonally dashed strokes", 0);
1152 double f = (d1+d2)/2;
1154 msg("<trace> %d dashes", dashLength);
1155 msg("<trace> | phase: %f", dashStart);
1156 for(t=0;t<dashLength;t++) {
1157 dash[t] = (float)dashPattern[t] * f;
1161 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1163 dash[dashLength] = -1;
1164 if(getLogLevel() >= LOGLEVEL_TRACE) {
1168 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1172 msg("<trace> After dashing:");
1175 if(getLogLevel() >= LOGLEVEL_TRACE) {
1176 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1178 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1179 lineCap==0?"butt": (lineCap==1?"round":"square"),
1181 col.r,col.g,col.b,col.a
1186 if(flags&STROKE_FILL) {
1187 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1188 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1189 if(getLogLevel() >= LOGLEVEL_TRACE) {
1190 dump_outline(gfxline);
1193 msg("<warning> Empty polygon (resulting from stroked line)");
1195 if(flags&STROKE_CLIP) {
1196 device->startclip(device, gfxline);
1197 states[statepos].clipping++;
1199 device->fill(device, gfxline, &col);
1201 gfxline_free(gfxline);
1202 gfxpoly_destroy(poly);
1204 if(flags&STROKE_CLIP)
1205 msg("<error> Stroke&clip not supported at the same time");
1206 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1210 gfxline_free(line2);
1213 gfxcolor_t getFillColor(GfxState * state)
1216 double opaq = state->getFillOpacity();
1217 state->getFillRGB(&rgb);
1219 col.r = colToByte(rgb.r);
1220 col.g = colToByte(rgb.g);
1221 col.b = colToByte(rgb.b);
1222 col.a = (unsigned char)(opaq*255);
1226 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1228 gfxcolor_t col = getFillColor(state);
1230 if(getLogLevel() >= LOGLEVEL_TRACE) {
1231 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1234 device->fill(device, line, &col);
1237 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1239 if(getLogLevel() >= LOGLEVEL_TRACE) {
1240 msg("<trace> %sclip", evenodd?"eo":"");
1243 gfxbbox_t bbox = gfxline_getbbox(line);
1244 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1246 device->startclip(device, line);
1247 states[statepos].clipping++;
1250 void GFXOutputDev::clip(GfxState *state)
1252 GfxPath * path = state->getPath();
1253 msg("<trace> clip");
1254 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1255 if(!config_disable_polygon_conversion) {
1256 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1260 clipToGfxLine(state, line, 0);
1264 void GFXOutputDev::eoClip(GfxState *state)
1266 GfxPath * path = state->getPath();
1267 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1268 clipToGfxLine(state, line, 1);
1271 void GFXOutputDev::clipToStrokePath(GfxState *state)
1273 GfxPath * path = state->getPath();
1274 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1276 if(getLogLevel() >= LOGLEVEL_TRACE) {
1277 double width = state->getTransformedLineWidth();
1278 msg("<trace> cliptostrokepath width=%f", width);
1282 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1286 void GFXOutputDev::finish()
1288 if(outer_clip_box) {
1290 device->endclip(device);
1296 GFXOutputDev::~GFXOutputDev()
1300 GBool GFXOutputDev::upsideDown()
1304 GBool GFXOutputDev::useDrawChar()
1309 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1310 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1312 static char tmp_printstr[4096];
1313 char* makeStringPrintable(char*str)
1315 int len = strlen(str);
1322 for(t=0;t<len;t++) {
1327 tmp_printstr[t] = c;
1330 tmp_printstr[len++] = '.';
1331 tmp_printstr[len++] = '.';
1332 tmp_printstr[len++] = '.';
1334 tmp_printstr[len] = 0;
1335 return tmp_printstr;
1337 void GFXOutputDev::updateFontMatrix(GfxState*state)
1339 double* ctm = state->getCTM();
1340 double fontSize = state->getFontSize();
1341 double*textMat = state->getTextMat();
1343 /* taking the absolute value of horizScaling seems to be required for
1344 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1345 double hscale = fabs(state->getHorizScaling());
1347 // from xpdf-3.02/SplashOutputDev:updateFont
1348 double mm11 = textMat[0] * fontSize * hscale;
1349 double mm12 = textMat[1] * fontSize * hscale;
1350 double mm21 = textMat[2] * fontSize;
1351 double mm22 = textMat[3] * fontSize;
1353 // multiply with ctm, like state->getFontTransMat() does
1354 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1355 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1356 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1357 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1358 this->current_font_matrix.tx = 0;
1359 this->current_font_matrix.ty = 0;
1362 void GFXOutputDev::beginString(GfxState *state, GString *s)
1364 int render = state->getRender();
1365 if(current_text_stroke) {
1366 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1368 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1371 static gfxline_t* mkEmptyGfxShape(double x, double y)
1373 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1374 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1378 static char isValidUnicode(int c)
1380 if(c>=32 && c<0x2fffe)
1385 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1386 double dx, double dy,
1387 double originX, double originY,
1388 CharCode charid, int nBytes, Unicode *_u, int uLen)
1390 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1391 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1395 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1397 int render = state->getRender();
1398 gfxcolor_t col = getFillColor(state);
1400 // check for invisible text -- this is used by Acrobat Capture
1401 if (render == RENDER_INVISIBLE) {
1403 if(!config_extrafontdata)
1407 GfxFont*font = state->getFont();
1409 if(font->getType() == fontType3) {
1410 /* type 3 chars are passed as graphics */
1411 msg("<debug> type3 char at %f/%f", x, y);
1415 Unicode u = uLen?(_u[0]):0;
1417 gfxmatrix_t m = this->current_font_matrix;
1418 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1419 //m.tx += originX; m.ty += originY;
1421 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);
1423 if((render == RENDER_FILL && !config_drawonlyshapes) || render == RENDER_INVISIBLE) {
1424 int space = this->current_fontinfo->space_char;
1425 if(config_extrafontdata && space>=0 && m.m00 && !m.m01) {
1426 /* space char detection */
1427 if(last_char_gfxfont == current_gfxfont &&
1428 last_char_y == m.ty &&
1429 !last_char_was_space) {
1430 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1431 int space = this->current_fontinfo->space_char;
1432 if(m.tx - expected_x >= m.m00*64) {
1433 msg("<debug> There's a %f (%f) pixel gap between char %d and char %d, I'm inserting a space here",
1435 (m.tx-expected_x)/m.m00,
1436 last_char, glyphid);
1438 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1439 if(m2.tx < expected_x) m2.tx = expected_x;
1440 device->drawchar(device, current_gfxfont, space, &col, &m2);
1443 last_char_gfxfont = current_gfxfont;
1444 last_char = glyphid;
1447 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1449 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1451 msg("<debug> Drawing glyph %d as shape", charid);
1452 if(!gfxglobals->textmodeinfo) {
1453 msg("<notice> Some texts will be rendered as shape");
1454 gfxglobals->textmodeinfo = 1;
1456 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1457 gfxline_t*tglyph = gfxline_clone(glyph);
1458 gfxline_transform(tglyph, &m);
1459 if((render&3) != RENDER_INVISIBLE) {
1460 gfxline_t*add = gfxline_clone(tglyph);
1461 current_text_stroke = gfxline_append(current_text_stroke, add);
1463 if(render&RENDER_CLIP) {
1464 gfxline_t*add = gfxline_clone(tglyph);
1465 current_text_clip = gfxline_append(current_text_clip, add);
1466 if(!current_text_clip) {
1467 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1470 gfxline_free(tglyph);
1474 void GFXOutputDev::endString(GfxState *state)
1476 int render = state->getRender();
1477 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1479 if(current_text_stroke) {
1480 /* fillstroke and stroke text rendering objects we can process right
1481 now (as there may be texts of other rendering modes in this
1482 text object)- clipping objects have to wait until endTextObject,
1484 device->setparameter(device, "mark","TXT");
1485 if((render&3) == RENDER_FILL) {
1486 fillGfxLine(state, current_text_stroke, 0);
1487 gfxline_free(current_text_stroke);
1488 current_text_stroke = 0;
1489 } else if((render&3) == RENDER_FILLSTROKE) {
1490 fillGfxLine(state, current_text_stroke, 0);
1491 strokeGfxline(state, current_text_stroke,0);
1492 gfxline_free(current_text_stroke);
1493 current_text_stroke = 0;
1494 } else if((render&3) == RENDER_STROKE) {
1495 strokeGfxline(state, current_text_stroke,0);
1496 gfxline_free(current_text_stroke);
1497 current_text_stroke = 0;
1499 device->setparameter(device, "mark","");
1503 void GFXOutputDev::endTextObject(GfxState *state)
1505 int render = state->getRender();
1506 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1508 if(current_text_clip) {
1509 device->setparameter(device, "mark","TXT");
1510 clipToGfxLine(state, current_text_clip, 0);
1511 device->setparameter(device, "mark","");
1512 gfxline_free(current_text_clip);
1513 current_text_clip = 0;
1517 /* the logic seems to be as following:
1518 first, beginType3Char is called, with the charcode and the coordinates.
1519 if this function returns true, it already knew about the char and has now drawn it.
1520 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1521 called with some parameters.
1522 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1523 at the position first passed to beginType3Char). the char ends with endType3Char.
1525 The drawing operations between beginType3Char and endType3Char are somewhat different to
1526 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1527 color determines the color of a font)
1530 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1532 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1535 if(config_extrafontdata && current_fontinfo) {
1537 gfxmatrix_t m = this->current_font_matrix;
1538 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1539 m.m00*=INTERNAL_FONT_SIZE;
1540 m.m01*=INTERNAL_FONT_SIZE;
1541 m.m10*=INTERNAL_FONT_SIZE;
1542 m.m11*=INTERNAL_FONT_SIZE;
1544 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1545 msg("<error> Invalid charid %d for font", charid);
1548 gfxcolor_t col={0,0,0,0};
1549 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1550 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1554 /* the character itself is going to be passed using the draw functions */
1555 return gFalse; /* gTrue= is_in_cache? */
1558 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1560 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1563 void GFXOutputDev::endType3Char(GfxState *state)
1566 msg("<debug> endType3Char");
1569 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1571 this->currentpage = pageNum;
1573 int rot = doc->getPageRotate(1);
1574 gfxcolor_t white = {255,255,255,255};
1575 gfxcolor_t black = {255,0,0,0};
1577 gfxline_t clippath[5];
1579 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1580 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1581 Use CropBox, not MediaBox, as page size
1588 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1589 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1591 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1592 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1594 this->clipmovex = -(int)x1;
1595 this->clipmovey = -(int)y1;
1597 /* apply user clip box */
1598 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1599 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1600 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1601 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1602 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1603 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1605 x1 += this->clipmovex;
1606 y1 += this->clipmovey;
1607 x2 += this->clipmovex;
1608 y2 += this->clipmovey;
1611 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1613 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);
1615 msg("<verbose> page is rotated %d degrees", rot);
1617 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1618 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1619 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1620 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1621 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1622 device->startclip(device, clippath); outer_clip_box = 1;
1623 if(!config_transparent) {
1624 device->fill(device, clippath, &white);
1626 states[statepos].clipbbox.xmin = x1;
1627 states[statepos].clipbbox.ymin = x1;
1628 states[statepos].clipbbox.xmax = x2;
1629 states[statepos].clipbbox.ymax = y2;
1631 states[statepos].dashPattern = 0;
1632 states[statepos].dashLength = 0;
1633 states[statepos].dashStart = 0;
1635 this->last_char_gfxfont = 0;
1639 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1641 double x1, y1, x2, y2;
1642 gfxline_t points[5];
1645 msg("<debug> drawlink");
1647 link->getRect(&x1, &y1, &x2, &y2);
1648 cvtUserToDev(x1, y1, &x, &y);
1649 points[0].type = gfx_moveTo;
1650 points[0].x = points[4].x = x + user_movex + clipmovex;
1651 points[0].y = points[4].y = y + user_movey + clipmovey;
1652 points[0].next = &points[1];
1653 cvtUserToDev(x2, y1, &x, &y);
1654 points[1].type = gfx_lineTo;
1655 points[1].x = x + user_movex + clipmovex;
1656 points[1].y = y + user_movey + clipmovey;
1657 points[1].next = &points[2];
1658 cvtUserToDev(x2, y2, &x, &y);
1659 points[2].type = gfx_lineTo;
1660 points[2].x = x + user_movex + clipmovex;
1661 points[2].y = y + user_movey + clipmovey;
1662 points[2].next = &points[3];
1663 cvtUserToDev(x1, y2, &x, &y);
1664 points[3].type = gfx_lineTo;
1665 points[3].x = x + user_movex + clipmovex;
1666 points[3].y = y + user_movey + clipmovey;
1667 points[3].next = &points[4];
1668 cvtUserToDev(x1, y1, &x, &y);
1669 points[4].type = gfx_lineTo;
1670 points[4].x = x + user_movex + clipmovex;
1671 points[4].y = y + user_movey + clipmovey;
1674 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1675 points[0].x, points[0].y,
1676 points[1].x, points[1].y,
1677 points[2].x, points[2].y,
1678 points[3].x, points[3].y);
1680 if(getLogLevel() >= LOGLEVEL_TRACE) {
1681 dump_outline(points);
1684 LinkAction*action=link->getAction();
1687 const char*type = "-?-";
1690 msg("<trace> drawlink action=%d", action->getKind());
1691 switch(action->getKind())
1695 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1696 LinkDest *dest=NULL;
1697 if (ha->getDest()==NULL)
1698 dest=catalog->findDest(ha->getNamedDest());
1700 dest=ha->getDest()->copy();
1702 if (dest->isPageRef()){
1703 Ref pageref=dest->getPageRef();
1704 page=catalog->findPage(pageref.num,pageref.gen);
1706 else page=dest->getPageNum();
1707 sprintf(buf, "%d", page);
1715 LinkGoToR*l = (LinkGoToR*)action;
1716 GString*g = l->getFileName();
1718 s = strdup(g->getCString());
1720 /* if the GoToR link has no filename, then
1721 try to find a refernce in the *local*
1723 GString*g = l->getNamedDest();
1725 s = strdup(g->getCString());
1731 LinkNamed*l = (LinkNamed*)action;
1732 GString*name = l->getName();
1734 s = strdup(name->lowerCase()->getCString());
1735 named = name->getCString();
1738 if(strstr(s, "next") || strstr(s, "forward"))
1740 page = currentpage + 1;
1742 else if(strstr(s, "prev") || strstr(s, "back"))
1744 page = currentpage - 1;
1746 else if(strstr(s, "last") || strstr(s, "end"))
1748 if(this->page2page && this->num_pages) {
1749 page = this->page2page[this->num_pages-1];
1752 else if(strstr(s, "first") || strstr(s, "top"))
1760 case actionLaunch: {
1762 LinkLaunch*l = (LinkLaunch*)action;
1763 GString * str = new GString(l->getFileName());
1764 GString * params = l->getParams();
1766 str->append(params);
1767 s = strdup(str->getCString());
1774 LinkURI*l = (LinkURI*)action;
1775 GString*g = l->getURI();
1777 url = g->getCString();
1782 case actionUnknown: {
1784 LinkUnknown*l = (LinkUnknown*)action;
1789 msg("<error> Unknown link type!");
1794 if(!s) s = strdup("-?-");
1796 msg("<trace> drawlink s=%s", s);
1798 if(!gfxglobals->linkinfo && (page || s))
1800 msg("<notice> File contains links");
1801 gfxglobals->linkinfo = 1;
1807 for(t=1;t<=this->num_pages;t++) {
1808 if(this->page2page[t]==page) {
1818 sprintf(buf, "page%d", lpage);
1819 device->drawlink(device, points, buf);
1823 device->drawlink(device, points, s);
1826 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1830 void GFXOutputDev::saveState(GfxState *state) {
1831 dbg("saveState %08x", state); dbgindent+=2;
1833 msg("<trace> saveState %08x", state);
1836 msg("<fatal> Too many nested states in pdf.");
1840 states[statepos].state = state;
1841 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1842 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1843 states[statepos].clipping = 0;
1844 states[statepos].olddevice = 0;
1845 states[statepos].clipbbox = states[statepos-1].clipbbox;
1847 states[statepos].dashPattern = states[statepos-1].dashPattern;
1848 states[statepos].dashStart = states[statepos-1].dashStart;
1849 states[statepos].dashLength = states[statepos-1].dashLength;
1852 void GFXOutputDev::restoreState(GfxState *state) {
1853 dbgindent-=2; dbg("restoreState %08x", state);
1856 msg("<fatal> Invalid restoreState");
1859 msg("<trace> restoreState %08x%s%s", state,
1860 states[statepos].softmask?" (end softmask)":"",
1861 states[statepos].clipping?" (end clipping)":"");
1862 if(states[statepos].softmask) {
1863 clearSoftMask(state);
1866 if(states[statepos].dashPattern) {
1867 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1868 free(states[statepos].dashPattern);
1869 states[statepos].dashPattern = 0;
1875 while(states[statepos].clipping) {
1876 device->endclip(device);
1877 states[statepos].clipping--;
1879 if(states[statepos].state!=state) {
1880 msg("<fatal> bad state nesting");
1883 for(t=0;t<=statepos;t++) {
1884 printf("%08x ", states[t].state);
1890 states[statepos].state=0;
1894 void GFXOutputDev::updateLineDash(GfxState *state)
1896 if(states[statepos].dashPattern &&
1897 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1898 free(states[statepos].dashPattern);
1899 states[statepos].dashPattern = 0;
1901 double *pattern = 0;
1904 state->getLineDash(&pattern, &dashLength, &dashStart);
1905 msg("<debug> updateLineDash, %d dashes", dashLength);
1907 states[statepos].dashPattern = 0;
1908 states[statepos].dashLength = 0;
1910 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1911 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1912 states[statepos].dashPattern = p;
1913 states[statepos].dashLength = dashLength;
1914 states[statepos].dashStart = dashStart;
1918 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1920 this->page2page = page2page;
1921 this->num_pages = num_pages;
1924 void GFXOutputDev::updateLineWidth(GfxState *state)
1926 double width = state->getTransformedLineWidth();
1929 void GFXOutputDev::updateLineCap(GfxState *state)
1931 int c = state->getLineCap();
1934 void GFXOutputDev::updateLineJoin(GfxState *state)
1936 int j = state->getLineJoin();
1939 void GFXOutputDev::updateFillColor(GfxState *state)
1942 double opaq = state->getFillOpacity();
1943 state->getFillRGB(&rgb);
1945 void GFXOutputDev::updateFillOpacity(GfxState *state)
1948 double opaq = state->getFillOpacity();
1949 state->getFillRGB(&rgb);
1950 dbg("update fillopaq %f", opaq);
1952 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1954 double opaq = state->getFillOpacity();
1955 dbg("update strokeopaq %f", opaq);
1957 void GFXOutputDev::updateFillOverprint(GfxState *state)
1959 double opaq = state->getFillOverprint();
1960 dbg("update filloverprint %f", opaq);
1962 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1964 double opaq = state->getStrokeOverprint();
1965 dbg("update strokeoverprint %f", opaq);
1967 void GFXOutputDev::updateTransfer(GfxState *state)
1969 dbg("update transfer");
1973 void GFXOutputDev::updateStrokeColor(GfxState *state)
1976 double opaq = state->getStrokeOpacity();
1977 state->getStrokeRGB(&rgb);
1980 void GFXOutputDev::updateFont(GfxState *state)
1982 GfxFont* gfxFont = state->getFont();
1986 char*id = getFontID(gfxFont);
1987 msg("<verbose> Updating font to %s", id);
1988 if(gfxFont->getType() == fontType3) {
1989 infofeature("Type3 fonts");
1990 if(!config_extrafontdata) {
1995 msg("<error> Internal Error: FontID is null");
1999 this->current_fontinfo = this->info->getFont(id);
2001 if(!this->current_fontinfo) {
2002 msg("<error> Internal Error: no fontinfo for font %s", id);
2005 if(!this->current_fontinfo->seen) {
2006 dumpFontInfo("<verbose>", gfxFont);
2009 current_gfxfont = this->current_fontinfo->getGfxFont();
2010 device->addfont(device, current_gfxfont);
2013 updateFontMatrix(state);
2016 #define SQR(x) ((x)*(x))
2018 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2020 if((newwidth<1 || newheight<1) ||
2021 (width<=newwidth || height<=newheight))
2023 unsigned char*newdata;
2025 newdata= (unsigned char*)malloc(newwidth*newheight);
2026 double fx = ((double)width)/newwidth;
2027 double fy = ((double)height)/newheight;
2029 int blocksize = (int)(8192/(fx*fy));
2030 int r = 8192*256/palettesize;
2031 for(x=0;x<newwidth;x++) {
2032 double ex = px + fx;
2033 int fromx = (int)px;
2035 int xweight1 = (int)((1-(px-fromx))*256);
2036 int xweight2 = (int)((ex-tox)*256);
2038 for(y=0;y<newheight;y++) {
2039 double ey = py + fy;
2040 int fromy = (int)py;
2042 int yweight1 = (int)((1-(py-fromy))*256);
2043 int yweight2 = (int)((ey-toy)*256);
2050 for(xx=fromx;xx<=tox;xx++)
2051 for(yy=fromy;yy<=toy;yy++) {
2052 int b = 1-data[width*yy+xx];
2054 if(xx==fromx) weight = (weight*xweight1)/256;
2055 if(xx==tox) weight = (weight*xweight2)/256;
2056 if(yy==fromy) weight = (weight*yweight1)/256;
2057 if(yy==toy) weight = (weight*yweight2)/256;
2060 //if(a) a=(palettesize-1)*r/blocksize;
2061 newdata[y*newwidth+x] = (a*blocksize)/r;
2069 #define IMAGE_TYPE_JPEG 0
2070 #define IMAGE_TYPE_LOSSLESS 1
2072 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2073 double x1,double y1,
2074 double x2,double y2,
2075 double x3,double y3,
2076 double x4,double y4, int type, int multiply)
2078 gfxcolor_t*newpic=0;
2080 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2081 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2083 gfxline_t p1,p2,p3,p4,p5;
2084 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2085 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2086 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2087 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2088 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2090 {p1.x = (int)(p1.x*20)/20.0;
2091 p1.y = (int)(p1.y*20)/20.0;
2092 p2.x = (int)(p2.x*20)/20.0;
2093 p2.y = (int)(p2.y*20)/20.0;
2094 p3.x = (int)(p3.x*20)/20.0;
2095 p3.y = (int)(p3.y*20)/20.0;
2096 p4.x = (int)(p4.x*20)/20.0;
2097 p4.y = (int)(p4.y*20)/20.0;
2098 p5.x = (int)(p5.x*20)/20.0;
2099 p5.y = (int)(p5.y*20)/20.0;
2103 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2104 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2106 m.tx = p1.x - 0.5*multiply;
2107 m.ty = p1.y - 0.5*multiply;
2110 img.data = (gfxcolor_t*)data;
2114 if(type == IMAGE_TYPE_JPEG)
2115 /* TODO: pass image_dpi to device instead */
2116 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2119 dev->fillbitmap(dev, &p1, &img, &m, 0);
2122 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2123 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2125 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2128 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2129 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2131 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2135 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2136 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2137 GBool inlineImg, int mask, int*maskColors,
2138 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2140 /* the code in this function is *old*. It's not pretty, but it works. */
2142 double x1,y1,x2,y2,x3,y3,x4,y4;
2143 ImageStream *imgStr;
2148 unsigned char* maskbitmap = 0;
2151 ncomps = colorMap->getNumPixelComps();
2152 bits = colorMap->getBits();
2157 unsigned char buf[8];
2158 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2160 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2161 imgMaskStr->reset();
2162 unsigned char pal[256];
2163 int n = 1 << colorMap->getBits();
2168 maskColorMap->getGray(pixBuf, &gray);
2169 pal[t] = colToByte(gray);
2171 for (y = 0; y < maskHeight; y++) {
2172 for (x = 0; x < maskWidth; x++) {
2173 imgMaskStr->getPixel(buf);
2174 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2179 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2180 imgMaskStr->reset();
2181 for (y = 0; y < maskHeight; y++) {
2182 for (x = 0; x < maskWidth; x++) {
2183 imgMaskStr->getPixel(buf);
2185 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2193 imgStr = new ImageStream(str, width, ncomps,bits);
2196 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2198 msg("<verbose> Ignoring %d by %d image", width, height);
2199 unsigned char buf[8];
2201 for (y = 0; y < height; ++y)
2202 for (x = 0; x < width; ++x) {
2203 imgStr->getPixel(buf);
2211 this->transformXY(state, 0, 1, &x1, &y1);
2212 this->transformXY(state, 0, 0, &x2, &y2);
2213 this->transformXY(state, 1, 0, &x3, &y3);
2214 this->transformXY(state, 1, 1, &x4, &y4);
2217 /* as type 3 bitmaps are antialized, we need to place them
2218 at integer coordinates, otherwise flash player's antializing
2219 will kick in and make everything blurry */
2220 x1 = (int)(x1);y1 = (int)(y1);
2221 x2 = (int)(x2);y2 = (int)(y2);
2222 x3 = (int)(x3);y3 = (int)(y3);
2223 x4 = (int)(x4);y4 = (int)(y4);
2226 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2228 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2229 gfxglobals->pbminfo = 1;
2232 msg("<verbose> drawing %d by %d masked picture", width, height);
2234 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2235 msg("<notice> File contains jpeg pictures");
2236 gfxglobals->jpeginfo = 1;
2240 unsigned char buf[8];
2242 unsigned char*pic = new unsigned char[width*height];
2243 gfxcolor_t pal[256];
2245 state->getFillRGB(&rgb);
2247 memset(pal,255,sizeof(pal));
2248 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2249 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2250 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2251 pal[0].a = 255; pal[1].a = 0;
2254 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2255 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2256 for (y = 0; y < height; ++y)
2257 for (x = 0; x < width; ++x)
2259 imgStr->getPixel(buf);
2262 pic[width*y+x] = buf[0];
2266 unsigned char*pic2 = 0;
2269 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2278 height = realheight;
2282 /* make a black/white palette */
2284 float r = 255./(float)(numpalette-1);
2286 for(t=0;t<numpalette;t++) {
2287 pal[t].r = colToByte(rgb.r);
2288 pal[t].g = colToByte(rgb.g);
2289 pal[t].b = colToByte(rgb.b);
2290 pal[t].a = (unsigned char)(t*r);
2295 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2296 for (y = 0; y < height; ++y) {
2297 for (x = 0; x < width; ++x) {
2298 pic2[width*y+x] = pal[pic[y*width+x]];
2301 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2305 if(maskbitmap) free(maskbitmap);
2311 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2312 gfxcolor_t*pic=new gfxcolor_t[width*height];
2313 for (y = 0; y < height; ++y) {
2314 for (x = 0; x < width; ++x) {
2315 imgStr->getPixel(pixBuf);
2316 colorMap->getRGB(pixBuf, &rgb);
2317 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2318 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2319 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2320 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2322 int x1 = x*maskWidth/width;
2323 int y1 = y*maskHeight/height;
2324 int x2 = (x+1)*maskWidth/width;
2325 int y2 = (y+1)*maskHeight/height;
2327 unsigned int alpha=0;
2328 unsigned int count=0;
2329 for(xx=x1;xx<x2;xx++)
2330 for(yy=y1;yy<y2;yy++) {
2331 alpha += maskbitmap[yy*maskWidth+xx];
2335 pic[width*y+x].a = alpha / count;
2337 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2342 if(str->getKind()==strDCT)
2343 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2345 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2348 if(maskbitmap) free(maskbitmap);
2351 gfxcolor_t*pic=new gfxcolor_t[width*height];
2352 gfxcolor_t pal[256];
2353 int n = 1 << colorMap->getBits();
2355 for(t=0;t<256;t++) {
2357 colorMap->getRGB(pixBuf, &rgb);
2359 {/*if(maskColors && *maskColors==t) {
2360 msg("<notice> Color %d is transparent", t);
2361 if (imgData->maskColors) {
2363 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2364 if (pix[i] < imgData->maskColors[2*i] ||
2365 pix[i] > imgData->maskColors[2*i+1]) {
2380 pal[t].r = (unsigned char)(colToByte(rgb.r));
2381 pal[t].g = (unsigned char)(colToByte(rgb.g));
2382 pal[t].b = (unsigned char)(colToByte(rgb.b));
2383 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2386 for (y = 0; y < height; ++y) {
2387 for (x = 0; x < width; ++x) {
2388 imgStr->getPixel(pixBuf);
2389 pic[width*y+x] = pal[pixBuf[0]];
2393 if(maskWidth < width && maskHeight < height) {
2394 for(y = 0; y < height; y++) {
2395 for (x = 0; x < width; x++) {
2396 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2400 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2401 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2402 double dx = width / maskWidth;
2403 double dy = height / maskHeight;
2405 for(y = 0; y < maskHeight; y++) {
2407 for (x = 0; x < maskWidth; x++) {
2408 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2409 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2417 height = maskHeight;
2420 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2424 if(maskbitmap) free(maskbitmap);
2429 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2430 int width, int height, GBool invert,
2433 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2434 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2435 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2438 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2439 int width, int height, GfxImageColorMap *colorMap,
2440 int *maskColors, GBool inlineImg)
2442 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2443 colorMap?"colorMap":"no colorMap",
2444 maskColors?"maskColors":"no maskColors",
2446 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2447 colorMap?"colorMap":"no colorMap",
2448 maskColors?"maskColors":"no maskColors",
2451 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2452 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2453 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2456 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2457 int width, int height,
2458 GfxImageColorMap *colorMap,
2459 Stream *maskStr, int maskWidth, int maskHeight,
2462 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2463 colorMap?"colorMap":"no colorMap",
2464 maskWidth, maskHeight);
2465 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2466 colorMap?"colorMap":"no colorMap",
2467 maskWidth, maskHeight);
2469 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2470 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2471 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2474 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2475 int width, int height,
2476 GfxImageColorMap *colorMap,
2478 int maskWidth, int maskHeight,
2479 GfxImageColorMap *maskColorMap)
2481 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2482 colorMap?"colorMap":"no colorMap",
2483 maskWidth, maskHeight);
2484 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2485 colorMap?"colorMap":"no colorMap",
2486 maskWidth, maskHeight);
2488 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2489 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2490 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2493 void GFXOutputDev::stroke(GfxState *state)
2497 GfxPath * path = state->getPath();
2498 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2499 strokeGfxline(state, line, 0);
2503 void GFXOutputDev::fill(GfxState *state)
2505 gfxcolor_t col = getFillColor(state);
2506 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2508 GfxPath * path = state->getPath();
2509 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2510 if(!config_disable_polygon_conversion) {
2511 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2515 fillGfxLine(state, line, 0);
2519 void GFXOutputDev::eoFill(GfxState *state)
2521 gfxcolor_t col = getFillColor(state);
2522 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2524 GfxPath * path = state->getPath();
2525 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2526 fillGfxLine(state, line, 1);
2531 static const char* dirseparator()
2540 void addGlobalFont(const char*filename)
2542 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2543 memset(f, 0, sizeof(fontfile_t));
2544 f->filename = filename;
2545 int len = strlen(filename);
2546 char*r1 = strrchr((char*)filename, '/');
2547 char*r2 = strrchr((char*)filename, '\\');
2555 msg("<verbose> Adding font \"%s\".", filename);
2556 if(global_fonts_next) {
2557 global_fonts_next->next = f;
2558 global_fonts_next = global_fonts_next->next;
2560 global_fonts_next = global_fonts = f;
2564 void addGlobalLanguageDir(const char*dir)
2566 msg("<notice> Adding %s to language pack directories", dir);
2569 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2570 strcpy(config_file, dir);
2571 strcat(config_file, dirseparator());
2572 strcat(config_file, "add-to-xpdfrc");
2574 fi = fopen(config_file, "rb");
2576 msg("<error> Could not open %s", config_file);
2579 globalParams->parseFile(new GString(config_file), fi);
2583 void addGlobalFontDir(const char*dirname)
2585 #ifdef HAVE_DIRENT_H
2586 DIR*dir = opendir(dirname);
2588 msg("<warning> Couldn't open directory %s", dirname);
2594 ent = readdir (dir);
2598 char*name = ent->d_name;
2604 if(!strncasecmp(&name[l-4], ".pfa", 4))
2606 if(!strncasecmp(&name[l-4], ".pfb", 4))
2608 if(!strncasecmp(&name[l-4], ".ttf", 4))
2611 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2612 strcpy(fontname, dirname);
2613 strcat(fontname, dirseparator());
2614 strcat(fontname, name);
2615 addGlobalFont(fontname);
2619 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2622 msg("<warning> No dirent.h");
2626 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2627 GfxColorSpace *blendingColorSpace,
2628 GBool isolated, GBool knockout,
2631 const char*colormodename = "";
2633 if(blendingColorSpace) {
2634 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2636 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);
2637 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);
2639 //states[statepos].createsoftmask |= forSoftMask;
2640 states[statepos].createsoftmask = forSoftMask;
2641 states[statepos].transparencygroup = !forSoftMask;
2642 states[statepos].isolated = isolated;
2644 states[statepos].olddevice = this->device;
2645 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2646 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2648 gfxdevice_record_init(this->device);
2650 /*if(!forSoftMask) { ////???
2651 state->setFillOpacity(0.0);
2656 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2659 gfxdevice_t*r = this->device;
2661 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2663 this->device = states[statepos].olddevice;
2665 msg("<error> Invalid state nesting");
2667 states[statepos].olddevice = 0;
2669 gfxresult_t*recording = r->finish(r);
2671 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2672 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2674 if(states[statepos].createsoftmask) {
2675 states[statepos-1].softmaskrecording = recording;
2677 states[statepos-1].grouprecording = recording;
2680 states[statepos].createsoftmask = 0;
2681 states[statepos].transparencygroup = 0;
2685 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2687 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2688 "colordodge","colorburn","hardlight","softlight","difference",
2689 "exclusion","hue","saturation","color","luminosity"};
2691 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2692 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2694 if(state->getBlendMode() == gfxBlendNormal)
2695 infofeature("transparency groups");
2698 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2699 warnfeature(buffer, 0);
2702 gfxresult_t*grouprecording = states[statepos].grouprecording;
2704 int blendmode = state->getBlendMode();
2705 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2706 int alpha = (int)(state->getFillOpacity()*255);
2707 if(blendmode == gfxBlendMultiply && alpha>200)
2710 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2711 gfxdevice_ops_init(&ops, this->device, alpha);
2712 gfxresult_record_replay(grouprecording, &ops);
2715 grouprecording->destroy(grouprecording);
2717 states[statepos].grouprecording = 0;
2720 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2722 if(states[statepos].softmask) {
2723 /* shouldn't happen, but *does* happen */
2724 clearSoftMask(state);
2727 /* alpha = 1: retrieve mask values from alpha layer
2728 alpha = 0: retrieve mask values from luminance */
2730 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2731 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2732 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2733 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2735 infofeature("soft masks");
2737 warnfeature("soft masks from alpha channel",0);
2739 if(states[statepos].olddevice) {
2740 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2743 states[statepos].olddevice = this->device;
2744 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2745 gfxdevice_record_init(this->device);
2747 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2749 states[statepos].softmask = 1;
2750 states[statepos].softmask_alpha = alpha;
2753 static inline Guchar div255(int x) {
2754 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2757 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2759 if(c < min) c = min;
2760 if(c > max) c = max;
2764 void GFXOutputDev::clearSoftMask(GfxState *state)
2766 if(!states[statepos].softmask)
2768 states[statepos].softmask = 0;
2769 dbg("clearSoftMask statepos=%d", statepos);
2770 msg("<verbose> clearSoftMask statepos=%d", statepos);
2772 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2773 msg("<error> Error in softmask/tgroup ordering");
2777 gfxresult_t*mask = states[statepos].softmaskrecording;
2778 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2779 this->device = states[statepos].olddevice;
2781 /* get outline of all objects below the soft mask */
2782 gfxdevice_t uniondev;
2783 gfxdevice_union_init(&uniondev, 0);
2784 gfxresult_record_replay(below, &uniondev);
2785 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2786 uniondev.finish(&uniondev);
2787 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2788 gfxline_free(belowoutline);belowoutline=0;
2790 this->device->startclip(this->device, belowoutline);
2791 gfxresult_record_replay(below, this->device);
2792 gfxresult_record_replay(mask, this->device);
2793 this->device->endclip(this->device);
2796 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2797 if(width<=0 || height<=0)
2800 gfxdevice_t belowrender;
2801 gfxdevice_render_init(&belowrender);
2802 if(states[statepos+1].isolated) {
2803 belowrender.setparameter(&belowrender, "fillwhite", "1");
2805 belowrender.setparameter(&belowrender, "antialize", "2");
2806 belowrender.startpage(&belowrender, width, height);
2807 gfxresult_record_replay(below, &belowrender);
2808 belowrender.endpage(&belowrender);
2809 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2810 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2811 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2813 gfxdevice_t maskrender;
2814 gfxdevice_render_init(&maskrender);
2815 maskrender.startpage(&maskrender, width, height);
2816 gfxresult_record_replay(mask, &maskrender);
2817 maskrender.endpage(&maskrender);
2818 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2819 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2821 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2822 msg("<fatal> Internal error in mask drawing");
2827 for(y=0;y<height;y++) {
2828 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2829 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2830 for(x=0;x<width;x++) {
2832 if(states[statepos].softmask_alpha) {
2835 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2838 l2->a = div255(alpha*l2->a);
2840 /* DON'T premultiply alpha- this is done by fillbitmap,
2841 depending on the output device */
2842 //l2->r = div255(alpha*l2->r);
2843 //l2->g = div255(alpha*l2->g);
2844 //l2->b = div255(alpha*l2->b);
2850 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2853 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2854 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2856 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2858 mask->destroy(mask);
2859 below->destroy(below);
2860 maskresult->destroy(maskresult);
2861 belowresult->destroy(belowresult);
2862 states[statepos].softmaskrecording = 0;
2867 // public: ~MemCheck()
2869 // delete globalParams;globalParams=0;
2870 // Object::memCheck(stderr);
2871 // gMemReport(stderr);