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 stralphacmp(const char*s1, const char*s2)
341 /* skip over space, minus, comma etc. */
342 while(*s1>=32 && *s1<=63) s1++;
343 while(*s2>=32 && *s2<=63) s2++;
351 static char fc_ismatch(FcPattern*match, char*family, char*style)
353 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
354 FcBool scalable=FcFalse, outline=FcFalse;
355 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
356 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
357 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
358 FcPatternGetBool(match, "outline", 0, &outline);
359 FcPatternGetBool(match, "scalable", 0, &scalable);
361 if(scalable!=FcTrue || outline!=FcTrue)
364 if (!stralphacmp(fcfamily, family)) {
365 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
368 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
374 static inline char islowercase(char c)
376 return (c>='a' && c<='z');
379 char* fontconfig_searchForFont(char*name)
381 #ifdef HAVE_FONTCONFIG
382 if(!config_use_fontconfig)
385 // call init ony once
389 // check whether we have a config file
390 char* configfile = (char*)FcConfigFilename(0);
391 int configexists = 0;
392 FILE*fi = fopen(configfile, "rb");
394 configexists = 1;fclose(fi);
395 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
397 msg("<debug> Initializing FontConfig (no configfile)");
401 /* A fontconfig instance which didn't find a configfile is unbelievably
402 cranky, so let's just write out a small xml file and make fontconfig
404 FcConfig*c = FcConfigCreate();
406 char* tmpFileName = mktmpname(namebuf);
407 FILE*fi = fopen(tmpFileName, "wb");
408 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
410 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
412 fprintf(fi, "<dir>~/.fonts</dir>\n");
414 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
416 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
417 fprintf(fi, "</fontconfig>\n");
419 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
420 FcConfigBuildFonts(c);
421 FcConfigSetCurrent(c);
425 msg("<debug> FontConfig Initialization failed. Disabling.");
426 config_use_fontconfig = 0;
429 FcConfig * config = FcConfigGetCurrent();
431 msg("<debug> FontConfig Config Initialization failed. Disabling.");
432 config_use_fontconfig = 0;
436 /* add external fonts to fontconfig's config, too. */
437 fontfile_t*fd = global_fonts;
439 FcConfigAppFontAddFile(config, (FcChar8*)fd->filename);
440 msg("<debug> Adding font %s to fontconfig", fd->filename);
444 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
445 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
446 if(!set || !set->nfont) {
447 msg("<debug> FontConfig has zero fonts. Disabling.");
448 config_use_fontconfig = 0;
452 if(getLogLevel() >= LOGLEVEL_TRACE) {
457 for(t=0;t<set->nfont;t++) {
458 char*fcfamily=0,*fcstyle=0,*filename=0;
459 FcBool scalable=FcFalse, outline=FcFalse;
460 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
461 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
462 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
463 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
464 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
465 if(scalable && outline) {
466 msg("<trace> %s (%s) -> %s", fcfamily, fcstyle, filename);
470 set = FcConfigGetFonts(config, FcSetApplication);
475 char*family = strdup(name);
476 int len = strlen(family);
478 char*styles[] = {"Medium", "Regular", "Bold", "Italic", "Black", "Narrow"};
481 for(t=0;t<sizeof(styles)/sizeof(styles[0]);t++) {
482 int l = strlen(styles[t]);
483 if(len>l+1 && !strcmp(family+len-l, styles[t]) && islowercase(family[len-l-1])) {
490 char*dash = strchr(family, '-');
491 if(!dash) dash = strchr(family, ',');
497 FcPattern*pattern = 0;
499 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
500 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
502 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
503 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
505 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
508 FcConfigSubstitute(0, pattern, FcMatchPattern);
509 FcDefaultSubstitute(pattern);
511 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
514 for(t=0;t<set->nfont;t++) {
515 FcPattern*match = set->fonts[t];
516 //FcPattern*match = FcFontMatch(0, pattern, &result);
517 if(fc_ismatch(match, family, style)) {
519 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
520 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
523 //FcPatternDestroy(match);
524 msg("<debug> fontconfig: returning filename %s", filename);
526 FcPatternDestroy(pattern);
527 FcFontSetDestroy(set);
528 return filename?strdup(filename):0;
533 FcPatternDestroy(pattern);
534 FcFontSetDestroy(set);
541 static DisplayFontParamKind detectFontType(const char*filename)
543 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
544 return displayFontTT;
545 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
546 return displayFontT1;
547 return displayFontTT;
550 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
552 msg("<verbose> looking for font %s", fontName->getCString());
554 char*name = fontName->getCString();
556 /* see if it is a pdf standard font */
558 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
559 if(!strcmp(name, pdf2t1map[t].pdffont)) {
560 if(!pdf2t1map[t].fullfilename) {
561 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
562 if(!pdf2t1map[t].fullfilename) {
563 msg("<error> Couldn't save default font- is the Temp Directory writable?");
565 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
568 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
569 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
574 int bestlen = 0x7fffffff;
575 const char*bestfilename = 0;
577 #ifndef HAVE_FONTCONFIG
578 /* if we don't have fontconfig, try a simple filename-comparison approach */
579 fontfile_t*f = global_fonts;
581 if(strstr(f->filename, name)) {
582 if(f->len < bestlen) {
584 bestfilename = f->filename;
591 /* if we didn't find anything up to now, try looking for the
592 font via fontconfig */
595 filename = fontconfig_searchForFont(name);
597 filename = strdup(bestfilename);
601 msg("<verbose> Font %s maps to %s\n", name, filename);
602 DisplayFontParamKind kind = detectFontType(filename);
603 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
604 if(kind == displayFontTT) {
605 dfp->tt.fileName = new GString(filename);
607 dfp->t1.fileName = new GString(filename);
612 msg("<verbose> Font %s not found\n", name);
613 return GlobalParams::getDisplayFont(fontName);
617 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
620 gfxglobals = new GFXOutputGlobals();
624 this->xref = doc->getXRef();
626 this->type3active = 0;
629 this->user_movex = 0;
630 this->user_movey = 0;
633 this->user_clipx1 = 0;
634 this->user_clipy1 = 0;
635 this->user_clipx2 = 0;
636 this->user_clipy2 = 0;
637 this->current_gfxfont = 0;
638 this->current_fontinfo = 0;
639 this->current_text_stroke = 0;
640 this->current_text_clip = 0;
641 this->outer_clip_box = 0;
642 this->config_bigchar=0;
643 this->config_convertgradients=1;
644 this->config_break_on_warning=0;
645 this->config_remapunicode=0;
646 this->config_transparent=0;
647 this->config_extrafontdata = 0;
648 this->config_drawonlyshapes = 0;
649 this->config_disable_polygon_conversion = 0;
650 this->config_multiply = 1;
651 this->config_detectspaces = 1;
652 this->config_linkdatafile = 0;
656 memset(states, 0, sizeof(states));
659 void GFXOutputDev::setParameter(const char*key, const char*value)
661 if(!strcmp(key,"breakonwarning")) {
662 this->config_break_on_warning = atoi(value);
663 } else if(!strcmp(key,"remapunicode")) {
664 this->config_remapunicode = atoi(value);
665 } else if(!strcmp(key,"transparent")) {
666 this->config_transparent = atoi(value);
667 } else if(!strcmp(key,"drawonlyshapes")) {
668 this->config_drawonlyshapes = atoi(value);
669 } else if(!strcmp(key,"detectspaces")) {
670 this->config_detectspaces = atoi(value);
671 } else if(!strcmp(key,"extrafontdata")) {
672 this->config_extrafontdata = atoi(value);
673 } else if(!strcmp(key,"linkdatafile")) {
674 this->config_linkdatafile = strdup(value);
675 } else if(!strcmp(key,"convertgradients")) {
676 this->config_convertgradients = atoi(value);
677 } else if(!strcmp(key,"multiply")) {
678 this->config_multiply = atoi(value);
679 if(this->config_multiply<1)
680 this->config_multiply=1;
681 } else if(!strcmp(key,"disable_polygon_conversion")) {
682 this->config_disable_polygon_conversion = atoi(value);
686 void GFXOutputDev::setDevice(gfxdevice_t*dev)
691 void GFXOutputDev::setMove(int x,int y)
693 this->user_movex = x;
694 this->user_movey = y;
697 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
699 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
700 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
702 this->user_clipx1 = x1;
703 this->user_clipy1 = y1;
704 this->user_clipx2 = x2;
705 this->user_clipy2 = y2;
708 static char*getFontName(GfxFont*font)
711 GString*gstr = font->getName();
712 char* fname = gstr==0?0:gstr->getCString();
716 sprintf(buf, "UFONT%d", r->num);
717 fontid = strdup(buf);
719 fontid = strdup(fname);
723 char* plus = strchr(fontid, '+');
724 if(plus && plus < &fontid[strlen(fontid)-1]) {
725 fontname = strdup(plus+1);
727 fontname = strdup(fontid);
733 static void dumpFontInfo(const char*loglevel, GfxFont*font);
734 static int lastdumps[1024];
735 static int lastdumppos = 0;
740 static void showFontError(GfxFont*font, int nr)
744 for(t=0;t<lastdumppos;t++)
745 if(lastdumps[t] == r->num)
749 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
750 lastdumps[lastdumppos++] = r->num;
752 msg("<warning> The following font caused problems:");
754 msg("<warning> The following font caused problems (substituting):");
756 msg("<warning> The following Type 3 Font will be rendered as graphics:");
757 dumpFontInfo("<warning>", font);
760 static void dumpFontInfo(const char*loglevel, GfxFont*font)
762 char* id = getFontID(font);
763 char* name = getFontName(font);
764 Ref* r=font->getID();
765 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
767 GString*gstr = font->getTag();
769 msg("%s| Tag: %s", loglevel, id);
771 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
773 GfxFontType type=font->getType();
775 case fontUnknownType:
776 msg("%s| Type: unknown",loglevel);
779 msg("%s| Type: 1",loglevel);
782 msg("%s| Type: 1C",loglevel);
785 msg("%s| Type: 3",loglevel);
788 msg("%s| Type: TrueType",loglevel);
791 msg("%s| Type: CIDType0",loglevel);
794 msg("%s| Type: CIDType0C",loglevel);
797 msg("%s| Type: CIDType2",loglevel);
802 GBool embedded = font->getEmbeddedFontID(&embRef);
804 if(font->getEmbeddedFontName()) {
805 embeddedName = font->getEmbeddedFontName()->getCString();
808 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
810 gstr = font->getExtFontFile();
812 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
814 // Get font descriptor flags.
815 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
816 if(font->isSerif()) msg("%s| is serif", loglevel);
817 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
818 if(font->isItalic()) msg("%s| is italic", loglevel);
819 if(font->isBold()) msg("%s| is bold", loglevel);
825 //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");}
826 //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");}
828 void dump_outline(gfxline_t*line)
830 /*gfxbbox_t*r = gfxline_isrectangle(line);
832 printf("is not a rectangle\n");
834 printf("is a rectangle: (%f,%f)-(%f-%f)\n", r->xmin, r->ymin, r->xmax, r->ymax);
838 if(line->type == gfx_moveTo) {
839 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
840 } else if(line->type == gfx_lineTo) {
841 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
842 } else if(line->type == gfx_splineTo) {
843 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
849 void gfxPath_dump(GfxPath*path)
851 int num = path->getNumSubpaths();
854 for(t = 0; t < num; t++) {
855 GfxSubpath *subpath = path->getSubpath(t);
856 int subnum = subpath->getNumPoints();
858 for(s=0;s<subnum;s++) {
859 double x=subpath->getX(s);
860 double y=subpath->getY(s);
861 if(s==0 && !subpath->getCurve(s)) {
862 printf("M %f %f\n", x, y);
863 } else if(s==0 && subpath->getCurve(s)) {
864 printf("E %f %f\n", x, y);
865 } else if(subpath->getCurve(s)) {
866 printf("C %f %f\n", x, y);
868 printf("T %f %f\n", x, y);
874 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
876 int num = path->getNumSubpaths();
879 double lastx=0,lasty=0,posx=0,posy=0;
882 msg("<warning> empty path");
886 gfxdrawer_target_gfxline(&draw);
888 for(t = 0; t < num; t++) {
889 GfxSubpath *subpath = path->getSubpath(t);
890 int subnum = subpath->getNumPoints();
891 double bx=0,by=0,cx=0,cy=0;
893 for(s=0;s<subnum;s++) {
896 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
899 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
900 draw.lineTo(&draw, lastx, lasty);
902 draw.moveTo(&draw, x,y);
907 } else if(subpath->getCurve(s) && cpos==0) {
911 } else if(subpath->getCurve(s) && cpos==1) {
919 draw.lineTo(&draw, x,y);
921 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
928 /* fix non-closed lines */
929 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
930 draw.lineTo(&draw, lastx, lasty);
932 gfxline_t*result = (gfxline_t*)draw.result(&draw);
934 gfxline_optimize(result);
939 GBool GFXOutputDev::useTilingPatternFill()
941 infofeature("tiled patterns");
942 // if(config_convertgradients)
946 GBool GFXOutputDev::useShadedFills()
948 infofeature("shaded fills");
949 if(config_convertgradients)
954 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
956 state->transform(x,y,nx,ny);
957 *nx += user_movex + clipmovex;
958 *ny += user_movey + clipmovey;
962 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
963 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
964 int paintType, Dict *resDict,
965 double *mat, double *bbox,
966 int x0, int y0, int x1, int y1,
967 double xStep, double yStep)
969 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
970 int paintType, Dict *resDict,
971 double *mat, double *bbox,
972 int x0, int y0, int x1, int y1,
973 double xStep, double yStep)
976 msg("<debug> tilingPatternFill");
977 infofeature("tiling pattern fills");
980 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
982 msg("<error> functionShadedFill not supported yet");
983 infofeature("function shaded fills");
986 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
990 colspace->getRGB(col, &rgb);
991 c.r = colToByte(rgb.r);
992 c.g = colToByte(rgb.g);
993 c.b = colToByte(rgb.b);
998 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
1000 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
1001 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
1004 this->transformXY(state, x0,y0, &x0,&y0);
1005 this->transformXY(state, x1,y1, &x1,&y1);
1006 this->transformXY(state, x2,y2, &x2,&y2);
1011 shading->getColor(0.0, &color0);
1012 shading->getColor(0.5, &color1);
1013 shading->getColor(1.0, &color2);
1015 GfxColorSpace* colspace = shading->getColorSpace();
1017 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
1018 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1019 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1020 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
1021 infofeature("radial shaded fills");
1023 gfxgradient_t gr[3];
1024 gfxgradient_t*g = &gr[0];
1028 g[0].color = col2col(colspace, &color0);
1029 g[1].color = col2col(colspace, &color1);
1030 g[2].color = col2col(colspace, &color2);
1035 gfxbbox_t b = states[statepos].clipbbox;
1036 gfxline_t p1,p2,p3,p4,p5;
1037 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1038 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1039 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1040 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1041 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1044 //m.m00 = (x3-x0); m.m10 = (x1-x0);
1045 //m.m01 = (y3-y0); m.m11 = (y1-y0);
1046 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
1047 m.m00 = (x1-x0); m.m10 = (x2-x0);
1048 m.m01 = (y1-y0); m.m11 = (y2-y0);
1052 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
1056 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
1059 shading->getCoords(&x0,&y0,&x1,&y1);
1060 this->transformXY(state, x0,y0,&x0,&y0);
1061 this->transformXY(state, x1,y1,&x1,&y1);
1066 shading->getColor(0.0, &color0);
1067 shading->getColor(0.5, &color1);
1068 shading->getColor(1.0, &color2);
1070 GfxColorSpace* colspace = shading->getColorSpace();
1072 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
1073 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
1074 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
1075 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
1077 infofeature("axial shaded fills");
1079 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1083 g[0].color = col2col(colspace, &color0);
1084 g[1].color = col2col(colspace, &color1);
1085 g[2].color = col2col(colspace, &color2);
1090 double xMin,yMin,xMax,yMax;
1091 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1092 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1093 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1096 xMin = 1024; yMin = 1024;
1098 gfxbbox_t b = states[statepos].clipbbox;
1099 gfxline_t p1,p2,p3,p4,p5;
1100 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1101 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1102 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1103 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1104 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1106 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1107 the middle of the two control points */
1109 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1110 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1111 m.tx = (x0 + x1)/2 - 0.5;
1112 m.ty = (y0 + y1)/2 - 0.5;
1114 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1120 GBool GFXOutputDev::useDrawForm()
1122 infofeature("forms");
1125 void GFXOutputDev::drawForm(Ref id)
1127 msg("<error> drawForm not implemented");
1129 GBool GFXOutputDev::needNonText()
1133 void GFXOutputDev::endPage()
1135 msg("<verbose> endPage (GfxOutputDev)");
1136 if(outer_clip_box) {
1137 device->endclip(device);
1140 /* notice: we're not fully done yet with this page- there might still be
1141 a few calls to drawLink() yet to come */
1144 static inline double sqr(double x) {return x*x;}
1146 #define STROKE_FILL 1
1147 #define STROKE_CLIP 2
1148 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1150 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1151 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1152 double miterLimit = state->getMiterLimit();
1153 double width = state->getTransformedLineWidth();
1156 double opaq = state->getStrokeOpacity();
1158 state->getFillRGB(&rgb);
1160 state->getStrokeRGB(&rgb);
1162 col.r = colToByte(rgb.r);
1163 col.g = colToByte(rgb.g);
1164 col.b = colToByte(rgb.b);
1165 col.a = (unsigned char)(opaq*255);
1167 gfx_capType capType = gfx_capRound;
1168 if(lineCap == 0) capType = gfx_capButt;
1169 else if(lineCap == 1) capType = gfx_capRound;
1170 else if(lineCap == 2) capType = gfx_capSquare;
1171 else msg("<error> Invalid line cap type");
1173 gfx_joinType joinType = gfx_joinRound;
1174 if(lineJoin == 0) joinType = gfx_joinMiter;
1175 else if(lineJoin == 1) joinType = gfx_joinRound;
1176 else if(lineJoin == 2) joinType = gfx_joinBevel;
1177 else msg("<error> Invalid line join type");
1179 gfxline_t*line2 = 0;
1181 int dashLength = states[statepos].dashLength;
1182 double*dashPattern = states[statepos].dashPattern;
1183 double dashStart = states[statepos].dashStart;
1184 if(dashLength && dashPattern) {
1185 float * dash = (float*)malloc(sizeof(float)*(dashLength+1));
1188 /* try to find out how much the transformation matrix would
1189 stretch the dashes, and factor that into the dash lengths.
1190 This is not the entirely correct approach- it would be
1191 better to first convert the path to an unscaled version,
1192 then apply dashing, and then transform the path using
1193 the current transformation matrix. However there are few
1194 PDFs which actually stretch a dashed path in a non-orthonormal
1196 double tx1, ty1, tx2, ty2, tx3, ty3;
1197 this->transformXY(state, 0, 0, &tx1, &ty1);
1198 this->transformXY(state, 0, 1, &tx2, &ty2);
1199 this->transformXY(state, 1, 0, &tx3, &ty3);
1200 double d1 = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1));
1201 double d2 = sqrt(sqr(tx3-tx1)+sqr(ty3-ty1));
1203 warnfeature("non-ortogonally dashed strokes", 0);
1204 double f = (d1+d2)/2;
1206 msg("<trace> %d dashes", dashLength);
1207 msg("<trace> | phase: %f", dashStart);
1208 for(t=0;t<dashLength;t++) {
1209 dash[t] = (float)dashPattern[t] * f;
1213 msg("<trace> | d%-3d: %f", t, dashPattern[t]);
1215 dash[dashLength] = -1;
1216 if(getLogLevel() >= LOGLEVEL_TRACE) {
1220 line2 = gfxtool_dash_line(line, dash, (float)(dashStart*f));
1224 msg("<trace> After dashing:");
1227 if(getLogLevel() >= LOGLEVEL_TRACE) {
1228 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1230 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1231 lineCap==0?"butt": (lineCap==1?"round":"square"),
1233 col.r,col.g,col.b,col.a
1238 if(flags&STROKE_FILL) {
1239 gfxpoly_t* poly = gfxpoly_from_stroke(line, width, capType, joinType, miterLimit, DEFAULT_GRID);
1240 gfxline_t*gfxline = gfxline_from_gfxpoly(poly);
1241 if(getLogLevel() >= LOGLEVEL_TRACE) {
1242 dump_outline(gfxline);
1245 msg("<warning> Empty polygon (resulting from stroked line)");
1247 if(flags&STROKE_CLIP) {
1248 device->startclip(device, gfxline);
1249 states[statepos].clipping++;
1251 device->fill(device, gfxline, &col);
1253 gfxline_free(gfxline);
1254 gfxpoly_destroy(poly);
1256 if(flags&STROKE_CLIP)
1257 msg("<error> Stroke&clip not supported at the same time");
1258 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1262 gfxline_free(line2);
1265 gfxcolor_t getFillColor(GfxState * state)
1268 double opaq = state->getFillOpacity();
1269 state->getFillRGB(&rgb);
1271 col.r = colToByte(rgb.r);
1272 col.g = colToByte(rgb.g);
1273 col.b = colToByte(rgb.b);
1274 col.a = (unsigned char)(opaq*255);
1278 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1280 gfxcolor_t col = getFillColor(state);
1282 if(getLogLevel() >= LOGLEVEL_TRACE) {
1283 msg("<trace> %sfill %02x%02x%02x%02x", evenodd?"eo":"", col.r, col.g, col.b, col.a);
1286 device->fill(device, line, &col);
1289 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line, char evenodd)
1291 if(getLogLevel() >= LOGLEVEL_TRACE) {
1292 msg("<trace> %sclip", evenodd?"eo":"");
1295 gfxbbox_t bbox = gfxline_getbbox(line);
1296 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1298 device->startclip(device, line);
1299 states[statepos].clipping++;
1302 void GFXOutputDev::clip(GfxState *state)
1304 GfxPath * path = state->getPath();
1305 msg("<trace> clip");
1306 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1307 if(!config_disable_polygon_conversion) {
1308 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
1312 clipToGfxLine(state, line, 0);
1316 void GFXOutputDev::eoClip(GfxState *state)
1318 GfxPath * path = state->getPath();
1319 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1320 clipToGfxLine(state, line, 1);
1323 void GFXOutputDev::clipToStrokePath(GfxState *state)
1325 GfxPath * path = state->getPath();
1326 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1328 if(getLogLevel() >= LOGLEVEL_TRACE) {
1329 double width = state->getTransformedLineWidth();
1330 msg("<trace> cliptostrokepath width=%f", width);
1334 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1338 void GFXOutputDev::finish()
1340 if(outer_clip_box) {
1342 device->endclip(device);
1348 GFXOutputDev::~GFXOutputDev()
1352 GBool GFXOutputDev::upsideDown()
1356 GBool GFXOutputDev::useDrawChar()
1361 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1362 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1364 static char tmp_printstr[4096];
1365 char* makeStringPrintable(char*str)
1367 int len = strlen(str);
1374 for(t=0;t<len;t++) {
1379 tmp_printstr[t] = c;
1382 tmp_printstr[len++] = '.';
1383 tmp_printstr[len++] = '.';
1384 tmp_printstr[len++] = '.';
1386 tmp_printstr[len] = 0;
1387 return tmp_printstr;
1389 void GFXOutputDev::updateFontMatrix(GfxState*state)
1391 double* ctm = state->getCTM();
1392 double fontSize = state->getFontSize();
1393 double*textMat = state->getTextMat();
1395 /* taking the absolute value of horizScaling seems to be required for
1396 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1397 double hscale = fabs(state->getHorizScaling());
1399 // from xpdf-3.02/SplashOutputDev:updateFont
1400 double mm11 = textMat[0] * fontSize * hscale;
1401 double mm12 = textMat[1] * fontSize * hscale;
1402 double mm21 = textMat[2] * fontSize;
1403 double mm22 = textMat[3] * fontSize;
1405 // multiply with ctm, like state->getFontTransMat() does
1406 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1407 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1408 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1409 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1410 this->current_font_matrix.tx = 0;
1411 this->current_font_matrix.ty = 0;
1414 void GFXOutputDev::beginString(GfxState *state, GString *s)
1416 int render = state->getRender();
1417 if(current_text_stroke) {
1418 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1420 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1423 static gfxline_t* mkEmptyGfxShape(double x, double y)
1425 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1426 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1430 static char isValidUnicode(int c)
1432 if(c>=32 && c<0x2fffe)
1437 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1438 double dx, double dy,
1439 double originX, double originY,
1440 CharCode charid, int nBytes, Unicode *_u, int uLen)
1442 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1443 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1447 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1449 int render = state->getRender();
1450 gfxcolor_t col = getFillColor(state);
1452 // check for invisible text -- this is used by Acrobat Capture
1453 if (render == RENDER_INVISIBLE) {
1455 if(!config_extrafontdata)
1459 GfxFont*font = state->getFont();
1461 if(font->getType() == fontType3) {
1462 /* type 3 chars are passed as graphics */
1463 msg("<debug> type3 char at %f/%f", x, y);
1467 Unicode u = uLen?(_u[0]):0;
1469 gfxmatrix_t m = this->current_font_matrix;
1470 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1471 //m.tx += originX; m.ty += originY;
1473 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d font=%p",m.tx,m.ty,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
1475 if((render == RENDER_FILL && !config_drawonlyshapes) ||
1476 (render == RENDER_FILLSTROKE && state->getTransformedLineWidth()<1.0) ||
1477 (render == RENDER_INVISIBLE)) {
1479 int space = this->current_fontinfo->space_char;
1480 if(config_extrafontdata && config_detectspaces && space>=0 && m.m00 && !m.m01) {
1481 /* space char detection */
1482 if(last_char_gfxfont == current_gfxfont &&
1483 last_char_y == m.ty &&
1484 !last_char_was_space) {
1485 double expected_x = last_char_x + current_gfxfont->glyphs[last_char].advance*m.m00;
1486 int space = this->current_fontinfo->space_char;
1487 float width = this->current_fontinfo->average_advance;
1488 if(m.tx - expected_x >= m.m00*width*4/10) {
1489 msg("<debug> There's a %f pixel gap between char %d and char %d (expected no more than %f), I'm inserting a space here",
1492 last_char, glyphid);
1494 m2.tx = expected_x + (m.tx - expected_x - current_gfxfont->glyphs[space].advance*m.m00)/2;
1495 if(m2.tx < expected_x) m2.tx = expected_x;
1496 device->drawchar(device, current_gfxfont, space, &col, &m2);
1499 last_char_gfxfont = current_gfxfont;
1500 last_char = glyphid;
1503 last_char_was_space = GLYPH_IS_SPACE(¤t_gfxfont->glyphs[glyphid]);
1505 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1507 msg("<debug> Drawing glyph %d as shape", charid);
1508 if(!gfxglobals->textmodeinfo) {
1509 msg("<notice> Some texts will be rendered as shape");
1510 gfxglobals->textmodeinfo = 1;
1513 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1514 gfxline_t*tglyph = gfxline_clone(glyph);
1515 gfxline_transform(tglyph, &m);
1516 if((render&3) != RENDER_INVISIBLE) {
1517 gfxline_t*add = gfxline_clone(tglyph);
1518 current_text_stroke = gfxline_append(current_text_stroke, add);
1520 if(render&RENDER_CLIP) {
1521 gfxline_t*add = gfxline_clone(tglyph);
1522 current_text_clip = gfxline_append(current_text_clip, add);
1523 if(!current_text_clip) {
1524 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1527 gfxline_free(tglyph);
1531 void GFXOutputDev::endString(GfxState *state)
1533 int render = state->getRender();
1534 msg("<trace> endString() render=%d textstroke=%p", render, current_text_stroke);
1536 if(current_text_stroke) {
1537 /* fillstroke and stroke text rendering objects we can process right
1538 now (as there may be texts of other rendering modes in this
1539 text object)- clipping objects have to wait until endTextObject,
1541 device->setparameter(device, "mark","TXT");
1542 if((render&3) == RENDER_FILL) {
1543 fillGfxLine(state, current_text_stroke, 0);
1544 gfxline_free(current_text_stroke);
1545 current_text_stroke = 0;
1546 } else if((render&3) == RENDER_FILLSTROKE) {
1547 fillGfxLine(state, current_text_stroke, 0);
1548 strokeGfxline(state, current_text_stroke,0);
1549 gfxline_free(current_text_stroke);
1550 current_text_stroke = 0;
1551 } else if((render&3) == RENDER_STROKE) {
1552 strokeGfxline(state, current_text_stroke,0);
1553 gfxline_free(current_text_stroke);
1554 current_text_stroke = 0;
1556 device->setparameter(device, "mark","");
1560 void GFXOutputDev::endTextObject(GfxState *state)
1562 int render = state->getRender();
1563 msg("<trace> endTextObject() render=%d textstroke=%p clipstroke=%p", render, current_text_stroke, current_text_clip);
1565 if(current_text_clip) {
1566 device->setparameter(device, "mark","TXT");
1567 clipToGfxLine(state, current_text_clip, 0);
1568 device->setparameter(device, "mark","");
1569 gfxline_free(current_text_clip);
1570 current_text_clip = 0;
1574 /* the logic seems to be as following:
1575 first, beginType3Char is called, with the charcode and the coordinates.
1576 if this function returns true, it already knew about the char and has now drawn it.
1577 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1578 called with some parameters.
1579 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1580 at the position first passed to beginType3Char). the char ends with endType3Char.
1582 The drawing operations between beginType3Char and endType3Char are somewhat different to
1583 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1584 color determines the color of a font)
1587 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1589 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1592 if(config_extrafontdata && current_fontinfo) {
1594 gfxmatrix_t m = this->current_font_matrix;
1595 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1597 /*m.m00*=INTERNAL_FONT_SIZE;
1598 m.m01*=INTERNAL_FONT_SIZE;
1599 m.m10*=INTERNAL_FONT_SIZE;
1600 m.m11*=INTERNAL_FONT_SIZE;*/
1602 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1603 msg("<error> Invalid charid %d for font", charid);
1606 gfxcolor_t col={0,0,0,0};
1607 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1608 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1612 /* the character itself is going to be passed using the draw functions */
1613 return gFalse; /* gTrue= is_in_cache? */
1616 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1618 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1621 void GFXOutputDev::endType3Char(GfxState *state)
1624 msg("<debug> endType3Char");
1627 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1629 this->currentpage = pageNum;
1631 int rot = doc->getPageRotate(1);
1632 gfxcolor_t white = {255,255,255,255};
1633 gfxcolor_t black = {255,0,0,0};
1635 gfxline_t clippath[5];
1637 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1638 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1639 Use CropBox, not MediaBox, as page size
1646 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1647 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1649 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1650 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1652 this->clipmovex = -(int)x1;
1653 this->clipmovey = -(int)y1;
1655 /* apply user clip box */
1656 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1657 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1658 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1659 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1660 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1661 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1663 x1 += this->clipmovex;
1664 y1 += this->clipmovey;
1665 x2 += this->clipmovex;
1666 y2 += this->clipmovey;
1669 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1671 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);
1673 msg("<verbose> page is rotated %d degrees", rot);
1675 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1676 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1677 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1678 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1679 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1680 device->startclip(device, clippath); outer_clip_box = 1;
1681 if(!config_transparent) {
1682 device->fill(device, clippath, &white);
1684 states[statepos].clipbbox.xmin = x1;
1685 states[statepos].clipbbox.ymin = x1;
1686 states[statepos].clipbbox.xmax = x2;
1687 states[statepos].clipbbox.ymax = y2;
1689 states[statepos].dashPattern = 0;
1690 states[statepos].dashLength = 0;
1691 states[statepos].dashStart = 0;
1693 this->last_char_gfxfont = 0;
1697 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1699 double x1, y1, x2, y2;
1700 gfxline_t points[5];
1703 msg("<debug> drawlink");
1705 link->getRect(&x1, &y1, &x2, &y2);
1706 cvtUserToDev(x1, y1, &x, &y);
1707 points[0].type = gfx_moveTo;
1708 points[0].x = points[4].x = x + user_movex + clipmovex;
1709 points[0].y = points[4].y = y + user_movey + clipmovey;
1710 points[0].next = &points[1];
1711 cvtUserToDev(x2, y1, &x, &y);
1712 points[1].type = gfx_lineTo;
1713 points[1].x = x + user_movex + clipmovex;
1714 points[1].y = y + user_movey + clipmovey;
1715 points[1].next = &points[2];
1716 cvtUserToDev(x2, y2, &x, &y);
1717 points[2].type = gfx_lineTo;
1718 points[2].x = x + user_movex + clipmovex;
1719 points[2].y = y + user_movey + clipmovey;
1720 points[2].next = &points[3];
1721 cvtUserToDev(x1, y2, &x, &y);
1722 points[3].type = gfx_lineTo;
1723 points[3].x = x + user_movex + clipmovex;
1724 points[3].y = y + user_movey + clipmovey;
1725 points[3].next = &points[4];
1726 cvtUserToDev(x1, y1, &x, &y);
1727 points[4].type = gfx_lineTo;
1728 points[4].x = x + user_movex + clipmovex;
1729 points[4].y = y + user_movey + clipmovey;
1732 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1733 points[0].x, points[0].y,
1734 points[1].x, points[1].y,
1735 points[2].x, points[2].y,
1736 points[3].x, points[3].y);
1738 if(getLogLevel() >= LOGLEVEL_TRACE) {
1739 dump_outline(points);
1742 LinkAction*action=link->getAction();
1745 const char*type = "-?-";
1748 msg("<trace> drawlink action=%d", action->getKind());
1749 switch(action->getKind())
1753 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1754 LinkDest *dest=NULL;
1755 if (ha->getDest()==NULL)
1756 dest=catalog->findDest(ha->getNamedDest());
1758 dest=ha->getDest()->copy();
1760 if (dest->isPageRef()){
1761 Ref pageref=dest->getPageRef();
1762 page=catalog->findPage(pageref.num,pageref.gen);
1764 else page=dest->getPageNum();
1765 sprintf(buf, "%d", page);
1773 LinkGoToR*l = (LinkGoToR*)action;
1774 GString*g = l->getFileName();
1776 s = strdup(g->getCString());
1778 /* if the GoToR link has no filename, then
1779 try to find a refernce in the *local*
1781 GString*g = l->getNamedDest();
1783 s = strdup(g->getCString());
1789 LinkNamed*l = (LinkNamed*)action;
1790 GString*name = l->getName();
1792 s = strdup(name->lowerCase()->getCString());
1793 named = name->getCString();
1796 if(strstr(s, "next") || strstr(s, "forward"))
1798 page = currentpage + 1;
1800 else if(strstr(s, "prev") || strstr(s, "back"))
1802 page = currentpage - 1;
1804 else if(strstr(s, "last") || strstr(s, "end"))
1806 if(this->page2page && this->num_pages) {
1807 page = this->page2page[this->num_pages-1];
1810 else if(strstr(s, "first") || strstr(s, "top"))
1818 case actionLaunch: {
1820 LinkLaunch*l = (LinkLaunch*)action;
1821 GString * str = new GString(l->getFileName());
1822 GString * params = l->getParams();
1824 str->append(params);
1825 s = strdup(str->getCString());
1832 LinkURI*l = (LinkURI*)action;
1833 GString*g = l->getURI();
1835 url = g->getCString();
1840 case actionUnknown: {
1842 LinkUnknown*l = (LinkUnknown*)action;
1847 msg("<error> Unknown link type!");
1852 if(!s) s = strdup("-?-");
1854 msg("<trace> drawlink s=%s", s);
1856 if(!gfxglobals->linkinfo && (page || s))
1858 msg("<notice> File contains links");
1859 gfxglobals->linkinfo = 1;
1865 for(t=1;t<=this->num_pages;t++) {
1866 if(this->page2page[t]==page) {
1876 sprintf(buf, "page%d", lpage);
1877 device->drawlink(device, points, buf);
1881 device->drawlink(device, points, s);
1882 if(this->config_linkdatafile) {
1883 FILE*fi = fopen(config_linkdatafile, "ab+");
1884 fprintf(fi, "%s\n", s);
1889 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1893 void GFXOutputDev::saveState(GfxState *state) {
1894 dbg("saveState %p", state); dbgindent+=2;
1896 msg("<trace> saveState %p", state);
1899 msg("<fatal> Too many nested states in pdf.");
1903 states[statepos].state = state;
1904 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1905 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1906 states[statepos].clipping = 0;
1907 states[statepos].olddevice = 0;
1908 states[statepos].clipbbox = states[statepos-1].clipbbox;
1910 states[statepos].dashPattern = states[statepos-1].dashPattern;
1911 states[statepos].dashStart = states[statepos-1].dashStart;
1912 states[statepos].dashLength = states[statepos-1].dashLength;
1915 void GFXOutputDev::restoreState(GfxState *state) {
1916 dbgindent-=2; dbg("restoreState %p", state);
1919 msg("<fatal> Invalid restoreState");
1922 msg("<trace> restoreState %p%s%s", state,
1923 states[statepos].softmask?" (end softmask)":"",
1924 states[statepos].clipping?" (end clipping)":"");
1925 if(states[statepos].softmask) {
1926 clearSoftMask(state);
1929 if(states[statepos].dashPattern) {
1930 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1931 free(states[statepos].dashPattern);
1932 states[statepos].dashPattern = 0;
1938 while(states[statepos].clipping) {
1939 device->endclip(device);
1940 states[statepos].clipping--;
1942 if(states[statepos].state!=state) {
1943 msg("<fatal> bad state nesting");
1946 for(t=0;t<=statepos;t++) {
1947 printf("%p ", states[t].state);
1953 states[statepos].state=0;
1957 void GFXOutputDev::updateLineDash(GfxState *state)
1959 if(states[statepos].dashPattern &&
1960 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1961 free(states[statepos].dashPattern);
1962 states[statepos].dashPattern = 0;
1964 double *pattern = 0;
1967 state->getLineDash(&pattern, &dashLength, &dashStart);
1968 msg("<debug> updateLineDash, %d dashes", dashLength);
1970 states[statepos].dashPattern = 0;
1971 states[statepos].dashLength = 0;
1973 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1974 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1975 states[statepos].dashPattern = p;
1976 states[statepos].dashLength = dashLength;
1977 states[statepos].dashStart = dashStart;
1981 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1983 this->page2page = page2page;
1984 this->num_pages = num_pages;
1987 void GFXOutputDev::updateLineWidth(GfxState *state)
1989 double width = state->getTransformedLineWidth();
1992 void GFXOutputDev::updateLineCap(GfxState *state)
1994 int c = state->getLineCap();
1997 void GFXOutputDev::updateLineJoin(GfxState *state)
1999 int j = state->getLineJoin();
2002 void GFXOutputDev::updateFillColor(GfxState *state)
2005 double opaq = state->getFillOpacity();
2006 state->getFillRGB(&rgb);
2008 void GFXOutputDev::updateFillOpacity(GfxState *state)
2011 double opaq = state->getFillOpacity();
2012 state->getFillRGB(&rgb);
2013 dbg("update fillopaq %f", opaq);
2015 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
2017 double opaq = state->getFillOpacity();
2018 dbg("update strokeopaq %f", opaq);
2020 void GFXOutputDev::updateFillOverprint(GfxState *state)
2022 double opaq = state->getFillOverprint();
2023 dbg("update filloverprint %f", opaq);
2025 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
2027 double opaq = state->getStrokeOverprint();
2028 dbg("update strokeoverprint %f", opaq);
2030 void GFXOutputDev::updateTransfer(GfxState *state)
2032 dbg("update transfer");
2036 void GFXOutputDev::updateStrokeColor(GfxState *state)
2039 double opaq = state->getStrokeOpacity();
2040 state->getStrokeRGB(&rgb);
2043 void GFXOutputDev::updateFont(GfxState *state)
2045 GfxFont* gfxFont = state->getFont();
2049 char*id = getFontID(gfxFont);
2050 msg("<verbose> Updating font to %s", id);
2051 if(gfxFont->getType() == fontType3) {
2052 infofeature("Type3 fonts");
2053 if(!config_extrafontdata) {
2058 msg("<error> Internal Error: FontID is null");
2062 this->current_fontinfo = this->info->getFont(id);
2064 if(!this->current_fontinfo) {
2065 msg("<error> Internal Error: no fontinfo for font %s", id);
2068 if(!this->current_fontinfo->seen) {
2069 dumpFontInfo("<verbose>", gfxFont);
2072 current_gfxfont = this->current_fontinfo->getGfxFont();
2073 device->addfont(device, current_gfxfont);
2076 updateFontMatrix(state);
2079 #define SQR(x) ((x)*(x))
2081 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2083 if((newwidth<1 || newheight<1) ||
2084 (width<=newwidth || height<=newheight))
2086 unsigned char*newdata;
2088 newdata= (unsigned char*)malloc(newwidth*newheight);
2089 double fx = ((double)width)/newwidth;
2090 double fy = ((double)height)/newheight;
2092 int blocksize = (int)(8192/(fx*fy));
2093 int r = 8192*256/palettesize;
2094 for(x=0;x<newwidth;x++) {
2095 double ex = px + fx;
2096 int fromx = (int)px;
2098 int xweight1 = (int)((1-(px-fromx))*256);
2099 int xweight2 = (int)((ex-tox)*256);
2101 for(y=0;y<newheight;y++) {
2102 double ey = py + fy;
2103 int fromy = (int)py;
2105 int yweight1 = (int)((1-(py-fromy))*256);
2106 int yweight2 = (int)((ey-toy)*256);
2113 for(xx=fromx;xx<=tox;xx++)
2114 for(yy=fromy;yy<=toy;yy++) {
2115 int b = 1-data[width*yy+xx];
2117 if(xx==fromx) weight = (weight*xweight1)/256;
2118 if(xx==tox) weight = (weight*xweight2)/256;
2119 if(yy==fromy) weight = (weight*yweight1)/256;
2120 if(yy==toy) weight = (weight*yweight2)/256;
2123 //if(a) a=(palettesize-1)*r/blocksize;
2124 newdata[y*newwidth+x] = (a*blocksize)/r;
2132 #define IMAGE_TYPE_JPEG 0
2133 #define IMAGE_TYPE_LOSSLESS 1
2135 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2136 double x1,double y1,
2137 double x2,double y2,
2138 double x3,double y3,
2139 double x4,double y4, int type, int multiply)
2141 gfxcolor_t*newpic=0;
2143 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2144 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2146 gfxline_t p1,p2,p3,p4,p5;
2147 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2148 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2149 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2150 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2151 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2153 {p1.x = (int)(p1.x*20)/20.0;
2154 p1.y = (int)(p1.y*20)/20.0;
2155 p2.x = (int)(p2.x*20)/20.0;
2156 p2.y = (int)(p2.y*20)/20.0;
2157 p3.x = (int)(p3.x*20)/20.0;
2158 p3.y = (int)(p3.y*20)/20.0;
2159 p4.x = (int)(p4.x*20)/20.0;
2160 p4.y = (int)(p4.y*20)/20.0;
2161 p5.x = (int)(p5.x*20)/20.0;
2162 p5.y = (int)(p5.y*20)/20.0;
2166 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2167 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2169 m.tx = p1.x - 0.5*multiply;
2170 m.ty = p1.y - 0.5*multiply;
2173 img.data = (gfxcolor_t*)data;
2177 if(type == IMAGE_TYPE_JPEG)
2178 /* TODO: pass image_dpi to device instead */
2179 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2182 dev->fillbitmap(dev, &p1, &img, &m, 0);
2185 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2186 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2188 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2191 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2192 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2194 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2198 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2199 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2200 GBool inlineImg, int mask, int*maskColors,
2201 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2203 /* the code in this function is *old*. It's not pretty, but it works. */
2205 double x1,y1,x2,y2,x3,y3,x4,y4;
2206 ImageStream *imgStr;
2211 unsigned char* maskbitmap = 0;
2214 ncomps = colorMap->getNumPixelComps();
2215 bits = colorMap->getBits();
2220 unsigned char buf[8];
2221 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2223 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2224 imgMaskStr->reset();
2225 unsigned char pal[256];
2226 int n = 1 << colorMap->getBits();
2231 maskColorMap->getGray(pixBuf, &gray);
2232 pal[t] = colToByte(gray);
2234 for (y = 0; y < maskHeight; y++) {
2235 for (x = 0; x < maskWidth; x++) {
2236 imgMaskStr->getPixel(buf);
2237 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2242 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2243 imgMaskStr->reset();
2244 for (y = 0; y < maskHeight; y++) {
2245 for (x = 0; x < maskWidth; x++) {
2246 imgMaskStr->getPixel(buf);
2248 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2256 imgStr = new ImageStream(str, width, ncomps,bits);
2259 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2261 msg("<verbose> Ignoring %d by %d image", width, height);
2262 unsigned char buf[8];
2264 for (y = 0; y < height; ++y)
2265 for (x = 0; x < width; ++x) {
2266 imgStr->getPixel(buf);
2274 this->transformXY(state, 0, 1, &x1, &y1);
2275 this->transformXY(state, 0, 0, &x2, &y2);
2276 this->transformXY(state, 1, 0, &x3, &y3);
2277 this->transformXY(state, 1, 1, &x4, &y4);
2280 /* as type 3 bitmaps are antialized, we need to place them
2281 at integer coordinates, otherwise flash player's antializing
2282 will kick in and make everything blurry */
2283 x1 = (int)(x1);y1 = (int)(y1);
2284 x2 = (int)(x2);y2 = (int)(y2);
2285 x3 = (int)(x3);y3 = (int)(y3);
2286 x4 = (int)(x4);y4 = (int)(y4);
2289 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2291 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2292 gfxglobals->pbminfo = 1;
2295 msg("<verbose> drawing %d by %d masked picture", width, height);
2297 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2298 msg("<notice> File contains jpeg pictures");
2299 gfxglobals->jpeginfo = 1;
2303 unsigned char buf[8];
2305 unsigned char*pic = new unsigned char[width*height];
2306 gfxcolor_t pal[256];
2308 state->getFillRGB(&rgb);
2310 memset(pal,255,sizeof(pal));
2311 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2312 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2313 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2314 pal[0].a = 255; pal[1].a = 0;
2317 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2318 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2319 for (y = 0; y < height; ++y)
2320 for (x = 0; x < width; ++x)
2322 imgStr->getPixel(buf);
2325 pic[width*y+x] = buf[0];
2329 unsigned char*pic2 = 0;
2332 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2341 height = realheight;
2345 /* make a black/white palette */
2347 float r = 255./(float)(numpalette-1);
2349 for(t=0;t<numpalette;t++) {
2350 pal[t].r = colToByte(rgb.r);
2351 pal[t].g = colToByte(rgb.g);
2352 pal[t].b = colToByte(rgb.b);
2353 pal[t].a = (unsigned char)(t*r);
2358 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2359 for (y = 0; y < height; ++y) {
2360 for (x = 0; x < width; ++x) {
2361 pic2[width*y+x] = pal[pic[y*width+x]];
2364 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2368 if(maskbitmap) free(maskbitmap);
2374 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2375 gfxcolor_t*pic=new gfxcolor_t[width*height];
2376 for (y = 0; y < height; ++y) {
2377 for (x = 0; x < width; ++x) {
2378 imgStr->getPixel(pixBuf);
2379 colorMap->getRGB(pixBuf, &rgb);
2380 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2381 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2382 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2383 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2385 int x1 = x*maskWidth/width;
2386 int y1 = y*maskHeight/height;
2387 int x2 = (x+1)*maskWidth/width;
2388 int y2 = (y+1)*maskHeight/height;
2390 unsigned int alpha=0;
2391 unsigned int count=0;
2392 for(xx=x1;xx<x2;xx++)
2393 for(yy=y1;yy<y2;yy++) {
2394 alpha += maskbitmap[yy*maskWidth+xx];
2398 pic[width*y+x].a = alpha / count;
2400 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2405 if(str->getKind()==strDCT)
2406 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2408 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2411 if(maskbitmap) free(maskbitmap);
2414 gfxcolor_t*pic=new gfxcolor_t[width*height];
2415 gfxcolor_t pal[256];
2416 int n = 1 << colorMap->getBits();
2418 for(t=0;t<256;t++) {
2420 colorMap->getRGB(pixBuf, &rgb);
2421 pal[t].r = (unsigned char)(colToByte(rgb.r));
2422 pal[t].g = (unsigned char)(colToByte(rgb.g));
2423 pal[t].b = (unsigned char)(colToByte(rgb.b));
2424 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2426 for (y = 0; y < height; ++y) {
2427 for (x = 0; x < width; ++x) {
2428 imgStr->getPixel(pixBuf);
2429 pic[width*y+x] = pal[pixBuf[0]];
2430 if(maskColors && *maskColors==pixBuf[0]) {
2431 pic[width*y+x].a = 0;
2436 if(maskWidth < width && maskHeight < height) {
2437 for(y = 0; y < height; y++) {
2438 for (x = 0; x < width; x++) {
2439 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2443 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2444 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2445 double dx = width / (double)maskWidth;
2446 double dy = height / (double)maskHeight;
2448 for(y = 0; y < maskHeight; y++) {
2450 for (x = 0; x < maskWidth; x++) {
2451 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2452 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2460 height = maskHeight;
2463 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2467 if(maskbitmap) free(maskbitmap);
2472 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2473 int width, int height, GBool invert,
2476 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2477 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2478 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2481 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2482 int width, int height, GfxImageColorMap *colorMap,
2483 int *maskColors, GBool inlineImg)
2485 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2486 colorMap?"colorMap":"no colorMap",
2487 maskColors?"maskColors":"no maskColors",
2489 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2490 colorMap?"colorMap":"no colorMap",
2491 maskColors?"maskColors":"no maskColors",
2494 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2495 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2496 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2499 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2500 int width, int height,
2501 GfxImageColorMap *colorMap,
2502 Stream *maskStr, int maskWidth, int maskHeight,
2505 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2506 colorMap?"colorMap":"no colorMap",
2507 maskWidth, maskHeight);
2508 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2509 colorMap?"colorMap":"no colorMap",
2510 maskWidth, maskHeight);
2512 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2513 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2514 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2517 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2518 int width, int height,
2519 GfxImageColorMap *colorMap,
2521 int maskWidth, int maskHeight,
2522 GfxImageColorMap *maskColorMap)
2524 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2525 colorMap?"colorMap":"no colorMap",
2526 maskWidth, maskHeight);
2527 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2528 colorMap?"colorMap":"no colorMap",
2529 maskWidth, maskHeight);
2531 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2532 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2533 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2536 void GFXOutputDev::stroke(GfxState *state)
2540 GfxPath * path = state->getPath();
2541 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2542 strokeGfxline(state, line, 0);
2546 void GFXOutputDev::fill(GfxState *state)
2548 gfxcolor_t col = getFillColor(state);
2549 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2551 GfxPath * path = state->getPath();
2552 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2553 if(!config_disable_polygon_conversion) {
2554 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2558 fillGfxLine(state, line, 0);
2562 void GFXOutputDev::eoFill(GfxState *state)
2564 gfxcolor_t col = getFillColor(state);
2565 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2567 GfxPath * path = state->getPath();
2568 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2569 fillGfxLine(state, line, 1);
2574 static const char* dirseparator()
2583 void addGlobalFont(const char*filename)
2585 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2586 memset(f, 0, sizeof(fontfile_t));
2587 f->filename = filename;
2588 int len = strlen(filename);
2589 char*r1 = strrchr((char*)filename, '/');
2590 char*r2 = strrchr((char*)filename, '\\');
2598 msg("<verbose> Adding font \"%s\".", filename);
2599 if(global_fonts_next) {
2600 global_fonts_next->next = f;
2601 global_fonts_next = global_fonts_next->next;
2603 global_fonts_next = global_fonts = f;
2607 void addGlobalLanguageDir(const char*dir)
2609 msg("<notice> Adding %s to language pack directories", dir);
2612 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2613 strcpy(config_file, dir);
2614 strcat(config_file, dirseparator());
2615 strcat(config_file, "add-to-xpdfrc");
2617 fi = fopen(config_file, "rb");
2619 msg("<error> Could not open %s", config_file);
2622 globalParams->parseFile(new GString(config_file), fi);
2626 void addGlobalFontDir(const char*dirname)
2628 #ifdef HAVE_DIRENT_H
2629 DIR*dir = opendir(dirname);
2631 msg("<warning> Couldn't open directory %s", dirname);
2637 ent = readdir (dir);
2641 char*name = ent->d_name;
2647 if(!strncasecmp(&name[l-4], ".pfa", 4))
2649 if(!strncasecmp(&name[l-4], ".pfb", 4))
2651 if(!strncasecmp(&name[l-4], ".ttf", 4))
2654 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2655 strcpy(fontname, dirname);
2656 strcat(fontname, dirseparator());
2657 strcat(fontname, name);
2658 addGlobalFont(fontname);
2662 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2665 msg("<warning> No dirent.h");
2669 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2670 GfxColorSpace *blendingColorSpace,
2671 GBool isolated, GBool knockout,
2674 const char*colormodename = "";
2676 if(blendingColorSpace) {
2677 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2679 dbg("beginTransparencyGroup device=%p %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", device, bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2680 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);
2682 //states[statepos].createsoftmask |= forSoftMask;
2683 states[statepos].createsoftmask = forSoftMask;
2684 states[statepos].transparencygroup = !forSoftMask;
2685 states[statepos].isolated = isolated;
2687 states[statepos].olddevice = this->device;
2688 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2689 dbg("this->device now %p (old: %p)", this->device, states[statepos].olddevice);
2691 gfxdevice_record_init(this->device, 0);
2693 /*if(!forSoftMask) { ////???
2694 state->setFillOpacity(0.0);
2699 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2702 gfxdevice_t*r = this->device;
2704 dbg("endTransparencyGroup this->device now back to %p (destroying %p)", states[statepos].olddevice, this->device);
2706 this->device = states[statepos].olddevice;
2708 msg("<error> Invalid state nesting");
2710 states[statepos].olddevice = 0;
2712 gfxresult_t*recording = r->finish(r);
2714 dbg(" forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2715 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%p/%p", states[statepos].createsoftmask, r, recording);
2717 if(states[statepos].createsoftmask) {
2718 states[statepos-1].softmaskrecording = recording;
2720 states[statepos-1].grouprecording = recording;
2723 states[statepos].createsoftmask = 0;
2724 states[statepos].transparencygroup = 0;
2728 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2730 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2731 "colordodge","colorburn","hardlight","softlight","difference",
2732 "exclusion","hue","saturation","color","luminosity"};
2734 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%p", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2735 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2737 if(state->getBlendMode() == gfxBlendNormal)
2738 infofeature("transparency groups");
2741 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2742 warnfeature(buffer, 0);
2745 gfxresult_t*grouprecording = states[statepos].grouprecording;
2747 int blendmode = state->getBlendMode();
2748 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2749 int alpha = (int)(state->getFillOpacity()*255);
2750 if(blendmode == gfxBlendMultiply && alpha>200)
2753 dbg("this->device=%p, this->device->name=%s\n", this->device, this->device->name);
2754 gfxdevice_ops_init(&ops, this->device, alpha);
2755 gfxresult_record_replay(grouprecording, &ops);
2758 grouprecording->destroy(grouprecording);
2760 states[statepos].grouprecording = 0;
2763 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2765 if(states[statepos].softmask) {
2766 /* shouldn't happen, but *does* happen */
2767 clearSoftMask(state);
2770 /* alpha = 1: retrieve mask values from alpha layer
2771 alpha = 0: retrieve mask values from luminance */
2773 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2774 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2775 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2776 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2778 infofeature("soft masks");
2780 warnfeature("soft masks from alpha channel",0);
2782 if(states[statepos].olddevice) {
2783 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2786 states[statepos].olddevice = this->device;
2787 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2788 gfxdevice_record_init(this->device, 0);
2790 dbg("softmaskrecording is %p (dev=%p) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2792 states[statepos].softmask = 1;
2793 states[statepos].softmask_alpha = alpha;
2796 static inline Guchar div255(int x) {
2797 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2800 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2802 if(c < min) c = min;
2803 if(c > max) c = max;
2807 void GFXOutputDev::clearSoftMask(GfxState *state)
2809 if(!states[statepos].softmask)
2811 states[statepos].softmask = 0;
2812 dbg("clearSoftMask statepos=%d", statepos);
2813 msg("<verbose> clearSoftMask statepos=%d", statepos);
2815 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2816 msg("<error> Error in softmask/tgroup ordering");
2820 gfxresult_t*mask = states[statepos].softmaskrecording;
2821 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2822 this->device = states[statepos].olddevice;
2824 /* get outline of all objects below the soft mask */
2825 gfxdevice_t uniondev;
2826 gfxdevice_union_init(&uniondev, 0);
2827 gfxresult_record_replay(below, &uniondev);
2828 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2829 uniondev.finish(&uniondev);
2830 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2831 gfxline_free(belowoutline);belowoutline=0;
2833 this->device->startclip(this->device, belowoutline);
2834 gfxresult_record_replay(below, this->device);
2835 gfxresult_record_replay(mask, this->device);
2836 this->device->endclip(this->device);
2839 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2840 if(width<=0 || height<=0)
2843 gfxdevice_t belowrender;
2844 gfxdevice_render_init(&belowrender);
2845 if(states[statepos+1].isolated) {
2846 belowrender.setparameter(&belowrender, "fillwhite", "1");
2848 belowrender.setparameter(&belowrender, "antialize", "2");
2849 belowrender.startpage(&belowrender, width, height);
2850 gfxresult_record_replay(below, &belowrender);
2851 belowrender.endpage(&belowrender);
2852 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2853 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2854 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2856 gfxdevice_t maskrender;
2857 gfxdevice_render_init(&maskrender);
2858 maskrender.startpage(&maskrender, width, height);
2859 gfxresult_record_replay(mask, &maskrender);
2860 maskrender.endpage(&maskrender);
2861 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2862 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2864 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2865 msg("<fatal> Internal error in mask drawing");
2870 for(y=0;y<height;y++) {
2871 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2872 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2873 for(x=0;x<width;x++) {
2875 if(states[statepos].softmask_alpha) {
2878 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2881 l2->a = div255(alpha*l2->a);
2883 /* DON'T premultiply alpha- this is done by fillbitmap,
2884 depending on the output device */
2885 //l2->r = div255(alpha*l2->r);
2886 //l2->g = div255(alpha*l2->g);
2887 //l2->b = div255(alpha*l2->b);
2893 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2896 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2897 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2899 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2901 mask->destroy(mask);
2902 below->destroy(below);
2903 maskresult->destroy(maskresult);
2904 belowresult->destroy(belowresult);
2905 states[statepos].softmaskrecording = 0;
2910 // public: ~MemCheck()
2912 // delete globalParams;globalParams=0;
2913 // Object::memCheck(stderr);
2914 // gMemReport(stderr);