2 implements a pdf output device (OutputDev).
4 This file is part of swftools.
6 Swftools is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 Swftools is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with swftools; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
28 #include "../../config.h"
33 #ifdef HAVE_SYS_STAT_H
36 #ifdef HAVE_FONTCONFIG
37 #include <fontconfig.h>
42 #include <goo/GooString.h>
43 #include <goo/gfile.h>
58 #include "OutputDev.h"
61 //#include "NameToUnicodeTable.h"
62 #include "GlobalParams.h"
63 #include "GFXOutputDev.h"
65 // swftools header files
67 #include "../gfxdevice.h"
68 #include "../gfxtools.h"
69 #include "../gfxfont.h"
70 #include "../gfxpoly.h"
71 #include "../devices/record.h"
72 #include "../devices/ops.h"
73 #include "../devices/polyops.h"
74 #include "../devices/render.h"
81 #define SQRT2 1.41421356237309504880
83 typedef struct _fontfile
86 int len; // basename length
88 struct _fontfile*next;
93 static fontfile_t* global_fonts = 0;
94 static fontfile_t* global_fonts_next = 0;
96 static int fontnum = 0;
109 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
110 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
111 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
112 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
113 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
114 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
115 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
116 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
117 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
118 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
119 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
120 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
121 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
122 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
125 static int verbose = 0;
126 static int dbgindent = 1;
127 static void dbg(const char*format, ...)
134 va_start(arglist, format);
135 vsnprintf(buf, sizeof(buf)-1, format, arglist);
138 while(l && buf[l-1]=='\n') {
143 int indent = dbgindent;
152 GFXOutputGlobals*gfxglobals=0;
154 GFXOutputGlobals::GFXOutputGlobals()
156 this->featurewarnings = 0;
158 this->textmodeinfo = 0;
162 GFXOutputGlobals::~GFXOutputGlobals()
164 feature_t*f = this->featurewarnings;
166 feature_t*next = f->next;
168 free(f->string);f->string =0;
174 this->featurewarnings = 0;
177 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
179 feature_t*f = gfxglobals->featurewarnings;
181 if(!strcmp(feature, f->string))
185 f = (feature_t*)malloc(sizeof(feature_t));
186 f->string = strdup(feature);
187 f->next = gfxglobals->featurewarnings;
188 gfxglobals->featurewarnings = f;
190 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
191 if(this->config_break_on_warning) {
192 msg("<fatal> Aborting conversion due to unsupported feature");
196 msg("<notice> File contains %s",feature);
199 void GFXOutputDev::warnfeature(const char*feature,char fully)
201 showfeature(feature,fully,1);
203 void GFXOutputDev::infofeature(const char*feature)
205 showfeature(feature,0,0);
208 GFXOutputState::GFXOutputState() {
210 this->createsoftmask = 0;
211 this->transparencygroup = 0;
213 this->grouprecording = 0;
217 GBool GFXOutputDev::interpretType3Chars()
222 typedef struct _drawnchar
240 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
241 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
246 free(chars);chars = 0;
253 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
257 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
260 chars[num_chars].x = x;
261 chars[num_chars].y = y;
262 chars[num_chars].color = color;
263 chars[num_chars].charid = charid;
267 char* writeOutStdFont(fontentry* f)
272 char* tmpFileName = mktmpname(namebuf1);
274 sprintf(namebuf2, "%s.afm", tmpFileName);
275 fi = fopen(namebuf2, "wb");
278 fwrite(f->afm, 1, f->afmlen, fi);
281 sprintf(namebuf2, "%s.pfb", tmpFileName);
282 fi = fopen(namebuf2, "wb");
285 fwrite(f->pfb, 1, f->pfblen, fi);
287 return strdup(namebuf2);
289 void unlinkfont(char* filename)
294 msg("<verbose> Removing temporary font file %s", filename);
297 if(!strncmp(&filename[l-4],".afm",4)) {
298 memcpy(&filename[l-4],".pfb",4); unlink(filename);
299 memcpy(&filename[l-4],".pfa",4); unlink(filename);
300 memcpy(&filename[l-4],".afm",4);
303 if(!strncmp(&filename[l-4],".pfa",4)) {
304 memcpy(&filename[l-4],".afm",4); unlink(filename);
305 memcpy(&filename[l-4],".pfa",4);
308 if(!strncmp(&filename[l-4],".pfb",4)) {
309 memcpy(&filename[l-4],".afm",4); unlink(filename);
310 memcpy(&filename[l-4],".pfb",4);
315 static int config_use_fontconfig = 1;
316 static int fcinitcalled = 0;
318 GFXGlobalParams::GFXGlobalParams()
319 : GlobalParams((char*)"")
321 //setupBaseFonts(char *dir); //not tested yet
323 GFXGlobalParams::~GFXGlobalParams()
325 msg("<verbose> Performing cleanups");
327 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
328 if(pdf2t1map[t].fullfilename) {
329 unlinkfont(pdf2t1map[t].fullfilename);
332 #ifdef HAVE_FONTCONFIG
333 if(config_use_fontconfig && fcinitcalled)
337 #ifdef HAVE_FONTCONFIG
338 static char fc_ismatch(FcPattern*match, char*family, char*style)
340 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
341 FcBool scalable=FcFalse, outline=FcFalse;
342 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
343 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
344 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
345 FcPatternGetBool(match, "outline", 0, &outline);
346 FcPatternGetBool(match, "scalable", 0, &scalable);
348 if(scalable!=FcTrue || outline!=FcTrue)
351 if (!strcasecmp(fcfamily, family)) {
352 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
355 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
361 char* fontconfig_searchForFont(char*name)
363 #ifdef HAVE_FONTCONFIG
364 if(!config_use_fontconfig)
367 // call init ony once
371 // check whether we have a config file
372 char* configfile = (char*)FcConfigFilename(0);
373 int configexists = 0;
374 FILE*fi = fopen(configfile, "rb");
376 configexists = 1;fclose(fi);
377 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
379 msg("<debug> Initializing FontConfig (no configfile)");
383 /* A fontconfig instance which didn't find a configfile is unbelievably
384 cranky, so let's just write out a small xml file and make fontconfig
386 FcConfig*c = FcConfigCreate();
388 char* tmpFileName = mktmpname(namebuf);
389 FILE*fi = fopen(tmpFileName, "wb");
390 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
392 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
394 fprintf(fi, "<dir>~/.fonts</dir>\n");
396 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
398 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
399 fprintf(fi, "</fontconfig>\n");
401 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
402 FcConfigBuildFonts(c);
403 FcConfigSetCurrent(c);
407 msg("<debug> FontConfig Initialization failed. Disabling.");
408 config_use_fontconfig = 0;
411 FcConfig * config = FcConfigGetCurrent();
413 msg("<debug> FontConfig Config Initialization failed. Disabling.");
414 config_use_fontconfig = 0;
418 /* add external fonts to fontconfig's config, too. */
419 fontfile_t*fd = global_fonts;
421 FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
425 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
426 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
427 if(!set || !set->nfont) {
428 msg("<debug> FontConfig has zero fonts. Disabling.");
429 config_use_fontconfig = 0;
433 if(getLogLevel() >= LOGLEVEL_TRACE) {
435 for(t=0;t<set->nfont;t++) {
436 char*fcfamily=0,*fcstyle=0,*filename=0;
437 FcBool scalable=FcFalse, outline=FcFalse;
438 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
439 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
440 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
441 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
442 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
443 if(scalable && outline) {
444 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
450 char*family = strdup(name);
452 char*dash = strchr(family, '-');
453 FcPattern*pattern = 0;
457 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
458 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
460 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
461 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
465 FcConfigSubstitute(0, pattern, FcMatchPattern);
466 FcDefaultSubstitute(pattern);
468 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
471 for(t=0;t<set->nfont;t++) {
472 FcPattern*match = set->fonts[t];
473 //FcPattern*match = FcFontMatch(0, pattern, &result);
474 if(fc_ismatch(match, family, style)) {
476 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
477 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
480 //FcPatternDestroy(match);
481 msg("<debug> fontconfig: returning filename %s", filename);
483 FcPatternDestroy(pattern);
484 FcFontSetDestroy(set);
485 return filename?strdup(filename):0;
490 FcPatternDestroy(pattern);
491 FcFontSetDestroy(set);
498 static DisplayFontParamKind detectFontType(const char*filename)
500 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
501 return displayFontTT;
502 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
503 return displayFontT1;
504 return displayFontTT;
507 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
509 msg("<verbose> looking for font %s in global params", fontName->getCString());
511 char*name = fontName->getCString();
513 /* see if it is a pdf standard font */
515 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
516 if(!strcmp(name, pdf2t1map[t].pdffont)) {
517 if(!pdf2t1map[t].fullfilename) {
518 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
519 if(!pdf2t1map[t].fullfilename) {
520 msg("<error> Couldn't save default font- is the Temp Directory writable?");
522 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
525 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
526 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
531 int bestlen = 0x7fffffff;
532 const char*bestfilename = 0;
534 #ifndef HAVE_FONTCONFIG
535 /* if we don't have fontconfig, try a simple filename-comparison approach */
536 fontfile_t*f = global_fonts;
538 if(strstr(f->filename, name)) {
539 if(f->len < bestlen) {
541 bestfilename = f->filename;
548 /* if we didn't find anything up to now, try looking for the
549 font via fontconfig */
552 filename = fontconfig_searchForFont(name);
554 filename = strdup(bestfilename);
558 msg("<verbose> Font %s maps to %s\n", name, filename);
559 DisplayFontParamKind kind = detectFontType(filename);
560 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
561 if(kind == displayFontTT) {
562 dfp->tt.fileName = new GString(filename);
564 dfp->t1.fileName = new GString(filename);
569 return GlobalParams::getDisplayFont(fontName);
572 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
575 gfxglobals = new GFXOutputGlobals();
579 this->xref = doc->getXRef();
581 this->type3active = 0;
584 this->user_movex = 0;
585 this->user_movey = 0;
588 this->user_clipx1 = 0;
589 this->user_clipy1 = 0;
590 this->user_clipx2 = 0;
591 this->user_clipy2 = 0;
592 this->current_fontinfo = 0;
593 this->current_text_stroke = 0;
594 this->current_text_clip = 0;
595 this->outer_clip_box = 0;
596 this->config_bigchar=0;
597 this->config_convertgradients=1;
598 this->config_break_on_warning=0;
599 this->config_remapunicode=0;
600 this->config_transparent=0;
601 this->config_extrafontdata = 0;
602 this->config_drawonlyshapes = 0;
603 this->config_disable_polygon_conversion = 0;
604 this->config_multiply = 1;
608 memset(states, 0, sizeof(states));
611 void GFXOutputDev::setParameter(const char*key, const char*value)
613 if(!strcmp(key,"breakonwarning")) {
614 this->config_break_on_warning = atoi(value);
615 } else if(!strcmp(key,"remapunicode")) {
616 this->config_remapunicode = atoi(value);
617 } else if(!strcmp(key,"transparent")) {
618 this->config_transparent = atoi(value);
619 } else if(!strcmp(key,"drawonlyshapes")) {
620 this->config_drawonlyshapes = atoi(value);
621 } else if(!strcmp(key,"extrafontdata")) {
622 this->config_extrafontdata = atoi(value);
623 } else if(!strcmp(key,"convertgradients")) {
624 this->config_convertgradients = atoi(value);
625 } else if(!strcmp(key,"multiply")) {
626 this->config_multiply = atoi(value);
627 if(this->config_multiply<1)
628 this->config_multiply=1;
629 } else if(!strcmp(key,"disable_polygon_conversion")) {
630 this->config_disable_polygon_conversion = atoi(value);
634 void GFXOutputDev::setDevice(gfxdevice_t*dev)
639 void GFXOutputDev::setMove(int x,int y)
641 this->user_movex = x;
642 this->user_movey = y;
645 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
647 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
648 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
650 this->user_clipx1 = x1;
651 this->user_clipy1 = y1;
652 this->user_clipx2 = x2;
653 this->user_clipy2 = y2;
656 static char*getFontName(GfxFont*font)
659 GString*gstr = font->getName();
660 char* fname = gstr==0?0:gstr->getCString();
664 sprintf(buf, "UFONT%d", r->num);
665 fontid = strdup(buf);
667 fontid = strdup(fname);
671 char* plus = strchr(fontid, '+');
672 if(plus && plus < &fontid[strlen(fontid)-1]) {
673 fontname = strdup(plus+1);
675 fontname = strdup(fontid);
681 static void dumpFontInfo(const char*loglevel, GfxFont*font);
682 static int lastdumps[1024];
683 static int lastdumppos = 0;
688 static void showFontError(GfxFont*font, int nr)
692 for(t=0;t<lastdumppos;t++)
693 if(lastdumps[t] == r->num)
697 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
698 lastdumps[lastdumppos++] = r->num;
700 msg("<warning> The following font caused problems:");
702 msg("<warning> The following font caused problems (substituting):");
704 msg("<warning> The following Type 3 Font will be rendered as graphics:");
705 dumpFontInfo("<warning>", font);
708 static void dumpFontInfo(const char*loglevel, GfxFont*font)
710 char* id = getFontID(font);
711 char* name = getFontName(font);
712 Ref* r=font->getID();
713 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
715 GString*gstr = font->getTag();
717 msg("%s| Tag: %s", loglevel, id);
719 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
721 GfxFontType type=font->getType();
723 case fontUnknownType:
724 msg("%s| Type: unknown",loglevel);
727 msg("%s| Type: 1",loglevel);
730 msg("%s| Type: 1C",loglevel);
733 msg("%s| Type: 3",loglevel);
736 msg("%s| Type: TrueType",loglevel);
739 msg("%s| Type: CIDType0",loglevel);
742 msg("%s| Type: CIDType0C",loglevel);
745 msg("%s| Type: CIDType2",loglevel);
750 GBool embedded = font->getEmbeddedFontID(&embRef);
752 if(font->getEmbeddedFontName()) {
753 embeddedName = font->getEmbeddedFontName()->getCString();
756 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
758 gstr = font->getExtFontFile();
760 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
762 // Get font descriptor flags.
763 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
764 if(font->isSerif()) msg("%s| is serif", loglevel);
765 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
766 if(font->isItalic()) msg("%s| is italic", loglevel);
767 if(font->isBold()) msg("%s| is bold", loglevel);
773 //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");}
774 //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");}
776 void dump_outline(gfxline_t*line)
778 /*gfxbbox_t*r = gfxline_isrectangle(line);
780 printf("is not a rectangle\n");
782 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
786 if(line->type == gfx_moveTo) {
787 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
788 } else if(line->type == gfx_lineTo) {
789 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
790 } else if(line->type == gfx_splineTo) {
791 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
797 void gfxPath_dump(GfxPath*path)
799 int num = path->getNumSubpaths();
802 for(t = 0; t < num; t++) {
803 GfxSubpath *subpath = path->getSubpath(t);
804 int subnum = subpath->getNumPoints();
806 for(s=0;s<subnum;s++) {
807 double x=subpath->getX(s);
808 double y=subpath->getY(s);
809 if(s==0 && !subpath->getCurve(s)) {
810 printf("M %f %f\n", x, y);
811 } else if(s==0 && subpath->getCurve(s)) {
812 printf("E %f %f\n", x, y);
813 } else if(subpath->getCurve(s)) {
814 printf("C %f %f\n", x, y);
816 printf("T %f %f\n", x, y);
822 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
824 int num = path->getNumSubpaths();
827 double lastx=0,lasty=0,posx=0,posy=0;
830 msg("<warning> empty path");
834 gfxdrawer_target_gfxline(&draw);
836 for(t = 0; t < num; t++) {
837 GfxSubpath *subpath = path->getSubpath(t);
838 int subnum = subpath->getNumPoints();
839 double bx=0,by=0,cx=0,cy=0;
841 for(s=0;s<subnum;s++) {
844 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
847 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
848 draw.lineTo(&draw, lastx, lasty);
850 draw.moveTo(&draw, x,y);
855 } else if(subpath->getCurve(s) && cpos==0) {
859 } else if(subpath->getCurve(s) && cpos==1) {
867 draw.lineTo(&draw, x,y);
869 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
876 /* fix non-closed lines */
877 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
878 draw.lineTo(&draw, lastx, lasty);
880 gfxline_t*result = (gfxline_t*)draw.result(&draw);
882 gfxline_optimize(result);
887 GBool GFXOutputDev::useTilingPatternFill()
889 infofeature("tiled patterns");
890 // if(config_convertgradients)
894 GBool GFXOutputDev::useShadedFills()
896 infofeature("shaded fills");
897 if(config_convertgradients)
902 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
904 state->transform(x,y,nx,ny);
905 *nx += user_movex + clipmovex;
906 *ny += user_movey + clipmovey;
910 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
911 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
912 int paintType, Dict *resDict,
913 double *mat, double *bbox,
914 int x0, int y0, int x1, int y1,
915 double xStep, double yStep)
917 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
918 int paintType, Dict *resDict,
919 double *mat, double *bbox,
920 int x0, int y0, int x1, int y1,
921 double xStep, double yStep)
924 msg("<debug> tilingPatternFill");
925 infofeature("tiling pattern fills");
928 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
930 msg("<error> functionShadedFill not supported yet");
931 infofeature("function shaded fills");
934 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
938 colspace->getRGB(col, &rgb);
939 c.r = colToByte(rgb.r);
940 c.g = colToByte(rgb.g);
941 c.b = colToByte(rgb.b);
946 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
948 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
949 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
952 this->transformXY(state, x0,y0, &x0,&y0);
953 this->transformXY(state, x1,y1, &x1,&y1);
954 this->transformXY(state, x2,y2, &x2,&y2);
959 shading->getColor(0.0, &color0);
960 shading->getColor(0.5, &color1);
961 shading->getColor(1.0, &color2);
963 GfxColorSpace* colspace = shading->getColorSpace();
965 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
966 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
967 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
968 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
969 infofeature("radial shaded fills");
971 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
975 g[0].color = col2col(colspace, &color0);
976 g[1].color = col2col(colspace, &color1);
977 g[2].color = col2col(colspace, &color2);
982 gfxbbox_t b = states[statepos].clipbbox;
983 gfxline_t p1,p2,p3,p4,p5;
984 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
985 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
986 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
987 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
988 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
991 //m.m00 = (x3-x0); m.m10 = (x1-x0);
992 //m.m01 = (y3-y0); m.m11 = (y1-y0);
993 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
994 m.m00 = (x1-x0); m.m10 = (x2-x0);
995 m.m01 = (y1-y0); m.m11 = (y2-y0);
999 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1003 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1006 shading->getCoords(&x0,&y0,&x1,&y1);
1007 this->transformXY(state, x0,y0,&x0,&y0);
1008 this->transformXY(state, x1,y1,&x1,&y1);
1013 shading->getColor(0.0, &color0);
1014 shading->getColor(0.5, &color1);
1015 shading->getColor(1.0, &color2);
1017 GfxColorSpace* colspace = shading->getColorSpace();
1019 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1020 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1021 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1022 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1024 infofeature("axial shaded fills");
1026 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1030 g[0].color = col2col(colspace, &color0);
1031 g[1].color = col2col(colspace, &color1);
1032 g[2].color = col2col(colspace, &color2);
1037 double xMin,yMin,xMax,yMax;
1038 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1039 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1040 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1043 xMin = 1024; yMin = 1024;
1045 gfxbbox_t b = states[statepos].clipbbox;
1046 gfxline_t p1,p2,p3,p4,p5;
1047 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1048 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1049 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1050 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1051 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1053 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1054 the middle of the two control points */
1056 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1057 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1058 m.tx = (x0 + x1)/2 - 0.5;
1059 m.ty = (y0 + y1)/2 - 0.5;
1061 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1067 GBool GFXOutputDev::useDrawForm()
1069 infofeature("forms");
1072 void GFXOutputDev::drawForm(Ref id)
1074 msg("<error> drawForm not implemented");
1076 GBool GFXOutputDev::needNonText()
1080 void GFXOutputDev::endPage()
1082 msg("<verbose> endPage (GfxOutputDev)");
1083 if(outer_clip_box) {
1084 device->endclip(device);
1087 /* notice: we're not fully done yet with this page- there might still be
1088 a few calls to drawLink() yet to come */
1091 static inline double sqr(double x) {return x*x;}
1093 #define STROKE_FILL 1
1094 #define STROKE_CLIP 2
1095 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1097 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1098 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1099 double miterLimit = state->getMiterLimit();
1100 double width = state->getTransformedLineWidth();
1103 double opaq = state->getStrokeOpacity();
1105 state->getFillRGB(&rgb);
1107 state->getStrokeRGB(&rgb);
1109 col.r = colToByte(rgb.r);
1110 col.g = colToByte(rgb.g);
1111 col.b = colToByte(rgb.b);
1112 col.a = (unsigned char)(opaq*255);
1114 gfx_capType capType = gfx_capRound;
1115 if(lineCap == 0) capType = gfx_capButt;
1116 else if(lineCap == 1) capType = gfx_capRound;
1117 else if(lineCap == 2) capType = gfx_capSquare;
1118 else msg("<error> Invalid line cap type");
1120 gfx_joinType joinType = gfx_joinRound;
1121 if(lineJoin == 0) joinType = gfx_joinMiter;
1122 else if(lineJoin == 1) joinType = gfx_joinRound;
1123 else if(lineJoin == 2) joinType = gfx_joinBevel;
1124 else msg("<error> Invalid line join type");
1126 gfxline_t*line2 = 0;
1128 int dashLength = states[statepos].dashLength;
1129 double*dashPattern = states[statepos].dashPattern;
1130 double dashStart = states[statepos].dashStart;
1131 if(dashLength && dashPattern) {
1132 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1135 /* try to find out how much the transformation matrix would
1136 stretch the dashes, and factor that into the dash lengths.
1137 This is not the entirely correct approach- it would be
1138 better to first convert the path to an unscaled version,
1139 then apply dashing, and then transform the path using
1140 the current transformation matrix. However there are few
1141 PDFs which actually stretch a dashed path in a non-orthonormal
1143 double tx1, ty1, tx2, ty2, tx3, ty3;
1144 this->transformXY(state, 0, 0, &tx1, &ty1);
1145 this->transformXY(state, 0, 1, &tx2, &ty2);
1146 this->transformXY(state, 1, 0, &tx3, &ty3);
1147 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1148 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1150 warnfeature("non-ortogonally dashed strokes", 0);
1151 double f = (d1+d2)/2;
1153 msg("<trace> %d dashes", dashLength);
1154 msg("<trace> | phase: %f", dashStart);
1155 for(t=0;t<dashLength;t++) {
1156 dash[t] = (float)dashPattern[t] * f;
1160 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1162 dash[dashLength] = -1;
1163 if(getLogLevel() >= LOGLEVEL_TRACE) {
1167 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1171 msg("<trace> After dashing:");
1174 if(getLogLevel() >= LOGLEVEL_TRACE) {
1175 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1177 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1178 lineCap==0?"butt": (lineCap==1?"round":"square"),
1180 col.r,col.g,col.b,col.a
1185 if(flags&STROKE_FILL) {
1186 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1187 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1188 if(getLogLevel() >= LOGLEVEL_TRACE) {
1189 dump_outline(gfxline);
1192 msg("<warning> Empty polygon (resulting from stroked line)");
1194 if(flags&STROKE_CLIP) {
1195 device->startclip(device, gfxline);
1196 states[statepos].clipping++;
1198 device->fill(device, gfxline, &col);
1200 gfxline_free(gfxline);
1201 gfxpoly_destroy(poly);
1203 if(flags&STROKE_CLIP)
1204 msg("<error> Stroke&clip not supported at the same time");
1205 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1209 gfxline_free(line2);
1212 gfxcolor_t getFillColor(GfxState * state)
1215 double opaq = state->getFillOpacity();
1216 state->getFillRGB(&rgb);
1218 col.r = colToByte(rgb.r);
1219 col.g = colToByte(rgb.g);
1220 col.b = colToByte(rgb.b);
1221 col.a = (unsigned char)(opaq*255);
1225 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1227 gfxcolor_t col = getFillColor(state);
1229 if(getLogLevel() >= LOGLEVEL_TRACE) {
1230 msg("<trace> %sfill %02x%02x%02x%02x%s", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1233 device->fill(device, line, &col);
1236 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1238 if(getLogLevel() >= LOGLEVEL_TRACE) {
1239 msg("<trace> %sclip", evenodd?"eo":"");
1242 gfxbbox_t bbox = gfxline_getbbox(line);
1243 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1245 device->startclip(device, line);
1246 states[statepos].clipping++;
1249 void GFXOutputDev::clip(GfxState *state)
1251 GfxPath * path = state->getPath();
1252 msg("<trace> clip");
1253 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1254 if(!config_disable_polygon_conversion) {
1255 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1259 clipToGfxLine(state, line, 0);
1263 void GFXOutputDev::eoClip(GfxState *state)
1265 GfxPath * path = state->getPath();
1266 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1267 clipToGfxLine(state, line, 1);
1270 void GFXOutputDev::clipToStrokePath(GfxState *state)
1272 GfxPath * path = state->getPath();
1273 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1275 if(getLogLevel() >= LOGLEVEL_TRACE) {
1276 double width = state->getTransformedLineWidth();
1277 msg("<trace> cliptostrokepath width=%f", width);
1281 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1285 void GFXOutputDev::finish()
1287 if(outer_clip_box) {
1289 device->endclip(device);
1295 GFXOutputDev::~GFXOutputDev()
1299 GBool GFXOutputDev::upsideDown()
1303 GBool GFXOutputDev::useDrawChar()
1308 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1309 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1311 static char tmp_printstr[4096];
1312 char* makeStringPrintable(char*str)
1314 int len = strlen(str);
1321 for(t=0;t<len;t++) {
1326 tmp_printstr[t] = c;
1329 tmp_printstr[len++] = '.';
1330 tmp_printstr[len++] = '.';
1331 tmp_printstr[len++] = '.';
1333 tmp_printstr[len] = 0;
1334 return tmp_printstr;
1336 void GFXOutputDev::updateFontMatrix(GfxState*state)
1338 double* ctm = state->getCTM();
1339 double fontSize = state->getFontSize();
1340 double*textMat = state->getTextMat();
1342 /* taking the absolute value of horizScaling seems to be required for
1343 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1344 double hscale = fabs(state->getHorizScaling());
1346 // from xpdf-3.02/SplashOutputDev:updateFont
1347 double mm11 = textMat[0] * fontSize * hscale;
1348 double mm12 = textMat[1] * fontSize * hscale;
1349 double mm21 = textMat[2] * fontSize;
1350 double mm22 = textMat[3] * fontSize;
1352 // multiply with ctm, like state->getFontTransMat() does
1353 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1354 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1355 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1356 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1357 this->current_font_matrix.tx = 0;
1358 this->current_font_matrix.ty = 0;
1361 void GFXOutputDev::beginString(GfxState *state, GString *s)
1363 int render = state->getRender();
1364 if(current_text_stroke) {
1365 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1367 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1370 static gfxline_t* mkEmptyGfxShape(double x, double y)
1372 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1373 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1377 static char isValidUnicode(int c)
1379 if(c>=32 && c<0x2fffe)
1384 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1385 double dx, double dy,
1386 double originX, double originY,
1387 CharCode charid, int nBytes, Unicode *_u, int uLen)
1389 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1390 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1394 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1396 int render = state->getRender();
1397 gfxcolor_t col = getFillColor(state);
1399 // check for invisible text -- this is used by Acrobat Capture
1400 if (render == RENDER_INVISIBLE) {
1402 if(!config_extrafontdata)
1406 GfxFont*font = state->getFont();
1408 if(font->getType() == fontType3) {
1409 /* type 3 chars are passed as graphics */
1410 msg("<debug> type3 char at %f/%f", x, y);
1414 Unicode u = uLen?(_u[0]):0;
1416 gfxmatrix_t m = this->current_font_matrix;
1417 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1418 //m.tx += originX; m.ty += originY;
1420 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);
1422 if((render == RENDER_FILL && !config_drawonlyshapes) || render == RENDER_INVISIBLE) {
1423 int space = this->current_fontinfo->space_char;
1424 if(config_extrafontdata && space>=0 && m.m00 && !m.m01) {
1425 /* space char detection */
1426 if(last_char_gfxfont == current_gfxfont &&
1427 last_char_y == m.ty &&
1428 !last_char_was_space) {
1429 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1430 int space = this->current_fontinfo->space_char;
1431 if(m.tx - expected_x >= m.m00*64) {
1432 msg("<debug> There's a %f (%f) pixel gap between char %d and char %d, I'm inserting a space here",
1434 (m.tx-expected_x)/m.m00,
1435 last_char, glyphid);
1437 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1438 if(m2.tx < expected_x) m2.tx = expected_x;
1439 device->drawchar(device, current_gfxfont, space, &col, &m2);
1442 last_char_gfxfont = current_gfxfont;
1443 last_char = glyphid;
1446 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1448 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1450 msg("<debug> Drawing glyph %d as shape", charid);
1451 if(!gfxglobals->textmodeinfo) {
1452 msg("<notice> Some texts will be rendered as shape");
1453 gfxglobals->textmodeinfo = 1;
1455 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1456 gfxline_t*tglyph = gfxline_clone(glyph);
1457 gfxline_transform(tglyph, &m);
1458 if((render&3) != RENDER_INVISIBLE) {
1459 gfxline_t*add = gfxline_clone(tglyph);
1460 current_text_stroke = gfxline_append(current_text_stroke, add);
1462 if(render&RENDER_CLIP) {
1463 gfxline_t*add = gfxline_clone(tglyph);
1464 current_text_clip = gfxline_append(current_text_clip, add);
1465 if(!current_text_clip) {
1466 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1469 gfxline_free(tglyph);
1473 void GFXOutputDev::endString(GfxState *state)
1475 int render = state->getRender();
1476 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1478 if(current_text_stroke) {
1479 /* fillstroke and stroke text rendering objects we can process right
1480 now (as there may be texts of other rendering modes in this
1481 text object)- clipping objects have to wait until endTextObject,
1483 device->setparameter(device, "mark","TXT");
1484 if((render&3) == RENDER_FILL) {
1485 fillGfxLine(state, current_text_stroke, 0);
1486 gfxline_free(current_text_stroke);
1487 current_text_stroke = 0;
1488 } else if((render&3) == RENDER_FILLSTROKE) {
1489 fillGfxLine(state, current_text_stroke, 0);
1490 strokeGfxline(state, current_text_stroke,0);
1491 gfxline_free(current_text_stroke);
1492 current_text_stroke = 0;
1493 } else if((render&3) == RENDER_STROKE) {
1494 strokeGfxline(state, current_text_stroke,0);
1495 gfxline_free(current_text_stroke);
1496 current_text_stroke = 0;
1498 device->setparameter(device, "mark","");
1502 void GFXOutputDev::endTextObject(GfxState *state)
1504 int render = state->getRender();
1505 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1507 if(current_text_clip) {
1508 device->setparameter(device, "mark","TXT");
1509 clipToGfxLine(state, current_text_clip, 0);
1510 device->setparameter(device, "mark","");
1511 gfxline_free(current_text_clip);
1512 current_text_clip = 0;
1516 /* the logic seems to be as following:
1517 first, beginType3Char is called, with the charcode and the coordinates.
1518 if this function returns true, it already knew about the char and has now drawn it.
1519 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1520 called with some parameters.
1521 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1522 at the position first passed to beginType3Char). the char ends with endType3Char.
1524 The drawing operations between beginType3Char and endType3Char are somewhat different to
1525 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1526 color determines the color of a font)
1529 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1531 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1534 if(config_extrafontdata && current_fontinfo) {
1536 gfxmatrix_t m = this->current_font_matrix;
1537 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1538 m.m00*=INTERNAL_FONT_SIZE;
1539 m.m01*=INTERNAL_FONT_SIZE;
1540 m.m10*=INTERNAL_FONT_SIZE;
1541 m.m11*=INTERNAL_FONT_SIZE;
1543 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1544 msg("<error> Invalid charid %d for font", charid);
1547 gfxcolor_t col={0,0,0,0};
1548 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1549 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1553 /* the character itself is going to be passed using the draw functions */
1554 return gFalse; /* gTrue= is_in_cache? */
1557 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1559 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1562 void GFXOutputDev::endType3Char(GfxState *state)
1565 msg("<debug> endType3Char");
1568 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1570 this->currentpage = pageNum;
1572 int rot = doc->getPageRotate(1);
1573 gfxcolor_t white = {255,255,255,255};
1574 gfxcolor_t black = {255,0,0,0};
1576 gfxline_t clippath[5];
1578 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1579 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1580 Use CropBox, not MediaBox, as page size
1587 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1588 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1590 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1591 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1593 this->clipmovex = -(int)x1;
1594 this->clipmovey = -(int)y1;
1596 /* apply user clip box */
1597 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1598 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1599 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1600 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1601 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1602 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1604 x1 += this->clipmovex;
1605 y1 += this->clipmovey;
1606 x2 += this->clipmovex;
1607 y2 += this->clipmovey;
1610 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1612 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);
1614 msg("<verbose> page is rotated %d degrees", rot);
1616 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1617 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1618 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1619 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1620 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1621 device->startclip(device, clippath); outer_clip_box = 1;
1622 if(!config_transparent) {
1623 device->fill(device, clippath, &white);
1625 states[statepos].clipbbox.xmin = x1;
1626 states[statepos].clipbbox.ymin = x1;
1627 states[statepos].clipbbox.xmax = x2;
1628 states[statepos].clipbbox.ymax = y2;
1630 states[statepos].dashPattern = 0;
1631 states[statepos].dashLength = 0;
1632 states[statepos].dashStart = 0;
1634 this->last_char_gfxfont = 0;
1638 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1640 double x1, y1, x2, y2;
1641 gfxline_t points[5];
1644 msg("<debug> drawlink");
1646 link->getRect(&x1, &y1, &x2, &y2);
1647 cvtUserToDev(x1, y1, &x, &y);
1648 points[0].type = gfx_moveTo;
1649 points[0].x = points[4].x = x + user_movex + clipmovex;
1650 points[0].y = points[4].y = y + user_movey + clipmovey;
1651 points[0].next = &points[1];
1652 cvtUserToDev(x2, y1, &x, &y);
1653 points[1].type = gfx_lineTo;
1654 points[1].x = x + user_movex + clipmovex;
1655 points[1].y = y + user_movey + clipmovey;
1656 points[1].next = &points[2];
1657 cvtUserToDev(x2, y2, &x, &y);
1658 points[2].type = gfx_lineTo;
1659 points[2].x = x + user_movex + clipmovex;
1660 points[2].y = y + user_movey + clipmovey;
1661 points[2].next = &points[3];
1662 cvtUserToDev(x1, y2, &x, &y);
1663 points[3].type = gfx_lineTo;
1664 points[3].x = x + user_movex + clipmovex;
1665 points[3].y = y + user_movey + clipmovey;
1666 points[3].next = &points[4];
1667 cvtUserToDev(x1, y1, &x, &y);
1668 points[4].type = gfx_lineTo;
1669 points[4].x = x + user_movex + clipmovex;
1670 points[4].y = y + user_movey + clipmovey;
1673 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1674 points[0].x, points[0].y,
1675 points[1].x, points[1].y,
1676 points[2].x, points[2].y,
1677 points[3].x, points[3].y);
1679 if(getLogLevel() >= LOGLEVEL_TRACE) {
1680 dump_outline(points);
1683 LinkAction*action=link->getAction();
1686 const char*type = "-?-";
1689 msg("<trace> drawlink action=%d", action->getKind());
1690 switch(action->getKind())
1694 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1695 LinkDest *dest=NULL;
1696 if (ha->getDest()==NULL)
1697 dest=catalog->findDest(ha->getNamedDest());
1699 dest=ha->getDest()->copy();
1701 if (dest->isPageRef()){
1702 Ref pageref=dest->getPageRef();
1703 page=catalog->findPage(pageref.num,pageref.gen);
1705 else page=dest->getPageNum();
1706 sprintf(buf, "%d", page);
1714 LinkGoToR*l = (LinkGoToR*)action;
1715 GString*g = l->getFileName();
1717 s = strdup(g->getCString());
1719 /* if the GoToR link has no filename, then
1720 try to find a refernce in the *local*
1722 GString*g = l->getNamedDest();
1724 s = strdup(g->getCString());
1730 LinkNamed*l = (LinkNamed*)action;
1731 GString*name = l->getName();
1733 s = strdup(name->lowerCase()->getCString());
1734 named = name->getCString();
1737 if(strstr(s, "next") || strstr(s, "forward"))
1739 page = currentpage + 1;
1741 else if(strstr(s, "prev") || strstr(s, "back"))
1743 page = currentpage - 1;
1745 else if(strstr(s, "last") || strstr(s, "end"))
1747 if(this->page2page && this->num_pages) {
1748 page = this->page2page[this->num_pages-1];
1751 else if(strstr(s, "first") || strstr(s, "top"))
1759 case actionLaunch: {
1761 LinkLaunch*l = (LinkLaunch*)action;
1762 GString * str = new GString(l->getFileName());
1763 GString * params = l->getParams();
1765 str->append(params);
1766 s = strdup(str->getCString());
1773 LinkURI*l = (LinkURI*)action;
1774 GString*g = l->getURI();
1776 url = g->getCString();
1781 case actionUnknown: {
1783 LinkUnknown*l = (LinkUnknown*)action;
1788 msg("<error> Unknown link type!");
1793 if(!s) s = strdup("-?-");
1795 msg("<trace> drawlink s=%s", s);
1797 if(!gfxglobals->linkinfo && (page || s))
1799 msg("<notice> File contains links");
1800 gfxglobals->linkinfo = 1;
1806 for(t=1;t<=this->num_pages;t++) {
1807 if(this->page2page[t]==page) {
1817 sprintf(buf, "page%d", lpage);
1818 device->drawlink(device, points, buf);
1822 device->drawlink(device, points, s);
1825 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1829 void GFXOutputDev::saveState(GfxState *state) {
1830 dbg("saveState %08x", state); dbgindent+=2;
1832 msg("<trace> saveState %08x", state);
1835 msg("<fatal> Too many nested states in pdf.");
1839 states[statepos].state = state;
1840 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1841 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1842 states[statepos].clipping = 0;
1843 states[statepos].olddevice = 0;
1844 states[statepos].clipbbox = states[statepos-1].clipbbox;
1846 states[statepos].dashPattern = states[statepos-1].dashPattern;
1847 states[statepos].dashStart = states[statepos-1].dashStart;
1848 states[statepos].dashLength = states[statepos-1].dashLength;
1851 void GFXOutputDev::restoreState(GfxState *state) {
1852 dbgindent-=2; dbg("restoreState %08x", state);
1855 msg("<fatal> Invalid restoreState");
1858 msg("<trace> restoreState %08x%s%s", state,
1859 states[statepos].softmask?" (end softmask)":"",
1860 states[statepos].clipping?" (end clipping)":"");
1861 if(states[statepos].softmask) {
1862 clearSoftMask(state);
1865 if(states[statepos].dashPattern) {
1866 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1867 free(states[statepos].dashPattern);
1868 states[statepos].dashPattern = 0;
1874 while(states[statepos].clipping) {
1875 device->endclip(device);
1876 states[statepos].clipping--;
1878 if(states[statepos].state!=state) {
1879 msg("<fatal> bad state nesting");
1882 for(t=0;t<=statepos;t++) {
1883 printf("%08x ", states[t].state);
1889 states[statepos].state=0;
1893 void GFXOutputDev::updateLineDash(GfxState *state)
1895 if(states[statepos].dashPattern &&
1896 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1897 free(states[statepos].dashPattern);
1898 states[statepos].dashPattern = 0;
1900 double *pattern = 0;
1903 state->getLineDash(&pattern, &dashLength, &dashStart);
1904 msg("<debug> updateLineDash, %d dashes", dashLength);
1906 states[statepos].dashPattern = 0;
1907 states[statepos].dashLength = 0;
1909 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1910 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1911 states[statepos].dashPattern = p;
1912 states[statepos].dashLength = dashLength;
1913 states[statepos].dashStart = dashStart;
1917 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1919 this->page2page = page2page;
1920 this->num_pages = num_pages;
1923 void GFXOutputDev::updateLineWidth(GfxState *state)
1925 double width = state->getTransformedLineWidth();
1928 void GFXOutputDev::updateLineCap(GfxState *state)
1930 int c = state->getLineCap();
1933 void GFXOutputDev::updateLineJoin(GfxState *state)
1935 int j = state->getLineJoin();
1938 void GFXOutputDev::updateFillColor(GfxState *state)
1941 double opaq = state->getFillOpacity();
1942 state->getFillRGB(&rgb);
1944 void GFXOutputDev::updateFillOpacity(GfxState *state)
1947 double opaq = state->getFillOpacity();
1948 state->getFillRGB(&rgb);
1949 dbg("update fillopaq %f", opaq);
1951 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1953 double opaq = state->getFillOpacity();
1954 dbg("update strokeopaq %f", opaq);
1956 void GFXOutputDev::updateFillOverprint(GfxState *state)
1958 double opaq = state->getFillOverprint();
1959 dbg("update filloverprint %f", opaq);
1961 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1963 double opaq = state->getStrokeOverprint();
1964 dbg("update strokeoverprint %f", opaq);
1966 void GFXOutputDev::updateTransfer(GfxState *state)
1968 dbg("update transfer");
1972 void GFXOutputDev::updateStrokeColor(GfxState *state)
1975 double opaq = state->getStrokeOpacity();
1976 state->getStrokeRGB(&rgb);
1979 void GFXOutputDev::updateFont(GfxState *state)
1981 GfxFont* gfxFont = state->getFont();
1985 char*id = getFontID(gfxFont);
1986 msg("<verbose> Updating font to %s", id);
1987 if(gfxFont->getType() == fontType3) {
1988 infofeature("Type3 fonts");
1989 if(!config_extrafontdata) {
1994 msg("<error> Internal Error: FontID is null");
1998 this->current_fontinfo = this->info->getFont(id);
2000 if(!this->current_fontinfo) {
2001 msg("<error> Internal Error: no fontinfo for font %s", id);
2004 if(!this->current_fontinfo->seen) {
2005 dumpFontInfo("<verbose>", gfxFont);
2008 current_gfxfont = this->current_fontinfo->getGfxFont();
2009 device->addfont(device, current_gfxfont);
2012 updateFontMatrix(state);
2015 #define SQR(x) ((x)*(x))
2017 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2019 if((newwidth<1 || newheight<1) ||
2020 (width<=newwidth || height<=newheight))
2022 unsigned char*newdata;
2024 newdata= (unsigned char*)malloc(newwidth*newheight);
2025 double fx = ((double)width)/newwidth;
2026 double fy = ((double)height)/newheight;
2028 int blocksize = (int)(8192/(fx*fy));
2029 int r = 8192*256/palettesize;
2030 for(x=0;x<newwidth;x++) {
2031 double ex = px + fx;
2032 int fromx = (int)px;
2034 int xweight1 = (int)((1-(px-fromx))*256);
2035 int xweight2 = (int)((ex-tox)*256);
2037 for(y=0;y<newheight;y++) {
2038 double ey = py + fy;
2039 int fromy = (int)py;
2041 int yweight1 = (int)((1-(py-fromy))*256);
2042 int yweight2 = (int)((ey-toy)*256);
2049 for(xx=fromx;xx<=tox;xx++)
2050 for(yy=fromy;yy<=toy;yy++) {
2051 int b = 1-data[width*yy+xx];
2053 if(xx==fromx) weight = (weight*xweight1)/256;
2054 if(xx==tox) weight = (weight*xweight2)/256;
2055 if(yy==fromy) weight = (weight*yweight1)/256;
2056 if(yy==toy) weight = (weight*yweight2)/256;
2059 //if(a) a=(palettesize-1)*r/blocksize;
2060 newdata[y*newwidth+x] = (a*blocksize)/r;
2068 #define IMAGE_TYPE_JPEG 0
2069 #define IMAGE_TYPE_LOSSLESS 1
2071 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2072 double x1,double y1,
2073 double x2,double y2,
2074 double x3,double y3,
2075 double x4,double y4, int type, int multiply)
2077 gfxcolor_t*newpic=0;
2079 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2080 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2082 gfxline_t p1,p2,p3,p4,p5;
2083 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2084 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2085 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2086 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2087 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2089 {p1.x = (int)(p1.x*20)/20.0;
2090 p1.y = (int)(p1.y*20)/20.0;
2091 p2.x = (int)(p2.x*20)/20.0;
2092 p2.y = (int)(p2.y*20)/20.0;
2093 p3.x = (int)(p3.x*20)/20.0;
2094 p3.y = (int)(p3.y*20)/20.0;
2095 p4.x = (int)(p4.x*20)/20.0;
2096 p4.y = (int)(p4.y*20)/20.0;
2097 p5.x = (int)(p5.x*20)/20.0;
2098 p5.y = (int)(p5.y*20)/20.0;
2102 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2103 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2105 m.tx = p1.x - 0.5*multiply;
2106 m.ty = p1.y - 0.5*multiply;
2109 img.data = (gfxcolor_t*)data;
2113 if(type == IMAGE_TYPE_JPEG)
2114 /* TODO: pass image_dpi to device instead */
2115 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2118 dev->fillbitmap(dev, &p1, &img, &m, 0);
2121 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2122 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2124 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2127 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2128 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2130 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2134 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2135 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2136 GBool inlineImg, int mask, int*maskColors,
2137 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2139 /* the code in this function is *old*. It's not pretty, but it works. */
2141 double x1,y1,x2,y2,x3,y3,x4,y4;
2142 ImageStream *imgStr;
2147 unsigned char* maskbitmap = 0;
2150 ncomps = colorMap->getNumPixelComps();
2151 bits = colorMap->getBits();
2156 unsigned char buf[8];
2157 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2159 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2160 imgMaskStr->reset();
2161 unsigned char pal[256];
2162 int n = 1 << colorMap->getBits();
2167 maskColorMap->getGray(pixBuf, &gray);
2168 pal[t] = colToByte(gray);
2170 for (y = 0; y < maskHeight; y++) {
2171 for (x = 0; x < maskWidth; x++) {
2172 imgMaskStr->getPixel(buf);
2173 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2178 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2179 imgMaskStr->reset();
2180 for (y = 0; y < maskHeight; y++) {
2181 for (x = 0; x < maskWidth; x++) {
2182 imgMaskStr->getPixel(buf);
2184 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2192 imgStr = new ImageStream(str, width, ncomps,bits);
2195 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2197 msg("<verbose> Ignoring %d by %d image", width, height);
2198 unsigned char buf[8];
2200 for (y = 0; y < height; ++y)
2201 for (x = 0; x < width; ++x) {
2202 imgStr->getPixel(buf);
2210 this->transformXY(state, 0, 1, &x1, &y1);
2211 this->transformXY(state, 0, 0, &x2, &y2);
2212 this->transformXY(state, 1, 0, &x3, &y3);
2213 this->transformXY(state, 1, 1, &x4, &y4);
2216 /* as type 3 bitmaps are antialized, we need to place them
2217 at integer coordinates, otherwise flash player's antializing
2218 will kick in and make everything blurry */
2219 x1 = (int)(x1);y1 = (int)(y1);
2220 x2 = (int)(x2);y2 = (int)(y2);
2221 x3 = (int)(x3);y3 = (int)(y3);
2222 x4 = (int)(x4);y4 = (int)(y4);
2225 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2227 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2228 gfxglobals->pbminfo = 1;
2231 msg("<verbose> drawing %d by %d masked picture", width, height);
2233 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2234 msg("<notice> File contains jpeg pictures");
2235 gfxglobals->jpeginfo = 1;
2239 unsigned char buf[8];
2241 unsigned char*pic = new unsigned char[width*height];
2242 gfxcolor_t pal[256];
2244 state->getFillRGB(&rgb);
2246 memset(pal,255,sizeof(pal));
2247 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2248 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2249 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2250 pal[0].a = 255; pal[1].a = 0;
2253 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2254 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2255 for (y = 0; y < height; ++y)
2256 for (x = 0; x < width; ++x)
2258 imgStr->getPixel(buf);
2261 pic[width*y+x] = buf[0];
2265 unsigned char*pic2 = 0;
2268 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2277 height = realheight;
2281 /* make a black/white palette */
2283 float r = 255./(float)(numpalette-1);
2285 for(t=0;t<numpalette;t++) {
2286 pal[t].r = colToByte(rgb.r);
2287 pal[t].g = colToByte(rgb.g);
2288 pal[t].b = colToByte(rgb.b);
2289 pal[t].a = (unsigned char)(t*r);
2294 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2295 for (y = 0; y < height; ++y) {
2296 for (x = 0; x < width; ++x) {
2297 pic2[width*y+x] = pal[pic[y*width+x]];
2300 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2304 if(maskbitmap) free(maskbitmap);
2310 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2311 gfxcolor_t*pic=new gfxcolor_t[width*height];
2312 for (y = 0; y < height; ++y) {
2313 for (x = 0; x < width; ++x) {
2314 imgStr->getPixel(pixBuf);
2315 colorMap->getRGB(pixBuf, &rgb);
2316 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2317 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2318 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2319 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2321 int x1 = x*maskWidth/width;
2322 int y1 = y*maskHeight/height;
2323 int x2 = (x+1)*maskWidth/width;
2324 int y2 = (y+1)*maskHeight/height;
2326 unsigned int alpha=0;
2327 unsigned int count=0;
2328 for(xx=x1;xx<x2;xx++)
2329 for(yy=y1;yy<y2;yy++) {
2330 alpha += maskbitmap[yy*maskWidth+xx];
2334 pic[width*y+x].a = alpha / count;
2336 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2341 if(str->getKind()==strDCT)
2342 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2344 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2347 if(maskbitmap) free(maskbitmap);
2350 gfxcolor_t*pic=new gfxcolor_t[width*height];
2351 gfxcolor_t pal[256];
2352 int n = 1 << colorMap->getBits();
2354 for(t=0;t<256;t++) {
2356 colorMap->getRGB(pixBuf, &rgb);
2358 {/*if(maskColors && *maskColors==t) {
2359 msg("<notice> Color %d is transparent", t);
2360 if (imgData->maskColors) {
2362 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2363 if (pix[i] < imgData->maskColors[2*i] ||
2364 pix[i] > imgData->maskColors[2*i+1]) {
2379 pal[t].r = (unsigned char)(colToByte(rgb.r));
2380 pal[t].g = (unsigned char)(colToByte(rgb.g));
2381 pal[t].b = (unsigned char)(colToByte(rgb.b));
2382 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2385 for (y = 0; y < height; ++y) {
2386 for (x = 0; x < width; ++x) {
2387 imgStr->getPixel(pixBuf);
2388 pic[width*y+x] = pal[pixBuf[0]];
2392 if(maskWidth < width && maskHeight < height) {
2393 for(y = 0; y < height; y++) {
2394 for (x = 0; x < width; x++) {
2395 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2399 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2400 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2401 double dx = width / maskWidth;
2402 double dy = height / maskHeight;
2404 for(y = 0; y < maskHeight; y++) {
2406 for (x = 0; x < maskWidth; x++) {
2407 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2408 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2416 height = maskHeight;
2419 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2423 if(maskbitmap) free(maskbitmap);
2428 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2429 int width, int height, GBool invert,
2432 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2433 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2434 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2437 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2438 int width, int height, GfxImageColorMap *colorMap,
2439 int *maskColors, GBool inlineImg)
2441 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2442 colorMap?"colorMap":"no colorMap",
2443 maskColors?"maskColors":"no maskColors",
2445 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2446 colorMap?"colorMap":"no colorMap",
2447 maskColors?"maskColors":"no maskColors",
2450 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2451 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2452 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2455 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2456 int width, int height,
2457 GfxImageColorMap *colorMap,
2458 Stream *maskStr, int maskWidth, int maskHeight,
2461 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2462 colorMap?"colorMap":"no colorMap",
2463 maskWidth, maskHeight);
2464 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2465 colorMap?"colorMap":"no colorMap",
2466 maskWidth, maskHeight);
2468 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2469 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2470 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2473 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2474 int width, int height,
2475 GfxImageColorMap *colorMap,
2477 int maskWidth, int maskHeight,
2478 GfxImageColorMap *maskColorMap)
2480 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2481 colorMap?"colorMap":"no colorMap",
2482 maskWidth, maskHeight);
2483 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2484 colorMap?"colorMap":"no colorMap",
2485 maskWidth, maskHeight);
2487 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2488 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2489 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2492 void GFXOutputDev::stroke(GfxState *state)
2496 GfxPath * path = state->getPath();
2497 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2498 strokeGfxline(state, line, 0);
2502 void GFXOutputDev::fill(GfxState *state)
2504 gfxcolor_t col = getFillColor(state);
2505 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2507 GfxPath * path = state->getPath();
2508 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2509 if(!config_disable_polygon_conversion) {
2510 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2514 fillGfxLine(state, line, 0);
2518 void GFXOutputDev::eoFill(GfxState *state)
2520 gfxcolor_t col = getFillColor(state);
2521 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2523 GfxPath * path = state->getPath();
2524 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2525 fillGfxLine(state, line, 1);
2530 static const char* dirseparator()
2539 void addGlobalFont(const char*filename)
2541 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2542 memset(f, 0, sizeof(fontfile_t));
2543 f->filename = filename;
2544 int len = strlen(filename);
2545 char*r1 = strrchr((char*)filename, '/');
2546 char*r2 = strrchr((char*)filename, '\\');
2554 msg("<verbose> Adding font \"%s\".", filename);
2555 if(global_fonts_next) {
2556 global_fonts_next->next = f;
2557 global_fonts_next = global_fonts_next->next;
2559 global_fonts_next = global_fonts = f;
2563 void addGlobalLanguageDir(const char*dir)
2565 msg("<notice> Adding %s to language pack directories", dir);
2568 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2569 strcpy(config_file, dir);
2570 strcat(config_file, dirseparator());
2571 strcat(config_file, "add-to-xpdfrc");
2573 fi = fopen(config_file, "rb");
2575 msg("<error> Could not open %s", config_file);
2578 globalParams->parseFile(new GString(config_file), fi);
2582 void addGlobalFontDir(const char*dirname)
2584 #ifdef HAVE_DIRENT_H
2585 DIR*dir = opendir(dirname);
2587 msg("<warning> Couldn't open directory %s", dirname);
2593 ent = readdir (dir);
2597 char*name = ent->d_name;
2603 if(!strncasecmp(&name[l-4], ".pfa", 4))
2605 if(!strncasecmp(&name[l-4], ".pfb", 4))
2607 if(!strncasecmp(&name[l-4], ".ttf", 4))
2610 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2611 strcpy(fontname, dirname);
2612 strcat(fontname, dirseparator());
2613 strcat(fontname, name);
2614 addGlobalFont(fontname);
2618 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2621 msg("<warning> No dirent.h");
2625 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2626 GfxColorSpace *blendingColorSpace,
2627 GBool isolated, GBool knockout,
2630 const char*colormodename = "";
2632 if(blendingColorSpace) {
2633 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2635 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);
2636 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);
2638 //states[statepos].createsoftmask |= forSoftMask;
2639 states[statepos].createsoftmask = forSoftMask;
2640 states[statepos].transparencygroup = !forSoftMask;
2641 states[statepos].isolated = isolated;
2643 states[statepos].olddevice = this->device;
2644 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2645 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2647 gfxdevice_record_init(this->device);
2649 /*if(!forSoftMask) { ////???
2650 state->setFillOpacity(0.0);
2655 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2658 gfxdevice_t*r = this->device;
2660 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2662 this->device = states[statepos].olddevice;
2664 msg("<error> Invalid state nesting");
2666 states[statepos].olddevice = 0;
2668 gfxresult_t*recording = r->finish(r);
2670 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2671 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2673 if(states[statepos].createsoftmask) {
2674 states[statepos-1].softmaskrecording = recording;
2676 states[statepos-1].grouprecording = recording;
2679 states[statepos].createsoftmask = 0;
2680 states[statepos].transparencygroup = 0;
2684 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2686 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2687 "colordodge","colorburn","hardlight","softlight","difference",
2688 "exclusion","hue","saturation","color","luminosity"};
2690 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2691 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2693 if(state->getBlendMode() == gfxBlendNormal)
2694 infofeature("transparency groups");
2697 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2698 warnfeature(buffer, 0);
2701 gfxresult_t*grouprecording = states[statepos].grouprecording;
2703 int blendmode = state->getBlendMode();
2704 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2705 int alpha = (int)(state->getFillOpacity()*255);
2706 if(blendmode == gfxBlendMultiply && alpha>200)
2709 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2710 gfxdevice_ops_init(&ops, this->device, alpha);
2711 gfxresult_record_replay(grouprecording, &ops);
2714 grouprecording->destroy(grouprecording);
2716 states[statepos].grouprecording = 0;
2719 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2721 if(states[statepos].softmask) {
2722 /* shouldn't happen, but *does* happen */
2723 clearSoftMask(state);
2726 /* alpha = 1: retrieve mask values from alpha layer
2727 alpha = 0: retrieve mask values from luminance */
2729 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2730 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2731 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2732 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2734 infofeature("soft masks");
2736 warnfeature("soft masks from alpha channel",0);
2738 if(states[statepos].olddevice) {
2739 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2742 states[statepos].olddevice = this->device;
2743 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2744 gfxdevice_record_init(this->device);
2746 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2748 states[statepos].softmask = 1;
2749 states[statepos].softmask_alpha = alpha;
2752 static inline Guchar div255(int x) {
2753 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2756 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2758 if(c < min) c = min;
2759 if(c > max) c = max;
2763 void GFXOutputDev::clearSoftMask(GfxState *state)
2765 if(!states[statepos].softmask)
2767 states[statepos].softmask = 0;
2768 dbg("clearSoftMask statepos=%d", statepos);
2769 msg("<verbose> clearSoftMask statepos=%d", statepos);
2771 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2772 msg("<error> Error in softmask/tgroup ordering");
2776 gfxresult_t*mask = states[statepos].softmaskrecording;
2777 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2778 this->device = states[statepos].olddevice;
2780 /* get outline of all objects below the soft mask */
2781 gfxdevice_t uniondev;
2782 gfxdevice_union_init(&uniondev, 0);
2783 gfxresult_record_replay(below, &uniondev);
2784 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2785 uniondev.finish(&uniondev);
2786 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2787 gfxline_free(belowoutline);belowoutline=0;
2789 this->device->startclip(this->device, belowoutline);
2790 gfxresult_record_replay(below, this->device);
2791 gfxresult_record_replay(mask, this->device);
2792 this->device->endclip(this->device);
2795 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2796 if(width<=0 || height<=0)
2799 gfxdevice_t belowrender;
2800 gfxdevice_render_init(&belowrender);
2801 if(states[statepos+1].isolated) {
2802 belowrender.setparameter(&belowrender, "fillwhite", "1");
2804 belowrender.setparameter(&belowrender, "antialize", "2");
2805 belowrender.startpage(&belowrender, width, height);
2806 gfxresult_record_replay(below, &belowrender);
2807 belowrender.endpage(&belowrender);
2808 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2809 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2810 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2812 gfxdevice_t maskrender;
2813 gfxdevice_render_init(&maskrender);
2814 maskrender.startpage(&maskrender, width, height);
2815 gfxresult_record_replay(mask, &maskrender);
2816 maskrender.endpage(&maskrender);
2817 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2818 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2820 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2821 msg("<fatal> Internal error in mask drawing");
2826 for(y=0;y<height;y++) {
2827 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2828 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2829 for(x=0;x<width;x++) {
2831 if(states[statepos].softmask_alpha) {
2834 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2837 l2->a = div255(alpha*l2->a);
2839 /* DON'T premultiply alpha- this is done by fillbitmap,
2840 depending on the output device */
2841 //l2->r = div255(alpha*l2->r);
2842 //l2->g = div255(alpha*l2->g);
2843 //l2->b = div255(alpha*l2->b);
2849 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2852 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2853 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2855 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2857 mask->destroy(mask);
2858 below->destroy(below);
2859 maskresult->destroy(maskresult);
2860 belowresult->destroy(belowresult);
2861 states[statepos].softmaskrecording = 0;
2866 // public: ~MemCheck()
2868 // delete globalParams;globalParams=0;
2869 // Object::memCheck(stderr);
2870 // gMemReport(stderr);