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 states[statepos].state=0;
1886 void GFXOutputDev::updateLineDash(GfxState *state)
1888 if(states[statepos].dashPattern &&
1889 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1890 free(states[statepos].dashPattern);
1891 states[statepos].dashPattern = 0;
1893 double *pattern = 0;
1896 state->getLineDash(&pattern, &dashLength, &dashStart);
1897 msg("<debug> updateLineDash, %d dashes", dashLength);
1899 states[statepos].dashPattern = 0;
1900 states[statepos].dashLength = 0;
1902 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1903 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1904 states[statepos].dashPattern = p;
1905 states[statepos].dashLength = dashLength;
1906 states[statepos].dashStart = dashStart;
1910 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1912 this->page2page = page2page;
1913 this->num_pages = num_pages;
1916 void GFXOutputDev::updateLineWidth(GfxState *state)
1918 double width = state->getTransformedLineWidth();
1921 void GFXOutputDev::updateLineCap(GfxState *state)
1923 int c = state->getLineCap();
1926 void GFXOutputDev::updateLineJoin(GfxState *state)
1928 int j = state->getLineJoin();
1931 void GFXOutputDev::updateFillColor(GfxState *state)
1934 double opaq = state->getFillOpacity();
1935 state->getFillRGB(&rgb);
1937 void GFXOutputDev::updateFillOpacity(GfxState *state)
1940 double opaq = state->getFillOpacity();
1941 state->getFillRGB(&rgb);
1942 dbg("update fillopaq %f", opaq);
1944 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1946 double opaq = state->getFillOpacity();
1947 dbg("update strokeopaq %f", opaq);
1949 void GFXOutputDev::updateFillOverprint(GfxState *state)
1951 double opaq = state->getFillOverprint();
1952 dbg("update filloverprint %f", opaq);
1954 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1956 double opaq = state->getStrokeOverprint();
1957 dbg("update strokeoverprint %f", opaq);
1959 void GFXOutputDev::updateTransfer(GfxState *state)
1961 dbg("update transfer");
1965 void GFXOutputDev::updateStrokeColor(GfxState *state)
1968 double opaq = state->getStrokeOpacity();
1969 state->getStrokeRGB(&rgb);
1972 void GFXOutputDev::updateFont(GfxState *state)
1974 GfxFont* gfxFont = state->getFont();
1978 char*id = getFontID(gfxFont);
1979 msg("<verbose> Updating font to %s", id);
1980 if(gfxFont->getType() == fontType3) {
1981 infofeature("Type3 fonts");
1982 if(!config_extrafontdata) {
1987 msg("<error> Internal Error: FontID is null");
1991 this->current_fontinfo = this->info->getFont(id);
1993 if(!this->current_fontinfo) {
1994 msg("<error> Internal Error: no fontinfo for font %s", id);
1997 if(!this->current_fontinfo->seen) {
1998 dumpFontInfo("<verbose>", gfxFont);
2001 current_gfxfont = this->current_fontinfo->getGfxFont();
2002 device->addfont(device, current_gfxfont);
2005 updateFontMatrix(state);
2008 #define SQR(x) ((x)*(x))
2010 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2012 if((newwidth<1 || newheight<1) ||
2013 (width<=newwidth || height<=newheight))
2015 unsigned char*newdata;
2017 newdata= (unsigned char*)malloc(newwidth*newheight);
2018 double fx = ((double)width)/newwidth;
2019 double fy = ((double)height)/newheight;
2021 int blocksize = (int)(8192/(fx*fy));
2022 int r = 8192*256/palettesize;
2023 for(x=0;x<newwidth;x++) {
2024 double ex = px + fx;
2025 int fromx = (int)px;
2027 int xweight1 = (int)((1-(px-fromx))*256);
2028 int xweight2 = (int)((ex-tox)*256);
2030 for(y=0;y<newheight;y++) {
2031 double ey = py + fy;
2032 int fromy = (int)py;
2034 int yweight1 = (int)((1-(py-fromy))*256);
2035 int yweight2 = (int)((ey-toy)*256);
2042 for(xx=fromx;xx<=tox;xx++)
2043 for(yy=fromy;yy<=toy;yy++) {
2044 int b = 1-data[width*yy+xx];
2046 if(xx==fromx) weight = (weight*xweight1)/256;
2047 if(xx==tox) weight = (weight*xweight2)/256;
2048 if(yy==fromy) weight = (weight*yweight1)/256;
2049 if(yy==toy) weight = (weight*yweight2)/256;
2052 //if(a) a=(palettesize-1)*r/blocksize;
2053 newdata[y*newwidth+x] = (a*blocksize)/r;
2061 #define IMAGE_TYPE_JPEG 0
2062 #define IMAGE_TYPE_LOSSLESS 1
2064 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2065 double x1,double y1,
2066 double x2,double y2,
2067 double x3,double y3,
2068 double x4,double y4, int type, int multiply)
2070 gfxcolor_t*newpic=0;
2072 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2073 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2075 gfxline_t p1,p2,p3,p4,p5;
2076 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2077 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2078 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2079 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2080 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2082 {p1.x = (int)(p1.x*20)/20.0;
2083 p1.y = (int)(p1.y*20)/20.0;
2084 p2.x = (int)(p2.x*20)/20.0;
2085 p2.y = (int)(p2.y*20)/20.0;
2086 p3.x = (int)(p3.x*20)/20.0;
2087 p3.y = (int)(p3.y*20)/20.0;
2088 p4.x = (int)(p4.x*20)/20.0;
2089 p4.y = (int)(p4.y*20)/20.0;
2090 p5.x = (int)(p5.x*20)/20.0;
2091 p5.y = (int)(p5.y*20)/20.0;
2095 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2096 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2098 m.tx = p1.x - 0.5*multiply;
2099 m.ty = p1.y - 0.5*multiply;
2102 img.data = (gfxcolor_t*)data;
2106 if(type == IMAGE_TYPE_JPEG)
2107 /* TODO: pass image_dpi to device instead */
2108 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2111 dev->fillbitmap(dev, &p1, &img, &m, 0);
2114 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2115 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2117 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2120 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2121 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2123 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2127 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2128 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2129 GBool inlineImg, int mask, int*maskColors,
2130 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2132 /* the code in this function is *old*. It's not pretty, but it works. */
2134 double x1,y1,x2,y2,x3,y3,x4,y4;
2135 ImageStream *imgStr;
2140 unsigned char* maskbitmap = 0;
2143 ncomps = colorMap->getNumPixelComps();
2144 bits = colorMap->getBits();
2149 unsigned char buf[8];
2150 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2152 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2153 imgMaskStr->reset();
2154 unsigned char pal[256];
2155 int n = 1 << colorMap->getBits();
2160 maskColorMap->getGray(pixBuf, &gray);
2161 pal[t] = colToByte(gray);
2163 for (y = 0; y < maskHeight; y++) {
2164 for (x = 0; x < maskWidth; x++) {
2165 imgMaskStr->getPixel(buf);
2166 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2171 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2172 imgMaskStr->reset();
2173 for (y = 0; y < maskHeight; y++) {
2174 for (x = 0; x < maskWidth; x++) {
2175 imgMaskStr->getPixel(buf);
2177 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2185 imgStr = new ImageStream(str, width, ncomps,bits);
2188 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2190 msg("<verbose> Ignoring %d by %d image", width, height);
2191 unsigned char buf[8];
2193 for (y = 0; y < height; ++y)
2194 for (x = 0; x < width; ++x) {
2195 imgStr->getPixel(buf);
2203 this->transformXY(state, 0, 1, &x1, &y1);
2204 this->transformXY(state, 0, 0, &x2, &y2);
2205 this->transformXY(state, 1, 0, &x3, &y3);
2206 this->transformXY(state, 1, 1, &x4, &y4);
2209 /* as type 3 bitmaps are antialized, we need to place them
2210 at integer coordinates, otherwise flash player's antializing
2211 will kick in and make everything blurry */
2212 x1 = (int)(x1);y1 = (int)(y1);
2213 x2 = (int)(x2);y2 = (int)(y2);
2214 x3 = (int)(x3);y3 = (int)(y3);
2215 x4 = (int)(x4);y4 = (int)(y4);
2218 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2220 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2221 gfxglobals->pbminfo = 1;
2224 msg("<verbose> drawing %d by %d masked picture", width, height);
2226 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2227 msg("<notice> File contains jpeg pictures");
2228 gfxglobals->jpeginfo = 1;
2232 unsigned char buf[8];
2234 unsigned char*pic = new unsigned char[width*height];
2235 gfxcolor_t pal[256];
2237 state->getFillRGB(&rgb);
2239 memset(pal,255,sizeof(pal));
2240 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2241 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2242 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2243 pal[0].a = 255; pal[1].a = 0;
2246 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2247 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2248 for (y = 0; y < height; ++y)
2249 for (x = 0; x < width; ++x)
2251 imgStr->getPixel(buf);
2254 pic[width*y+x] = buf[0];
2258 unsigned char*pic2 = 0;
2261 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2270 height = realheight;
2274 /* make a black/white palette */
2276 float r = 255./(float)(numpalette-1);
2278 for(t=0;t<numpalette;t++) {
2279 pal[t].r = colToByte(rgb.r);
2280 pal[t].g = colToByte(rgb.g);
2281 pal[t].b = colToByte(rgb.b);
2282 pal[t].a = (unsigned char)(t*r);
2287 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2288 for (y = 0; y < height; ++y) {
2289 for (x = 0; x < width; ++x) {
2290 pic2[width*y+x] = pal[pic[y*width+x]];
2293 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2297 if(maskbitmap) free(maskbitmap);
2303 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2304 gfxcolor_t*pic=new gfxcolor_t[width*height];
2305 for (y = 0; y < height; ++y) {
2306 for (x = 0; x < width; ++x) {
2307 imgStr->getPixel(pixBuf);
2308 colorMap->getRGB(pixBuf, &rgb);
2309 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2310 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2311 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2312 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2314 int x1 = x*maskWidth/width;
2315 int y1 = y*maskHeight/height;
2316 int x2 = (x+1)*maskWidth/width;
2317 int y2 = (y+1)*maskHeight/height;
2319 unsigned int alpha=0;
2320 unsigned int count=0;
2321 for(xx=x1;xx<x2;xx++)
2322 for(yy=y1;yy<y2;yy++) {
2323 alpha += maskbitmap[yy*maskWidth+xx];
2327 pic[width*y+x].a = alpha / count;
2329 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2334 if(str->getKind()==strDCT)
2335 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2337 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2340 if(maskbitmap) free(maskbitmap);
2343 gfxcolor_t*pic=new gfxcolor_t[width*height];
2344 gfxcolor_t pal[256];
2345 int n = 1 << colorMap->getBits();
2347 for(t=0;t<256;t++) {
2349 colorMap->getRGB(pixBuf, &rgb);
2351 {/*if(maskColors && *maskColors==t) {
2352 msg("<notice> Color %d is transparent", t);
2353 if (imgData->maskColors) {
2355 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2356 if (pix[i] < imgData->maskColors[2*i] ||
2357 pix[i] > imgData->maskColors[2*i+1]) {
2372 pal[t].r = (unsigned char)(colToByte(rgb.r));
2373 pal[t].g = (unsigned char)(colToByte(rgb.g));
2374 pal[t].b = (unsigned char)(colToByte(rgb.b));
2375 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2378 for (y = 0; y < height; ++y) {
2379 for (x = 0; x < width; ++x) {
2380 imgStr->getPixel(pixBuf);
2381 pic[width*y+x] = pal[pixBuf[0]];
2385 if(maskWidth < width && maskHeight < height) {
2386 for(y = 0; y < height; y++) {
2387 for (x = 0; x < width; x++) {
2388 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2392 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2393 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2394 double dx = width / maskWidth;
2395 double dy = height / maskHeight;
2397 for(y = 0; y < maskHeight; y++) {
2399 for (x = 0; x < maskWidth; x++) {
2400 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2401 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2409 height = maskHeight;
2412 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2416 if(maskbitmap) free(maskbitmap);
2421 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2422 int width, int height, GBool invert,
2425 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2426 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2427 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2430 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2431 int width, int height, GfxImageColorMap *colorMap,
2432 int *maskColors, GBool inlineImg)
2434 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2435 colorMap?"colorMap":"no colorMap",
2436 maskColors?"maskColors":"no maskColors",
2438 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2439 colorMap?"colorMap":"no colorMap",
2440 maskColors?"maskColors":"no maskColors",
2443 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2444 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2445 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2448 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2449 int width, int height,
2450 GfxImageColorMap *colorMap,
2451 Stream *maskStr, int maskWidth, int maskHeight,
2454 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2455 colorMap?"colorMap":"no colorMap",
2456 maskWidth, maskHeight);
2457 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2458 colorMap?"colorMap":"no colorMap",
2459 maskWidth, maskHeight);
2461 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2462 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2463 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2466 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2467 int width, int height,
2468 GfxImageColorMap *colorMap,
2470 int maskWidth, int maskHeight,
2471 GfxImageColorMap *maskColorMap)
2473 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2474 colorMap?"colorMap":"no colorMap",
2475 maskWidth, maskHeight);
2476 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2477 colorMap?"colorMap":"no colorMap",
2478 maskWidth, maskHeight);
2480 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2481 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2482 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2485 void GFXOutputDev::stroke(GfxState *state)
2489 GfxPath * path = state->getPath();
2490 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2491 strokeGfxline(state, line, 0);
2495 void GFXOutputDev::fill(GfxState *state)
2497 gfxcolor_t col = getFillColor(state);
2498 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2500 GfxPath * path = state->getPath();
2501 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2502 if(!config_disable_polygon_conversion) {
2503 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2507 fillGfxLine(state, line, 0);
2511 void GFXOutputDev::eoFill(GfxState *state)
2513 gfxcolor_t col = getFillColor(state);
2514 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2516 GfxPath * path = state->getPath();
2517 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2518 fillGfxLine(state, line, 1);
2523 static const char* dirseparator()
2532 void addGlobalFont(const char*filename)
2534 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2535 memset(f, 0, sizeof(fontfile_t));
2536 f->filename = filename;
2537 int len = strlen(filename);
2538 char*r1 = strrchr((char*)filename, '/');
2539 char*r2 = strrchr((char*)filename, '\\');
2547 msg("<verbose> Adding font \"%s\".", filename);
2548 if(global_fonts_next) {
2549 global_fonts_next->next = f;
2550 global_fonts_next = global_fonts_next->next;
2552 global_fonts_next = global_fonts = f;
2556 void addGlobalLanguageDir(const char*dir)
2558 msg("<notice> Adding %s to language pack directories", dir);
2561 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2562 strcpy(config_file, dir);
2563 strcat(config_file, dirseparator());
2564 strcat(config_file, "add-to-xpdfrc");
2566 fi = fopen(config_file, "rb");
2568 msg("<error> Could not open %s", config_file);
2571 globalParams->parseFile(new GString(config_file), fi);
2575 void addGlobalFontDir(const char*dirname)
2577 #ifdef HAVE_DIRENT_H
2578 DIR*dir = opendir(dirname);
2580 msg("<warning> Couldn't open directory %s", dirname);
2586 ent = readdir (dir);
2590 char*name = ent->d_name;
2596 if(!strncasecmp(&name[l-4], ".pfa", 4))
2598 if(!strncasecmp(&name[l-4], ".pfb", 4))
2600 if(!strncasecmp(&name[l-4], ".ttf", 4))
2603 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2604 strcpy(fontname, dirname);
2605 strcat(fontname, dirseparator());
2606 strcat(fontname, name);
2607 addGlobalFont(fontname);
2611 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2614 msg("<warning> No dirent.h");
2618 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2619 GfxColorSpace *blendingColorSpace,
2620 GBool isolated, GBool knockout,
2623 const char*colormodename = "";
2625 if(blendingColorSpace) {
2626 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2628 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);
2629 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);
2631 //states[statepos].createsoftmask |= forSoftMask;
2632 states[statepos].createsoftmask = forSoftMask;
2633 states[statepos].transparencygroup = !forSoftMask;
2634 states[statepos].isolated = isolated;
2636 states[statepos].olddevice = this->device;
2637 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2638 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2640 gfxdevice_record_init(this->device);
2642 /*if(!forSoftMask) { ////???
2643 state->setFillOpacity(0.0);
2648 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2651 gfxdevice_t*r = this->device;
2653 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2655 this->device = states[statepos].olddevice;
2657 msg("<fatal> Bad state nesting in transparency group");
2658 msg("<fatal> Notice: this is a known problem, which will be fixed in 0.9.1");
2659 msg("<fatal> In the meantime, please convert the file with -s poly2bitmap");
2660 restoreState(state);
2661 this->device = states[statepos].olddevice;
2663 states[statepos].olddevice = 0;
2665 gfxresult_t*recording = r->finish(r);
2667 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2668 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2670 if(states[statepos].createsoftmask) {
2671 states[statepos-1].softmaskrecording = recording;
2673 states[statepos-1].grouprecording = recording;
2676 states[statepos].createsoftmask = 0;
2677 states[statepos].transparencygroup = 0;
2681 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2683 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2684 "colordodge","colorburn","hardlight","softlight","difference",
2685 "exclusion","hue","saturation","color","luminosity"};
2687 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2688 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2690 if(state->getBlendMode() == gfxBlendNormal)
2691 infofeature("transparency groups");
2694 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2695 warnfeature(buffer, 0);
2698 gfxresult_t*grouprecording = states[statepos].grouprecording;
2700 int blendmode = state->getBlendMode();
2701 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2702 int alpha = (int)(state->getFillOpacity()*255);
2703 if(blendmode == gfxBlendMultiply && alpha>200)
2706 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2707 gfxdevice_ops_init(&ops, this->device, alpha);
2708 gfxresult_record_replay(grouprecording, &ops);
2711 grouprecording->destroy(grouprecording);
2713 states[statepos].grouprecording = 0;
2716 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2718 if(states[statepos].softmask) {
2719 /* shouldn't happen, but *does* happen */
2720 clearSoftMask(state);
2723 /* alpha = 1: retrieve mask values from alpha layer
2724 alpha = 0: retrieve mask values from luminance */
2726 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2727 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2728 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2729 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2731 infofeature("soft masks");
2733 warnfeature("soft masks from alpha channel",0);
2735 if(states[statepos].olddevice) {
2736 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2739 states[statepos].olddevice = this->device;
2740 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2741 gfxdevice_record_init(this->device);
2743 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2745 states[statepos].softmask = 1;
2746 states[statepos].softmask_alpha = alpha;
2749 static inline Guchar div255(int x) {
2750 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2753 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2755 if(c < min) c = min;
2756 if(c > max) c = max;
2760 void GFXOutputDev::clearSoftMask(GfxState *state)
2762 if(!states[statepos].softmask)
2764 states[statepos].softmask = 0;
2765 dbg("clearSoftMask statepos=%d", statepos);
2766 msg("<verbose> clearSoftMask statepos=%d", statepos);
2768 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2769 msg("<error> Error in softmask/tgroup ordering");
2773 gfxresult_t*mask = states[statepos].softmaskrecording;
2774 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2775 this->device = states[statepos].olddevice;
2777 /* get outline of all objects below the soft mask */
2778 gfxdevice_t uniondev;
2779 gfxdevice_union_init(&uniondev, 0);
2780 gfxresult_record_replay(below, &uniondev);
2781 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2782 uniondev.finish(&uniondev);
2783 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2784 gfxline_free(belowoutline);belowoutline=0;
2786 this->device->startclip(this->device, belowoutline);
2787 gfxresult_record_replay(below, this->device);
2788 gfxresult_record_replay(mask, this->device);
2789 this->device->endclip(this->device);
2792 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2793 if(width<=0 || height<=0)
2796 gfxdevice_t belowrender;
2797 gfxdevice_render_init(&belowrender);
2798 if(states[statepos+1].isolated) {
2799 belowrender.setparameter(&belowrender, "fillwhite", "1");
2801 belowrender.setparameter(&belowrender, "antialize", "2");
2802 belowrender.startpage(&belowrender, width, height);
2803 gfxresult_record_replay(below, &belowrender);
2804 belowrender.endpage(&belowrender);
2805 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2806 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2807 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2809 gfxdevice_t maskrender;
2810 gfxdevice_render_init(&maskrender);
2811 maskrender.startpage(&maskrender, width, height);
2812 gfxresult_record_replay(mask, &maskrender);
2813 maskrender.endpage(&maskrender);
2814 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2815 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2817 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2818 msg("<fatal> Internal error in mask drawing");
2823 for(y=0;y<height;y++) {
2824 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2825 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2826 for(x=0;x<width;x++) {
2828 if(states[statepos].softmask_alpha) {
2831 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2834 l2->a = div255(alpha*l2->a);
2836 /* DON'T premultiply alpha- this is done by fillbitmap,
2837 depending on the output device */
2838 //l2->r = div255(alpha*l2->r);
2839 //l2->g = div255(alpha*l2->g);
2840 //l2->b = div255(alpha*l2->b);
2846 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2849 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2850 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2852 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2854 mask->destroy(mask);
2855 below->destroy(below);
2856 maskresult->destroy(maskresult);
2857 belowresult->destroy(belowresult);
2858 states[statepos].softmaskrecording = 0;
2863 // public: ~MemCheck()
2865 // delete globalParams;globalParams=0;
2866 // Object::memCheck(stderr);
2867 // gMemReport(stderr);