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);
422 msg("<debug> Adding font %s to fontconfig", fd->filename);
426 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
427 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
428 if(!set || !set->nfont) {
429 msg("<debug> FontConfig has zero fonts. Disabling.");
430 config_use_fontconfig = 0;
434 if(getLogLevel() >= LOGLEVEL_TRACE) {
438 for(t=0;t<set->nfont;t++) {
439 char*fcfamily=0,*fcstyle=0,*filename=0;
440 FcBool scalable=FcFalse, outline=FcFalse;
441 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
442 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
443 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
444 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
445 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
446 if(scalable && outline) {
447 msg("<trace> %s (%s) -> %s", fcfamily, fcstyle, filename);
450 set = FcConfigGetFonts(config, FcSetApplication);
455 char*family = strdup(name);
457 char*dash = strchr(family, '-');
458 if(!dash) dash = strchr(family, ',');
460 FcPattern*pattern = 0;
464 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
465 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
467 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
468 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
472 FcConfigSubstitute(0, pattern, FcMatchPattern);
473 FcDefaultSubstitute(pattern);
475 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
478 for(t=0;t<set->nfont;t++) {
479 FcPattern*match = set->fonts[t];
480 //FcPattern*match = FcFontMatch(0, pattern, &result);
481 if(fc_ismatch(match, family, style)) {
483 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
484 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
487 //FcPatternDestroy(match);
488 msg("<debug> fontconfig: returning filename %s", filename);
490 FcPatternDestroy(pattern);
491 FcFontSetDestroy(set);
492 return filename?strdup(filename):0;
497 FcPatternDestroy(pattern);
498 FcFontSetDestroy(set);
505 static DisplayFontParamKind detectFontType(const char*filename)
507 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
508 return displayFontTT;
509 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
510 return displayFontT1;
511 return displayFontTT;
514 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
516 msg("<verbose> looking for font %s", fontName->getCString());
518 char*name = fontName->getCString();
520 /* see if it is a pdf standard font */
522 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
523 if(!strcmp(name, pdf2t1map[t].pdffont)) {
524 if(!pdf2t1map[t].fullfilename) {
525 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
526 if(!pdf2t1map[t].fullfilename) {
527 msg("<error> Couldn't save default font- is the Temp Directory writable?");
529 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
532 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
533 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
538 int bestlen = 0x7fffffff;
539 const char*bestfilename = 0;
541 #ifndef HAVE_FONTCONFIG
542 /* if we don't have fontconfig, try a simple filename-comparison approach */
543 fontfile_t*f = global_fonts;
545 if(strstr(f->filename, name)) {
546 if(f->len < bestlen) {
548 bestfilename = f->filename;
555 /* if we didn't find anything up to now, try looking for the
556 font via fontconfig */
559 filename = fontconfig_searchForFont(name);
561 filename = strdup(bestfilename);
565 msg("<verbose> Font %s maps to %s\n", name, filename);
566 DisplayFontParamKind kind = detectFontType(filename);
567 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
568 if(kind == displayFontTT) {
569 dfp->tt.fileName = new GString(filename);
571 dfp->t1.fileName = new GString(filename);
576 msg("<verbose> Font %s not found\n", name);
577 return GlobalParams::getDisplayFont(fontName);
581 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
584 gfxglobals = new GFXOutputGlobals();
588 this->xref = doc->getXRef();
590 this->type3active = 0;
593 this->user_movex = 0;
594 this->user_movey = 0;
597 this->user_clipx1 = 0;
598 this->user_clipy1 = 0;
599 this->user_clipx2 = 0;
600 this->user_clipy2 = 0;
601 this->current_gfxfont = 0;
602 this->current_fontinfo = 0;
603 this->current_text_stroke = 0;
604 this->current_text_clip = 0;
605 this->outer_clip_box = 0;
606 this->config_bigchar=0;
607 this->config_convertgradients=1;
608 this->config_break_on_warning=0;
609 this->config_remapunicode=0;
610 this->config_transparent=0;
611 this->config_extrafontdata = 0;
612 this->config_drawonlyshapes = 0;
613 this->config_disable_polygon_conversion = 0;
614 this->config_multiply = 1;
618 memset(states, 0, sizeof(states));
621 void GFXOutputDev::setParameter(const char*key, const char*value)
623 if(!strcmp(key,"breakonwarning")) {
624 this->config_break_on_warning = atoi(value);
625 } else if(!strcmp(key,"remapunicode")) {
626 this->config_remapunicode = atoi(value);
627 } else if(!strcmp(key,"transparent")) {
628 this->config_transparent = atoi(value);
629 } else if(!strcmp(key,"drawonlyshapes")) {
630 this->config_drawonlyshapes = atoi(value);
631 } else if(!strcmp(key,"extrafontdata")) {
632 this->config_extrafontdata = atoi(value);
633 } else if(!strcmp(key,"convertgradients")) {
634 this->config_convertgradients = atoi(value);
635 } else if(!strcmp(key,"multiply")) {
636 this->config_multiply = atoi(value);
637 if(this->config_multiply<1)
638 this->config_multiply=1;
639 } else if(!strcmp(key,"disable_polygon_conversion")) {
640 this->config_disable_polygon_conversion = atoi(value);
644 void GFXOutputDev::setDevice(gfxdevice_t*dev)
649 void GFXOutputDev::setMove(int x,int y)
651 this->user_movex = x;
652 this->user_movey = y;
655 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
657 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
658 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
660 this->user_clipx1 = x1;
661 this->user_clipy1 = y1;
662 this->user_clipx2 = x2;
663 this->user_clipy2 = y2;
666 static char*getFontName(GfxFont*font)
669 GString*gstr = font->getName();
670 char* fname = gstr==0?0:gstr->getCString();
674 sprintf(buf, "UFONT%d", r->num);
675 fontid = strdup(buf);
677 fontid = strdup(fname);
681 char* plus = strchr(fontid, '+');
682 if(plus && plus < &fontid[strlen(fontid)-1]) {
683 fontname = strdup(plus+1);
685 fontname = strdup(fontid);
691 static void dumpFontInfo(const char*loglevel, GfxFont*font);
692 static int lastdumps[1024];
693 static int lastdumppos = 0;
698 static void showFontError(GfxFont*font, int nr)
702 for(t=0;t<lastdumppos;t++)
703 if(lastdumps[t] == r->num)
707 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
708 lastdumps[lastdumppos++] = r->num;
710 msg("<warning> The following font caused problems:");
712 msg("<warning> The following font caused problems (substituting):");
714 msg("<warning> The following Type 3 Font will be rendered as graphics:");
715 dumpFontInfo("<warning>", font);
718 static void dumpFontInfo(const char*loglevel, GfxFont*font)
720 char* id = getFontID(font);
721 char* name = getFontName(font);
722 Ref* r=font->getID();
723 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
725 GString*gstr = font->getTag();
727 msg("%s| Tag: %s", loglevel, id);
729 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
731 GfxFontType type=font->getType();
733 case fontUnknownType:
734 msg("%s| Type: unknown",loglevel);
737 msg("%s| Type: 1",loglevel);
740 msg("%s| Type: 1C",loglevel);
743 msg("%s| Type: 3",loglevel);
746 msg("%s| Type: TrueType",loglevel);
749 msg("%s| Type: CIDType0",loglevel);
752 msg("%s| Type: CIDType0C",loglevel);
755 msg("%s| Type: CIDType2",loglevel);
760 GBool embedded = font->getEmbeddedFontID(&embRef);
762 if(font->getEmbeddedFontName()) {
763 embeddedName = font->getEmbeddedFontName()->getCString();
766 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
768 gstr = font->getExtFontFile();
770 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
772 // Get font descriptor flags.
773 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
774 if(font->isSerif()) msg("%s| is serif", loglevel);
775 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
776 if(font->isItalic()) msg("%s| is italic", loglevel);
777 if(font->isBold()) msg("%s| is bold", loglevel);
783 //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");}
784 //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");}
786 void dump_outline(gfxline_t*line)
788 /*gfxbbox_t*r = gfxline_isrectangle(line);
790 printf("is not a rectangle\n");
792 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
796 if(line->type == gfx_moveTo) {
797 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
798 } else if(line->type == gfx_lineTo) {
799 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
800 } else if(line->type == gfx_splineTo) {
801 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
807 void gfxPath_dump(GfxPath*path)
809 int num = path->getNumSubpaths();
812 for(t = 0; t < num; t++) {
813 GfxSubpath *subpath = path->getSubpath(t);
814 int subnum = subpath->getNumPoints();
816 for(s=0;s<subnum;s++) {
817 double x=subpath->getX(s);
818 double y=subpath->getY(s);
819 if(s==0 && !subpath->getCurve(s)) {
820 printf("M %f %f\n", x, y);
821 } else if(s==0 && subpath->getCurve(s)) {
822 printf("E %f %f\n", x, y);
823 } else if(subpath->getCurve(s)) {
824 printf("C %f %f\n", x, y);
826 printf("T %f %f\n", x, y);
832 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
834 int num = path->getNumSubpaths();
837 double lastx=0,lasty=0,posx=0,posy=0;
840 msg("<warning> empty path");
844 gfxdrawer_target_gfxline(&draw);
846 for(t = 0; t < num; t++) {
847 GfxSubpath *subpath = path->getSubpath(t);
848 int subnum = subpath->getNumPoints();
849 double bx=0,by=0,cx=0,cy=0;
851 for(s=0;s<subnum;s++) {
854 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
857 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
858 draw.lineTo(&draw, lastx, lasty);
860 draw.moveTo(&draw, x,y);
865 } else if(subpath->getCurve(s) && cpos==0) {
869 } else if(subpath->getCurve(s) && cpos==1) {
877 draw.lineTo(&draw, x,y);
879 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
886 /* fix non-closed lines */
887 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
888 draw.lineTo(&draw, lastx, lasty);
890 gfxline_t*result = (gfxline_t*)draw.result(&draw);
892 gfxline_optimize(result);
897 GBool GFXOutputDev::useTilingPatternFill()
899 infofeature("tiled patterns");
900 // if(config_convertgradients)
904 GBool GFXOutputDev::useShadedFills()
906 infofeature("shaded fills");
907 if(config_convertgradients)
912 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
914 state->transform(x,y,nx,ny);
915 *nx += user_movex + clipmovex;
916 *ny += user_movey + clipmovey;
920 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
921 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
922 int paintType, Dict *resDict,
923 double *mat, double *bbox,
924 int x0, int y0, int x1, int y1,
925 double xStep, double yStep)
927 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
928 int paintType, Dict *resDict,
929 double *mat, double *bbox,
930 int x0, int y0, int x1, int y1,
931 double xStep, double yStep)
934 msg("<debug> tilingPatternFill");
935 infofeature("tiling pattern fills");
938 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
940 msg("<error> functionShadedFill not supported yet");
941 infofeature("function shaded fills");
944 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
948 colspace->getRGB(col, &rgb);
949 c.r = colToByte(rgb.r);
950 c.g = colToByte(rgb.g);
951 c.b = colToByte(rgb.b);
956 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
958 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
959 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
962 this->transformXY(state, x0,y0, &x0,&y0);
963 this->transformXY(state, x1,y1, &x1,&y1);
964 this->transformXY(state, x2,y2, &x2,&y2);
969 shading->getColor(0.0, &color0);
970 shading->getColor(0.5, &color1);
971 shading->getColor(1.0, &color2);
973 GfxColorSpace* colspace = shading->getColorSpace();
975 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
976 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
977 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
978 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
979 infofeature("radial shaded fills");
981 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
985 g[0].color = col2col(colspace, &color0);
986 g[1].color = col2col(colspace, &color1);
987 g[2].color = col2col(colspace, &color2);
992 gfxbbox_t b = states[statepos].clipbbox;
993 gfxline_t p1,p2,p3,p4,p5;
994 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
995 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
996 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
997 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
998 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1001 //m.m00 = (x3-x0); m.m10 = (x1-x0);
1002 //m.m01 = (y3-y0); m.m11 = (y1-y0);
1003 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1004 m.m00 = (x1-x0); m.m10 = (x2-x0);
1005 m.m01 = (y1-y0); m.m11 = (y2-y0);
1009 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1013 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1016 shading->getCoords(&x0,&y0,&x1,&y1);
1017 this->transformXY(state, x0,y0,&x0,&y0);
1018 this->transformXY(state, x1,y1,&x1,&y1);
1023 shading->getColor(0.0, &color0);
1024 shading->getColor(0.5, &color1);
1025 shading->getColor(1.0, &color2);
1027 GfxColorSpace* colspace = shading->getColorSpace();
1029 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1030 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1031 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1032 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1034 infofeature("axial shaded fills");
1036 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1040 g[0].color = col2col(colspace, &color0);
1041 g[1].color = col2col(colspace, &color1);
1042 g[2].color = col2col(colspace, &color2);
1047 double xMin,yMin,xMax,yMax;
1048 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1049 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1050 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1053 xMin = 1024; yMin = 1024;
1055 gfxbbox_t b = states[statepos].clipbbox;
1056 gfxline_t p1,p2,p3,p4,p5;
1057 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1058 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1059 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1060 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1061 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1063 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1064 the middle of the two control points */
1066 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1067 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1068 m.tx = (x0 + x1)/2 - 0.5;
1069 m.ty = (y0 + y1)/2 - 0.5;
1071 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1077 GBool GFXOutputDev::useDrawForm()
1079 infofeature("forms");
1082 void GFXOutputDev::drawForm(Ref id)
1084 msg("<error> drawForm not implemented");
1086 GBool GFXOutputDev::needNonText()
1090 void GFXOutputDev::endPage()
1092 msg("<verbose> endPage (GfxOutputDev)");
1093 if(outer_clip_box) {
1094 device->endclip(device);
1097 /* notice: we're not fully done yet with this page- there might still be
1098 a few calls to drawLink() yet to come */
1101 static inline double sqr(double x) {return x*x;}
1103 #define STROKE_FILL 1
1104 #define STROKE_CLIP 2
1105 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1107 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1108 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1109 double miterLimit = state->getMiterLimit();
1110 double width = state->getTransformedLineWidth();
1113 double opaq = state->getStrokeOpacity();
1115 state->getFillRGB(&rgb);
1117 state->getStrokeRGB(&rgb);
1119 col.r = colToByte(rgb.r);
1120 col.g = colToByte(rgb.g);
1121 col.b = colToByte(rgb.b);
1122 col.a = (unsigned char)(opaq*255);
1124 gfx_capType capType = gfx_capRound;
1125 if(lineCap == 0) capType = gfx_capButt;
1126 else if(lineCap == 1) capType = gfx_capRound;
1127 else if(lineCap == 2) capType = gfx_capSquare;
1128 else msg("<error> Invalid line cap type");
1130 gfx_joinType joinType = gfx_joinRound;
1131 if(lineJoin == 0) joinType = gfx_joinMiter;
1132 else if(lineJoin == 1) joinType = gfx_joinRound;
1133 else if(lineJoin == 2) joinType = gfx_joinBevel;
1134 else msg("<error> Invalid line join type");
1136 gfxline_t*line2 = 0;
1138 int dashLength = states[statepos].dashLength;
1139 double*dashPattern = states[statepos].dashPattern;
1140 double dashStart = states[statepos].dashStart;
1141 if(dashLength && dashPattern) {
1142 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1145 /* try to find out how much the transformation matrix would
1146 stretch the dashes, and factor that into the dash lengths.
1147 This is not the entirely correct approach- it would be
1148 better to first convert the path to an unscaled version,
1149 then apply dashing, and then transform the path using
1150 the current transformation matrix. However there are few
1151 PDFs which actually stretch a dashed path in a non-orthonormal
1153 double tx1, ty1, tx2, ty2, tx3, ty3;
1154 this->transformXY(state, 0, 0, &tx1, &ty1);
1155 this->transformXY(state, 0, 1, &tx2, &ty2);
1156 this->transformXY(state, 1, 0, &tx3, &ty3);
1157 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1158 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1160 warnfeature("non-ortogonally dashed strokes", 0);
1161 double f = (d1+d2)/2;
1163 msg("<trace> %d dashes", dashLength);
1164 msg("<trace> | phase: %f", dashStart);
1165 for(t=0;t<dashLength;t++) {
1166 dash[t] = (float)dashPattern[t] * f;
1170 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1172 dash[dashLength] = -1;
1173 if(getLogLevel() >= LOGLEVEL_TRACE) {
1177 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1181 msg("<trace> After dashing:");
1184 if(getLogLevel() >= LOGLEVEL_TRACE) {
1185 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1187 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1188 lineCap==0?"butt": (lineCap==1?"round":"square"),
1190 col.r,col.g,col.b,col.a
1195 if(flags&STROKE_FILL) {
1196 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1197 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1198 if(getLogLevel() >= LOGLEVEL_TRACE) {
1199 dump_outline(gfxline);
1202 msg("<warning> Empty polygon (resulting from stroked line)");
1204 if(flags&STROKE_CLIP) {
1205 device->startclip(device, gfxline);
1206 states[statepos].clipping++;
1208 device->fill(device, gfxline, &col);
1210 gfxline_free(gfxline);
1211 gfxpoly_destroy(poly);
1213 if(flags&STROKE_CLIP)
1214 msg("<error> Stroke&clip not supported at the same time");
1215 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1219 gfxline_free(line2);
1222 gfxcolor_t getFillColor(GfxState * state)
1225 double opaq = state->getFillOpacity();
1226 state->getFillRGB(&rgb);
1228 col.r = colToByte(rgb.r);
1229 col.g = colToByte(rgb.g);
1230 col.b = colToByte(rgb.b);
1231 col.a = (unsigned char)(opaq*255);
1235 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1237 gfxcolor_t col = getFillColor(state);
1239 if(getLogLevel() >= LOGLEVEL_TRACE) {
1240 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1243 device->fill(device, line, &col);
1246 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1248 if(getLogLevel() >= LOGLEVEL_TRACE) {
1249 msg("<trace> %sclip", evenodd?"eo":"");
1252 gfxbbox_t bbox = gfxline_getbbox(line);
1253 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1255 device->startclip(device, line);
1256 states[statepos].clipping++;
1259 void GFXOutputDev::clip(GfxState *state)
1261 GfxPath * path = state->getPath();
1262 msg("<trace> clip");
1263 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1264 if(!config_disable_polygon_conversion) {
1265 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1269 clipToGfxLine(state, line, 0);
1273 void GFXOutputDev::eoClip(GfxState *state)
1275 GfxPath * path = state->getPath();
1276 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1277 clipToGfxLine(state, line, 1);
1280 void GFXOutputDev::clipToStrokePath(GfxState *state)
1282 GfxPath * path = state->getPath();
1283 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1285 if(getLogLevel() >= LOGLEVEL_TRACE) {
1286 double width = state->getTransformedLineWidth();
1287 msg("<trace> cliptostrokepath width=%f", width);
1291 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1295 void GFXOutputDev::finish()
1297 if(outer_clip_box) {
1299 device->endclip(device);
1305 GFXOutputDev::~GFXOutputDev()
1309 GBool GFXOutputDev::upsideDown()
1313 GBool GFXOutputDev::useDrawChar()
1318 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1319 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1321 static char tmp_printstr[4096];
1322 char* makeStringPrintable(char*str)
1324 int len = strlen(str);
1331 for(t=0;t<len;t++) {
1336 tmp_printstr[t] = c;
1339 tmp_printstr[len++] = '.';
1340 tmp_printstr[len++] = '.';
1341 tmp_printstr[len++] = '.';
1343 tmp_printstr[len] = 0;
1344 return tmp_printstr;
1346 void GFXOutputDev::updateFontMatrix(GfxState*state)
1348 double* ctm = state->getCTM();
1349 double fontSize = state->getFontSize();
1350 double*textMat = state->getTextMat();
1352 /* taking the absolute value of horizScaling seems to be required for
1353 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1354 double hscale = fabs(state->getHorizScaling());
1356 // from xpdf-3.02/SplashOutputDev:updateFont
1357 double mm11 = textMat[0] * fontSize * hscale;
1358 double mm12 = textMat[1] * fontSize * hscale;
1359 double mm21 = textMat[2] * fontSize;
1360 double mm22 = textMat[3] * fontSize;
1362 // multiply with ctm, like state->getFontTransMat() does
1363 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1364 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1365 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1366 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1367 this->current_font_matrix.tx = 0;
1368 this->current_font_matrix.ty = 0;
1371 void GFXOutputDev::beginString(GfxState *state, GString *s)
1373 int render = state->getRender();
1374 if(current_text_stroke) {
1375 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1377 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1380 static gfxline_t* mkEmptyGfxShape(double x, double y)
1382 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1383 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1387 static char isValidUnicode(int c)
1389 if(c>=32 && c<0x2fffe)
1394 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1395 double dx, double dy,
1396 double originX, double originY,
1397 CharCode charid, int nBytes, Unicode *_u, int uLen)
1399 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1400 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1404 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1406 int render = state->getRender();
1407 gfxcolor_t col = getFillColor(state);
1409 // check for invisible text -- this is used by Acrobat Capture
1410 if (render == RENDER_INVISIBLE) {
1412 if(!config_extrafontdata)
1416 GfxFont*font = state->getFont();
1418 if(font->getType() == fontType3) {
1419 /* type 3 chars are passed as graphics */
1420 msg("<debug> type3 char at %f/%f", x, y);
1424 Unicode u = uLen?(_u[0]):0;
1426 gfxmatrix_t m = this->current_font_matrix;
1427 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1428 //m.tx += originX; m.ty += originY;
1430 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);
1432 if((render == RENDER_FILL && !config_drawonlyshapes) || render == RENDER_INVISIBLE) {
1433 int space = this->current_fontinfo->space_char;
1434 if(config_extrafontdata && space>=0 && m.m00 && !m.m01) {
1435 /* space char detection */
1436 if(last_char_gfxfont == current_gfxfont &&
1437 last_char_y == m.ty &&
1438 !last_char_was_space) {
1439 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1440 int space = this->current_fontinfo->space_char;
1441 if(m.tx - expected_x >= m.m00*64) {
1442 msg("<debug> There's a %f (%f) pixel gap between char %d and char %d, I'm inserting a space here",
1444 (m.tx-expected_x)/m.m00,
1445 last_char, glyphid);
1447 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1448 if(m2.tx < expected_x) m2.tx = expected_x;
1449 device->drawchar(device, current_gfxfont, space, &col, &m2);
1452 last_char_gfxfont = current_gfxfont;
1453 last_char = glyphid;
1456 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1458 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1460 msg("<debug> Drawing glyph %d as shape", charid);
1461 if(!gfxglobals->textmodeinfo) {
1462 msg("<notice> Some texts will be rendered as shape");
1463 gfxglobals->textmodeinfo = 1;
1465 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1466 gfxline_t*tglyph = gfxline_clone(glyph);
1467 gfxline_transform(tglyph, &m);
1468 if((render&3) != RENDER_INVISIBLE) {
1469 gfxline_t*add = gfxline_clone(tglyph);
1470 current_text_stroke = gfxline_append(current_text_stroke, add);
1472 if(render&RENDER_CLIP) {
1473 gfxline_t*add = gfxline_clone(tglyph);
1474 current_text_clip = gfxline_append(current_text_clip, add);
1475 if(!current_text_clip) {
1476 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1479 gfxline_free(tglyph);
1483 void GFXOutputDev::endString(GfxState *state)
1485 int render = state->getRender();
1486 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1488 if(current_text_stroke) {
1489 /* fillstroke and stroke text rendering objects we can process right
1490 now (as there may be texts of other rendering modes in this
1491 text object)- clipping objects have to wait until endTextObject,
1493 device->setparameter(device, "mark","TXT");
1494 if((render&3) == RENDER_FILL) {
1495 fillGfxLine(state, current_text_stroke, 0);
1496 gfxline_free(current_text_stroke);
1497 current_text_stroke = 0;
1498 } else if((render&3) == RENDER_FILLSTROKE) {
1499 fillGfxLine(state, current_text_stroke, 0);
1500 strokeGfxline(state, current_text_stroke,0);
1501 gfxline_free(current_text_stroke);
1502 current_text_stroke = 0;
1503 } else if((render&3) == RENDER_STROKE) {
1504 strokeGfxline(state, current_text_stroke,0);
1505 gfxline_free(current_text_stroke);
1506 current_text_stroke = 0;
1508 device->setparameter(device, "mark","");
1512 void GFXOutputDev::endTextObject(GfxState *state)
1514 int render = state->getRender();
1515 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1517 if(current_text_clip) {
1518 device->setparameter(device, "mark","TXT");
1519 clipToGfxLine(state, current_text_clip, 0);
1520 device->setparameter(device, "mark","");
1521 gfxline_free(current_text_clip);
1522 current_text_clip = 0;
1526 /* the logic seems to be as following:
1527 first, beginType3Char is called, with the charcode and the coordinates.
1528 if this function returns true, it already knew about the char and has now drawn it.
1529 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1530 called with some parameters.
1531 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1532 at the position first passed to beginType3Char). the char ends with endType3Char.
1534 The drawing operations between beginType3Char and endType3Char are somewhat different to
1535 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1536 color determines the color of a font)
1539 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1541 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1544 if(config_extrafontdata && current_fontinfo) {
1546 gfxmatrix_t m = this->current_font_matrix;
1547 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1548 m.m00*=INTERNAL_FONT_SIZE;
1549 m.m01*=INTERNAL_FONT_SIZE;
1550 m.m10*=INTERNAL_FONT_SIZE;
1551 m.m11*=INTERNAL_FONT_SIZE;
1553 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1554 msg("<error> Invalid charid %d for font", charid);
1557 gfxcolor_t col={0,0,0,0};
1558 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1559 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1563 /* the character itself is going to be passed using the draw functions */
1564 return gFalse; /* gTrue= is_in_cache? */
1567 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1569 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1572 void GFXOutputDev::endType3Char(GfxState *state)
1575 msg("<debug> endType3Char");
1578 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1580 this->currentpage = pageNum;
1582 int rot = doc->getPageRotate(1);
1583 gfxcolor_t white = {255,255,255,255};
1584 gfxcolor_t black = {255,0,0,0};
1586 gfxline_t clippath[5];
1588 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1589 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1590 Use CropBox, not MediaBox, as page size
1597 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1598 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1600 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1601 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1603 this->clipmovex = -(int)x1;
1604 this->clipmovey = -(int)y1;
1606 /* apply user clip box */
1607 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1608 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1609 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1610 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1611 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1612 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1614 x1 += this->clipmovex;
1615 y1 += this->clipmovey;
1616 x2 += this->clipmovex;
1617 y2 += this->clipmovey;
1620 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1622 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);
1624 msg("<verbose> page is rotated %d degrees", rot);
1626 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1627 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1628 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1629 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1630 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1631 device->startclip(device, clippath); outer_clip_box = 1;
1632 if(!config_transparent) {
1633 device->fill(device, clippath, &white);
1635 states[statepos].clipbbox.xmin = x1;
1636 states[statepos].clipbbox.ymin = x1;
1637 states[statepos].clipbbox.xmax = x2;
1638 states[statepos].clipbbox.ymax = y2;
1640 states[statepos].dashPattern = 0;
1641 states[statepos].dashLength = 0;
1642 states[statepos].dashStart = 0;
1644 this->last_char_gfxfont = 0;
1648 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1650 double x1, y1, x2, y2;
1651 gfxline_t points[5];
1654 msg("<debug> drawlink");
1656 link->getRect(&x1, &y1, &x2, &y2);
1657 cvtUserToDev(x1, y1, &x, &y);
1658 points[0].type = gfx_moveTo;
1659 points[0].x = points[4].x = x + user_movex + clipmovex;
1660 points[0].y = points[4].y = y + user_movey + clipmovey;
1661 points[0].next = &points[1];
1662 cvtUserToDev(x2, y1, &x, &y);
1663 points[1].type = gfx_lineTo;
1664 points[1].x = x + user_movex + clipmovex;
1665 points[1].y = y + user_movey + clipmovey;
1666 points[1].next = &points[2];
1667 cvtUserToDev(x2, y2, &x, &y);
1668 points[2].type = gfx_lineTo;
1669 points[2].x = x + user_movex + clipmovex;
1670 points[2].y = y + user_movey + clipmovey;
1671 points[2].next = &points[3];
1672 cvtUserToDev(x1, y2, &x, &y);
1673 points[3].type = gfx_lineTo;
1674 points[3].x = x + user_movex + clipmovex;
1675 points[3].y = y + user_movey + clipmovey;
1676 points[3].next = &points[4];
1677 cvtUserToDev(x1, y1, &x, &y);
1678 points[4].type = gfx_lineTo;
1679 points[4].x = x + user_movex + clipmovex;
1680 points[4].y = y + user_movey + clipmovey;
1683 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1684 points[0].x, points[0].y,
1685 points[1].x, points[1].y,
1686 points[2].x, points[2].y,
1687 points[3].x, points[3].y);
1689 if(getLogLevel() >= LOGLEVEL_TRACE) {
1690 dump_outline(points);
1693 LinkAction*action=link->getAction();
1696 const char*type = "-?-";
1699 msg("<trace> drawlink action=%d", action->getKind());
1700 switch(action->getKind())
1704 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1705 LinkDest *dest=NULL;
1706 if (ha->getDest()==NULL)
1707 dest=catalog->findDest(ha->getNamedDest());
1709 dest=ha->getDest()->copy();
1711 if (dest->isPageRef()){
1712 Ref pageref=dest->getPageRef();
1713 page=catalog->findPage(pageref.num,pageref.gen);
1715 else page=dest->getPageNum();
1716 sprintf(buf, "%d", page);
1724 LinkGoToR*l = (LinkGoToR*)action;
1725 GString*g = l->getFileName();
1727 s = strdup(g->getCString());
1729 /* if the GoToR link has no filename, then
1730 try to find a refernce in the *local*
1732 GString*g = l->getNamedDest();
1734 s = strdup(g->getCString());
1740 LinkNamed*l = (LinkNamed*)action;
1741 GString*name = l->getName();
1743 s = strdup(name->lowerCase()->getCString());
1744 named = name->getCString();
1747 if(strstr(s, "next") || strstr(s, "forward"))
1749 page = currentpage + 1;
1751 else if(strstr(s, "prev") || strstr(s, "back"))
1753 page = currentpage - 1;
1755 else if(strstr(s, "last") || strstr(s, "end"))
1757 if(this->page2page && this->num_pages) {
1758 page = this->page2page[this->num_pages-1];
1761 else if(strstr(s, "first") || strstr(s, "top"))
1769 case actionLaunch: {
1771 LinkLaunch*l = (LinkLaunch*)action;
1772 GString * str = new GString(l->getFileName());
1773 GString * params = l->getParams();
1775 str->append(params);
1776 s = strdup(str->getCString());
1783 LinkURI*l = (LinkURI*)action;
1784 GString*g = l->getURI();
1786 url = g->getCString();
1791 case actionUnknown: {
1793 LinkUnknown*l = (LinkUnknown*)action;
1798 msg("<error> Unknown link type!");
1803 if(!s) s = strdup("-?-");
1805 msg("<trace> drawlink s=%s", s);
1807 if(!gfxglobals->linkinfo && (page || s))
1809 msg("<notice> File contains links");
1810 gfxglobals->linkinfo = 1;
1816 for(t=1;t<=this->num_pages;t++) {
1817 if(this->page2page[t]==page) {
1827 sprintf(buf, "page%d", lpage);
1828 device->drawlink(device, points, buf);
1832 device->drawlink(device, points, s);
1835 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1839 void GFXOutputDev::saveState(GfxState *state) {
1840 dbg("saveState %08x", state); dbgindent+=2;
1842 msg("<trace> saveState %08x", state);
1845 msg("<fatal> Too many nested states in pdf.");
1849 states[statepos].state = state;
1850 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1851 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1852 states[statepos].clipping = 0;
1853 states[statepos].olddevice = 0;
1854 states[statepos].clipbbox = states[statepos-1].clipbbox;
1856 states[statepos].dashPattern = states[statepos-1].dashPattern;
1857 states[statepos].dashStart = states[statepos-1].dashStart;
1858 states[statepos].dashLength = states[statepos-1].dashLength;
1861 void GFXOutputDev::restoreState(GfxState *state) {
1862 dbgindent-=2; dbg("restoreState %08x", state);
1865 msg("<fatal> Invalid restoreState");
1868 msg("<trace> restoreState %08x%s%s", state,
1869 states[statepos].softmask?" (end softmask)":"",
1870 states[statepos].clipping?" (end clipping)":"");
1871 if(states[statepos].softmask) {
1872 clearSoftMask(state);
1875 if(states[statepos].dashPattern) {
1876 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1877 free(states[statepos].dashPattern);
1878 states[statepos].dashPattern = 0;
1884 while(states[statepos].clipping) {
1885 device->endclip(device);
1886 states[statepos].clipping--;
1888 if(states[statepos].state!=state) {
1889 msg("<fatal> bad state nesting");
1892 for(t=0;t<=statepos;t++) {
1893 printf("%08x ", states[t].state);
1899 states[statepos].state=0;
1903 void GFXOutputDev::updateLineDash(GfxState *state)
1905 if(states[statepos].dashPattern &&
1906 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1907 free(states[statepos].dashPattern);
1908 states[statepos].dashPattern = 0;
1910 double *pattern = 0;
1913 state->getLineDash(&pattern, &dashLength, &dashStart);
1914 msg("<debug> updateLineDash, %d dashes", dashLength);
1916 states[statepos].dashPattern = 0;
1917 states[statepos].dashLength = 0;
1919 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1920 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1921 states[statepos].dashPattern = p;
1922 states[statepos].dashLength = dashLength;
1923 states[statepos].dashStart = dashStart;
1927 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1929 this->page2page = page2page;
1930 this->num_pages = num_pages;
1933 void GFXOutputDev::updateLineWidth(GfxState *state)
1935 double width = state->getTransformedLineWidth();
1938 void GFXOutputDev::updateLineCap(GfxState *state)
1940 int c = state->getLineCap();
1943 void GFXOutputDev::updateLineJoin(GfxState *state)
1945 int j = state->getLineJoin();
1948 void GFXOutputDev::updateFillColor(GfxState *state)
1951 double opaq = state->getFillOpacity();
1952 state->getFillRGB(&rgb);
1954 void GFXOutputDev::updateFillOpacity(GfxState *state)
1957 double opaq = state->getFillOpacity();
1958 state->getFillRGB(&rgb);
1959 dbg("update fillopaq %f", opaq);
1961 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1963 double opaq = state->getFillOpacity();
1964 dbg("update strokeopaq %f", opaq);
1966 void GFXOutputDev::updateFillOverprint(GfxState *state)
1968 double opaq = state->getFillOverprint();
1969 dbg("update filloverprint %f", opaq);
1971 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1973 double opaq = state->getStrokeOverprint();
1974 dbg("update strokeoverprint %f", opaq);
1976 void GFXOutputDev::updateTransfer(GfxState *state)
1978 dbg("update transfer");
1982 void GFXOutputDev::updateStrokeColor(GfxState *state)
1985 double opaq = state->getStrokeOpacity();
1986 state->getStrokeRGB(&rgb);
1989 void GFXOutputDev::updateFont(GfxState *state)
1991 GfxFont* gfxFont = state->getFont();
1995 char*id = getFontID(gfxFont);
1996 msg("<verbose> Updating font to %s", id);
1997 if(gfxFont->getType() == fontType3) {
1998 infofeature("Type3 fonts");
1999 if(!config_extrafontdata) {
2004 msg("<error> Internal Error: FontID is null");
2008 this->current_fontinfo = this->info->getFont(id);
2010 if(!this->current_fontinfo) {
2011 msg("<error> Internal Error: no fontinfo for font %s", id);
2014 if(!this->current_fontinfo->seen) {
2015 dumpFontInfo("<verbose>", gfxFont);
2018 current_gfxfont = this->current_fontinfo->getGfxFont();
2019 device->addfont(device, current_gfxfont);
2022 updateFontMatrix(state);
2025 #define SQR(x) ((x)*(x))
2027 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2029 if((newwidth<1 || newheight<1) ||
2030 (width<=newwidth || height<=newheight))
2032 unsigned char*newdata;
2034 newdata= (unsigned char*)malloc(newwidth*newheight);
2035 double fx = ((double)width)/newwidth;
2036 double fy = ((double)height)/newheight;
2038 int blocksize = (int)(8192/(fx*fy));
2039 int r = 8192*256/palettesize;
2040 for(x=0;x<newwidth;x++) {
2041 double ex = px + fx;
2042 int fromx = (int)px;
2044 int xweight1 = (int)((1-(px-fromx))*256);
2045 int xweight2 = (int)((ex-tox)*256);
2047 for(y=0;y<newheight;y++) {
2048 double ey = py + fy;
2049 int fromy = (int)py;
2051 int yweight1 = (int)((1-(py-fromy))*256);
2052 int yweight2 = (int)((ey-toy)*256);
2059 for(xx=fromx;xx<=tox;xx++)
2060 for(yy=fromy;yy<=toy;yy++) {
2061 int b = 1-data[width*yy+xx];
2063 if(xx==fromx) weight = (weight*xweight1)/256;
2064 if(xx==tox) weight = (weight*xweight2)/256;
2065 if(yy==fromy) weight = (weight*yweight1)/256;
2066 if(yy==toy) weight = (weight*yweight2)/256;
2069 //if(a) a=(palettesize-1)*r/blocksize;
2070 newdata[y*newwidth+x] = (a*blocksize)/r;
2078 #define IMAGE_TYPE_JPEG 0
2079 #define IMAGE_TYPE_LOSSLESS 1
2081 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2082 double x1,double y1,
2083 double x2,double y2,
2084 double x3,double y3,
2085 double x4,double y4, int type, int multiply)
2087 gfxcolor_t*newpic=0;
2089 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2090 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2092 gfxline_t p1,p2,p3,p4,p5;
2093 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2094 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2095 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2096 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2097 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2099 {p1.x = (int)(p1.x*20)/20.0;
2100 p1.y = (int)(p1.y*20)/20.0;
2101 p2.x = (int)(p2.x*20)/20.0;
2102 p2.y = (int)(p2.y*20)/20.0;
2103 p3.x = (int)(p3.x*20)/20.0;
2104 p3.y = (int)(p3.y*20)/20.0;
2105 p4.x = (int)(p4.x*20)/20.0;
2106 p4.y = (int)(p4.y*20)/20.0;
2107 p5.x = (int)(p5.x*20)/20.0;
2108 p5.y = (int)(p5.y*20)/20.0;
2112 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2113 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2115 m.tx = p1.x - 0.5*multiply;
2116 m.ty = p1.y - 0.5*multiply;
2119 img.data = (gfxcolor_t*)data;
2123 if(type == IMAGE_TYPE_JPEG)
2124 /* TODO: pass image_dpi to device instead */
2125 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2128 dev->fillbitmap(dev, &p1, &img, &m, 0);
2131 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2132 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2134 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2137 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2138 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2140 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2144 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2145 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2146 GBool inlineImg, int mask, int*maskColors,
2147 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2149 /* the code in this function is *old*. It's not pretty, but it works. */
2151 double x1,y1,x2,y2,x3,y3,x4,y4;
2152 ImageStream *imgStr;
2157 unsigned char* maskbitmap = 0;
2160 ncomps = colorMap->getNumPixelComps();
2161 bits = colorMap->getBits();
2166 unsigned char buf[8];
2167 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2169 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2170 imgMaskStr->reset();
2171 unsigned char pal[256];
2172 int n = 1 << colorMap->getBits();
2177 maskColorMap->getGray(pixBuf, &gray);
2178 pal[t] = colToByte(gray);
2180 for (y = 0; y < maskHeight; y++) {
2181 for (x = 0; x < maskWidth; x++) {
2182 imgMaskStr->getPixel(buf);
2183 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2188 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2189 imgMaskStr->reset();
2190 for (y = 0; y < maskHeight; y++) {
2191 for (x = 0; x < maskWidth; x++) {
2192 imgMaskStr->getPixel(buf);
2194 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2202 imgStr = new ImageStream(str, width, ncomps,bits);
2205 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2207 msg("<verbose> Ignoring %d by %d image", width, height);
2208 unsigned char buf[8];
2210 for (y = 0; y < height; ++y)
2211 for (x = 0; x < width; ++x) {
2212 imgStr->getPixel(buf);
2220 this->transformXY(state, 0, 1, &x1, &y1);
2221 this->transformXY(state, 0, 0, &x2, &y2);
2222 this->transformXY(state, 1, 0, &x3, &y3);
2223 this->transformXY(state, 1, 1, &x4, &y4);
2226 /* as type 3 bitmaps are antialized, we need to place them
2227 at integer coordinates, otherwise flash player's antializing
2228 will kick in and make everything blurry */
2229 x1 = (int)(x1);y1 = (int)(y1);
2230 x2 = (int)(x2);y2 = (int)(y2);
2231 x3 = (int)(x3);y3 = (int)(y3);
2232 x4 = (int)(x4);y4 = (int)(y4);
2235 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2237 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2238 gfxglobals->pbminfo = 1;
2241 msg("<verbose> drawing %d by %d masked picture", width, height);
2243 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2244 msg("<notice> File contains jpeg pictures");
2245 gfxglobals->jpeginfo = 1;
2249 unsigned char buf[8];
2251 unsigned char*pic = new unsigned char[width*height];
2252 gfxcolor_t pal[256];
2254 state->getFillRGB(&rgb);
2256 memset(pal,255,sizeof(pal));
2257 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2258 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2259 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2260 pal[0].a = 255; pal[1].a = 0;
2263 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2264 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2265 for (y = 0; y < height; ++y)
2266 for (x = 0; x < width; ++x)
2268 imgStr->getPixel(buf);
2271 pic[width*y+x] = buf[0];
2275 unsigned char*pic2 = 0;
2278 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2287 height = realheight;
2291 /* make a black/white palette */
2293 float r = 255./(float)(numpalette-1);
2295 for(t=0;t<numpalette;t++) {
2296 pal[t].r = colToByte(rgb.r);
2297 pal[t].g = colToByte(rgb.g);
2298 pal[t].b = colToByte(rgb.b);
2299 pal[t].a = (unsigned char)(t*r);
2304 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2305 for (y = 0; y < height; ++y) {
2306 for (x = 0; x < width; ++x) {
2307 pic2[width*y+x] = pal[pic[y*width+x]];
2310 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2314 if(maskbitmap) free(maskbitmap);
2320 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2321 gfxcolor_t*pic=new gfxcolor_t[width*height];
2322 for (y = 0; y < height; ++y) {
2323 for (x = 0; x < width; ++x) {
2324 imgStr->getPixel(pixBuf);
2325 colorMap->getRGB(pixBuf, &rgb);
2326 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2327 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2328 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2329 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2331 int x1 = x*maskWidth/width;
2332 int y1 = y*maskHeight/height;
2333 int x2 = (x+1)*maskWidth/width;
2334 int y2 = (y+1)*maskHeight/height;
2336 unsigned int alpha=0;
2337 unsigned int count=0;
2338 for(xx=x1;xx<x2;xx++)
2339 for(yy=y1;yy<y2;yy++) {
2340 alpha += maskbitmap[yy*maskWidth+xx];
2344 pic[width*y+x].a = alpha / count;
2346 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2351 if(str->getKind()==strDCT)
2352 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2354 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2357 if(maskbitmap) free(maskbitmap);
2360 gfxcolor_t*pic=new gfxcolor_t[width*height];
2361 gfxcolor_t pal[256];
2362 int n = 1 << colorMap->getBits();
2364 for(t=0;t<256;t++) {
2366 colorMap->getRGB(pixBuf, &rgb);
2368 {/*if(maskColors && *maskColors==t) {
2369 msg("<notice> Color %d is transparent", t);
2370 if (imgData->maskColors) {
2372 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2373 if (pix[i] < imgData->maskColors[2*i] ||
2374 pix[i] > imgData->maskColors[2*i+1]) {
2389 pal[t].r = (unsigned char)(colToByte(rgb.r));
2390 pal[t].g = (unsigned char)(colToByte(rgb.g));
2391 pal[t].b = (unsigned char)(colToByte(rgb.b));
2392 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2395 for (y = 0; y < height; ++y) {
2396 for (x = 0; x < width; ++x) {
2397 imgStr->getPixel(pixBuf);
2398 pic[width*y+x] = pal[pixBuf[0]];
2402 if(maskWidth < width && maskHeight < height) {
2403 for(y = 0; y < height; y++) {
2404 for (x = 0; x < width; x++) {
2405 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2409 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2410 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2411 double dx = width / maskWidth;
2412 double dy = height / maskHeight;
2414 for(y = 0; y < maskHeight; y++) {
2416 for (x = 0; x < maskWidth; x++) {
2417 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2418 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2426 height = maskHeight;
2429 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2433 if(maskbitmap) free(maskbitmap);
2438 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2439 int width, int height, GBool invert,
2442 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2443 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2444 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2447 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2448 int width, int height, GfxImageColorMap *colorMap,
2449 int *maskColors, GBool inlineImg)
2451 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2452 colorMap?"colorMap":"no colorMap",
2453 maskColors?"maskColors":"no maskColors",
2455 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2456 colorMap?"colorMap":"no colorMap",
2457 maskColors?"maskColors":"no maskColors",
2460 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2461 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2462 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2465 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2466 int width, int height,
2467 GfxImageColorMap *colorMap,
2468 Stream *maskStr, int maskWidth, int maskHeight,
2471 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2472 colorMap?"colorMap":"no colorMap",
2473 maskWidth, maskHeight);
2474 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2475 colorMap?"colorMap":"no colorMap",
2476 maskWidth, maskHeight);
2478 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2479 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2480 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2483 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2484 int width, int height,
2485 GfxImageColorMap *colorMap,
2487 int maskWidth, int maskHeight,
2488 GfxImageColorMap *maskColorMap)
2490 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2491 colorMap?"colorMap":"no colorMap",
2492 maskWidth, maskHeight);
2493 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2494 colorMap?"colorMap":"no colorMap",
2495 maskWidth, maskHeight);
2497 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2498 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2499 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2502 void GFXOutputDev::stroke(GfxState *state)
2506 GfxPath * path = state->getPath();
2507 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2508 strokeGfxline(state, line, 0);
2512 void GFXOutputDev::fill(GfxState *state)
2514 gfxcolor_t col = getFillColor(state);
2515 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2517 GfxPath * path = state->getPath();
2518 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2519 if(!config_disable_polygon_conversion) {
2520 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2524 fillGfxLine(state, line, 0);
2528 void GFXOutputDev::eoFill(GfxState *state)
2530 gfxcolor_t col = getFillColor(state);
2531 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2533 GfxPath * path = state->getPath();
2534 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2535 fillGfxLine(state, line, 1);
2540 static const char* dirseparator()
2549 void addGlobalFont(const char*filename)
2551 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2552 memset(f, 0, sizeof(fontfile_t));
2553 f->filename = filename;
2554 int len = strlen(filename);
2555 char*r1 = strrchr((char*)filename, '/');
2556 char*r2 = strrchr((char*)filename, '\\');
2564 msg("<verbose> Adding font \"%s\".", filename);
2565 if(global_fonts_next) {
2566 global_fonts_next->next = f;
2567 global_fonts_next = global_fonts_next->next;
2569 global_fonts_next = global_fonts = f;
2573 void addGlobalLanguageDir(const char*dir)
2575 msg("<notice> Adding %s to language pack directories", dir);
2578 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2579 strcpy(config_file, dir);
2580 strcat(config_file, dirseparator());
2581 strcat(config_file, "add-to-xpdfrc");
2583 fi = fopen(config_file, "rb");
2585 msg("<error> Could not open %s", config_file);
2588 globalParams->parseFile(new GString(config_file), fi);
2592 void addGlobalFontDir(const char*dirname)
2594 #ifdef HAVE_DIRENT_H
2595 DIR*dir = opendir(dirname);
2597 msg("<warning> Couldn't open directory %s", dirname);
2603 ent = readdir (dir);
2607 char*name = ent->d_name;
2613 if(!strncasecmp(&name[l-4], ".pfa", 4))
2615 if(!strncasecmp(&name[l-4], ".pfb", 4))
2617 if(!strncasecmp(&name[l-4], ".ttf", 4))
2620 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2621 strcpy(fontname, dirname);
2622 strcat(fontname, dirseparator());
2623 strcat(fontname, name);
2624 addGlobalFont(fontname);
2628 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2631 msg("<warning> No dirent.h");
2635 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2636 GfxColorSpace *blendingColorSpace,
2637 GBool isolated, GBool knockout,
2640 const char*colormodename = "";
2642 if(blendingColorSpace) {
2643 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2645 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);
2646 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);
2648 //states[statepos].createsoftmask |= forSoftMask;
2649 states[statepos].createsoftmask = forSoftMask;
2650 states[statepos].transparencygroup = !forSoftMask;
2651 states[statepos].isolated = isolated;
2653 states[statepos].olddevice = this->device;
2654 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2655 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2657 gfxdevice_record_init(this->device);
2659 /*if(!forSoftMask) { ////???
2660 state->setFillOpacity(0.0);
2665 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2668 gfxdevice_t*r = this->device;
2670 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2672 this->device = states[statepos].olddevice;
2674 msg("<error> Invalid state nesting");
2676 states[statepos].olddevice = 0;
2678 gfxresult_t*recording = r->finish(r);
2680 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2681 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2683 if(states[statepos].createsoftmask) {
2684 states[statepos-1].softmaskrecording = recording;
2686 states[statepos-1].grouprecording = recording;
2689 states[statepos].createsoftmask = 0;
2690 states[statepos].transparencygroup = 0;
2694 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2696 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2697 "colordodge","colorburn","hardlight","softlight","difference",
2698 "exclusion","hue","saturation","color","luminosity"};
2700 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2701 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2703 if(state->getBlendMode() == gfxBlendNormal)
2704 infofeature("transparency groups");
2707 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2708 warnfeature(buffer, 0);
2711 gfxresult_t*grouprecording = states[statepos].grouprecording;
2713 int blendmode = state->getBlendMode();
2714 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2715 int alpha = (int)(state->getFillOpacity()*255);
2716 if(blendmode == gfxBlendMultiply && alpha>200)
2719 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2720 gfxdevice_ops_init(&ops, this->device, alpha);
2721 gfxresult_record_replay(grouprecording, &ops);
2724 grouprecording->destroy(grouprecording);
2726 states[statepos].grouprecording = 0;
2729 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2731 if(states[statepos].softmask) {
2732 /* shouldn't happen, but *does* happen */
2733 clearSoftMask(state);
2736 /* alpha = 1: retrieve mask values from alpha layer
2737 alpha = 0: retrieve mask values from luminance */
2739 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2740 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2741 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2742 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2744 infofeature("soft masks");
2746 warnfeature("soft masks from alpha channel",0);
2748 if(states[statepos].olddevice) {
2749 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2752 states[statepos].olddevice = this->device;
2753 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2754 gfxdevice_record_init(this->device);
2756 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2758 states[statepos].softmask = 1;
2759 states[statepos].softmask_alpha = alpha;
2762 static inline Guchar div255(int x) {
2763 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2766 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2768 if(c < min) c = min;
2769 if(c > max) c = max;
2773 void GFXOutputDev::clearSoftMask(GfxState *state)
2775 if(!states[statepos].softmask)
2777 states[statepos].softmask = 0;
2778 dbg("clearSoftMask statepos=%d", statepos);
2779 msg("<verbose> clearSoftMask statepos=%d", statepos);
2781 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2782 msg("<error> Error in softmask/tgroup ordering");
2786 gfxresult_t*mask = states[statepos].softmaskrecording;
2787 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2788 this->device = states[statepos].olddevice;
2790 /* get outline of all objects below the soft mask */
2791 gfxdevice_t uniondev;
2792 gfxdevice_union_init(&uniondev, 0);
2793 gfxresult_record_replay(below, &uniondev);
2794 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2795 uniondev.finish(&uniondev);
2796 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2797 gfxline_free(belowoutline);belowoutline=0;
2799 this->device->startclip(this->device, belowoutline);
2800 gfxresult_record_replay(below, this->device);
2801 gfxresult_record_replay(mask, this->device);
2802 this->device->endclip(this->device);
2805 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2806 if(width<=0 || height<=0)
2809 gfxdevice_t belowrender;
2810 gfxdevice_render_init(&belowrender);
2811 if(states[statepos+1].isolated) {
2812 belowrender.setparameter(&belowrender, "fillwhite", "1");
2814 belowrender.setparameter(&belowrender, "antialize", "2");
2815 belowrender.startpage(&belowrender, width, height);
2816 gfxresult_record_replay(below, &belowrender);
2817 belowrender.endpage(&belowrender);
2818 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2819 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2820 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2822 gfxdevice_t maskrender;
2823 gfxdevice_render_init(&maskrender);
2824 maskrender.startpage(&maskrender, width, height);
2825 gfxresult_record_replay(mask, &maskrender);
2826 maskrender.endpage(&maskrender);
2827 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2828 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2830 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2831 msg("<fatal> Internal error in mask drawing");
2836 for(y=0;y<height;y++) {
2837 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2838 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2839 for(x=0;x<width;x++) {
2841 if(states[statepos].softmask_alpha) {
2844 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2847 l2->a = div255(alpha*l2->a);
2849 /* DON'T premultiply alpha- this is done by fillbitmap,
2850 depending on the output device */
2851 //l2->r = div255(alpha*l2->r);
2852 //l2->g = div255(alpha*l2->g);
2853 //l2->b = div255(alpha*l2->b);
2859 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2862 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2863 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2865 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2867 mask->destroy(mask);
2868 below->destroy(below);
2869 maskresult->destroy(maskresult);
2870 belowresult->destroy(belowresult);
2871 states[statepos].softmaskrecording = 0;
2876 // public: ~MemCheck()
2878 // delete globalParams;globalParams=0;
2879 // Object::memCheck(stderr);
2880 // gMemReport(stderr);