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 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1425 msg("<debug> Drawing glyph %d as shape", charid);
1426 if(!gfxglobals->textmodeinfo) {
1427 msg("<notice> Some texts will be rendered as shape");
1428 gfxglobals->textmodeinfo = 1;
1430 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1431 gfxline_t*tglyph = gfxline_clone(glyph);
1432 gfxline_transform(tglyph, &m);
1433 if((render&3) != RENDER_INVISIBLE) {
1434 gfxline_t*add = gfxline_clone(tglyph);
1435 current_text_stroke = gfxline_append(current_text_stroke, add);
1437 if(render&RENDER_CLIP) {
1438 gfxline_t*add = gfxline_clone(tglyph);
1439 current_text_clip = gfxline_append(current_text_clip, add);
1440 if(!current_text_clip) {
1441 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1444 gfxline_free(tglyph);
1448 void GFXOutputDev::endString(GfxState *state)
1450 int render = state->getRender();
1451 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1453 if(current_text_stroke) {
1454 /* fillstroke and stroke text rendering objects we can process right
1455 now (as there may be texts of other rendering modes in this
1456 text object)- clipping objects have to wait until endTextObject,
1458 device->setparameter(device, "mark","TXT");
1459 if((render&3) == RENDER_FILL) {
1460 fillGfxLine(state, current_text_stroke, 0);
1461 gfxline_free(current_text_stroke);
1462 current_text_stroke = 0;
1463 } else if((render&3) == RENDER_FILLSTROKE) {
1464 fillGfxLine(state, current_text_stroke, 0);
1465 strokeGfxline(state, current_text_stroke,0);
1466 gfxline_free(current_text_stroke);
1467 current_text_stroke = 0;
1468 } else if((render&3) == RENDER_STROKE) {
1469 strokeGfxline(state, current_text_stroke,0);
1470 gfxline_free(current_text_stroke);
1471 current_text_stroke = 0;
1473 device->setparameter(device, "mark","");
1477 void GFXOutputDev::endTextObject(GfxState *state)
1479 int render = state->getRender();
1480 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1482 if(current_text_clip) {
1483 device->setparameter(device, "mark","TXT");
1484 clipToGfxLine(state, current_text_clip, 0);
1485 device->setparameter(device, "mark","");
1486 gfxline_free(current_text_clip);
1487 current_text_clip = 0;
1491 /* the logic seems to be as following:
1492 first, beginType3Char is called, with the charcode and the coordinates.
1493 if this function returns true, it already knew about the char and has now drawn it.
1494 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1495 called with some parameters.
1496 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1497 at the position first passed to beginType3Char). the char ends with endType3Char.
1499 The drawing operations between beginType3Char and endType3Char are somewhat different to
1500 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1501 color determines the color of a font)
1504 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1506 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1509 if(config_extrafontdata && current_fontinfo) {
1511 gfxmatrix_t m = this->current_font_matrix;
1512 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1513 m.m00*=INTERNAL_FONT_SIZE;
1514 m.m01*=INTERNAL_FONT_SIZE;
1515 m.m10*=INTERNAL_FONT_SIZE;
1516 m.m11*=INTERNAL_FONT_SIZE;
1518 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1519 msg("<error> Invalid charid %d for font", charid);
1522 gfxcolor_t col={0,0,0,0};
1523 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1524 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1528 /* the character itself is going to be passed using the draw functions */
1529 return gFalse; /* gTrue= is_in_cache? */
1532 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1534 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1537 void GFXOutputDev::endType3Char(GfxState *state)
1540 msg("<debug> endType3Char");
1543 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1545 this->currentpage = pageNum;
1547 int rot = doc->getPageRotate(1);
1548 gfxcolor_t white = {255,255,255,255};
1549 gfxcolor_t black = {255,0,0,0};
1551 gfxline_t clippath[5];
1553 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1554 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1555 Use CropBox, not MediaBox, as page size
1562 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1563 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1565 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1566 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1568 this->clipmovex = -(int)x1;
1569 this->clipmovey = -(int)y1;
1571 /* apply user clip box */
1572 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1573 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1574 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1575 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1576 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1577 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1579 x1 += this->clipmovex;
1580 y1 += this->clipmovey;
1581 x2 += this->clipmovex;
1582 y2 += this->clipmovey;
1585 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1587 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);
1589 msg("<verbose> page is rotated %d degrees", rot);
1591 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1592 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1593 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1594 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1595 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1596 device->startclip(device, clippath); outer_clip_box = 1;
1597 if(!config_transparent) {
1598 device->fill(device, clippath, &white);
1600 states[statepos].clipbbox.xmin = x1;
1601 states[statepos].clipbbox.ymin = x1;
1602 states[statepos].clipbbox.xmax = x2;
1603 states[statepos].clipbbox.ymax = y2;
1605 states[statepos].dashPattern = 0;
1606 states[statepos].dashLength = 0;
1607 states[statepos].dashStart = 0;
1611 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1613 double x1, y1, x2, y2;
1614 gfxline_t points[5];
1617 msg("<debug> drawlink");
1619 link->getRect(&x1, &y1, &x2, &y2);
1620 cvtUserToDev(x1, y1, &x, &y);
1621 points[0].type = gfx_moveTo;
1622 points[0].x = points[4].x = x + user_movex + clipmovex;
1623 points[0].y = points[4].y = y + user_movey + clipmovey;
1624 points[0].next = &points[1];
1625 cvtUserToDev(x2, y1, &x, &y);
1626 points[1].type = gfx_lineTo;
1627 points[1].x = x + user_movex + clipmovex;
1628 points[1].y = y + user_movey + clipmovey;
1629 points[1].next = &points[2];
1630 cvtUserToDev(x2, y2, &x, &y);
1631 points[2].type = gfx_lineTo;
1632 points[2].x = x + user_movex + clipmovex;
1633 points[2].y = y + user_movey + clipmovey;
1634 points[2].next = &points[3];
1635 cvtUserToDev(x1, y2, &x, &y);
1636 points[3].type = gfx_lineTo;
1637 points[3].x = x + user_movex + clipmovex;
1638 points[3].y = y + user_movey + clipmovey;
1639 points[3].next = &points[4];
1640 cvtUserToDev(x1, y1, &x, &y);
1641 points[4].type = gfx_lineTo;
1642 points[4].x = x + user_movex + clipmovex;
1643 points[4].y = y + user_movey + clipmovey;
1646 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1647 points[0].x, points[0].y,
1648 points[1].x, points[1].y,
1649 points[2].x, points[2].y,
1650 points[3].x, points[3].y);
1652 if(getLogLevel() >= LOGLEVEL_TRACE) {
1653 dump_outline(points);
1656 LinkAction*action=link->getAction();
1659 const char*type = "-?-";
1662 msg("<trace> drawlink action=%d", action->getKind());
1663 switch(action->getKind())
1667 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1668 LinkDest *dest=NULL;
1669 if (ha->getDest()==NULL)
1670 dest=catalog->findDest(ha->getNamedDest());
1672 dest=ha->getDest()->copy();
1674 if (dest->isPageRef()){
1675 Ref pageref=dest->getPageRef();
1676 page=catalog->findPage(pageref.num,pageref.gen);
1678 else page=dest->getPageNum();
1679 sprintf(buf, "%d", page);
1687 LinkGoToR*l = (LinkGoToR*)action;
1688 GString*g = l->getFileName();
1690 s = strdup(g->getCString());
1692 /* if the GoToR link has no filename, then
1693 try to find a refernce in the *local*
1695 GString*g = l->getNamedDest();
1697 s = strdup(g->getCString());
1703 LinkNamed*l = (LinkNamed*)action;
1704 GString*name = l->getName();
1706 s = strdup(name->lowerCase()->getCString());
1707 named = name->getCString();
1710 if(strstr(s, "next") || strstr(s, "forward"))
1712 page = currentpage + 1;
1714 else if(strstr(s, "prev") || strstr(s, "back"))
1716 page = currentpage - 1;
1718 else if(strstr(s, "last") || strstr(s, "end"))
1720 if(this->page2page && this->num_pages) {
1721 page = this->page2page[this->num_pages-1];
1724 else if(strstr(s, "first") || strstr(s, "top"))
1732 case actionLaunch: {
1734 LinkLaunch*l = (LinkLaunch*)action;
1735 GString * str = new GString(l->getFileName());
1736 GString * params = l->getParams();
1738 str->append(params);
1739 s = strdup(str->getCString());
1746 LinkURI*l = (LinkURI*)action;
1747 GString*g = l->getURI();
1749 url = g->getCString();
1754 case actionUnknown: {
1756 LinkUnknown*l = (LinkUnknown*)action;
1761 msg("<error> Unknown link type!");
1766 if(!s) s = strdup("-?-");
1768 msg("<trace> drawlink s=%s", s);
1770 if(!gfxglobals->linkinfo && (page || s))
1772 msg("<notice> File contains links");
1773 gfxglobals->linkinfo = 1;
1779 for(t=1;t<=this->num_pages;t++) {
1780 if(this->page2page[t]==page) {
1790 sprintf(buf, "page%d", lpage);
1791 device->drawlink(device, points, buf);
1795 device->drawlink(device, points, s);
1798 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1802 void GFXOutputDev::saveState(GfxState *state) {
1803 dbg("saveState %08x", state); dbgindent+=2;
1805 msg("<trace> saveState %08x", state);
1808 msg("<fatal> Too many nested states in pdf.");
1812 states[statepos].state = state;
1813 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1814 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1815 states[statepos].clipping = 0;
1816 states[statepos].olddevice = 0;
1817 states[statepos].clipbbox = states[statepos-1].clipbbox;
1819 states[statepos].dashPattern = states[statepos-1].dashPattern;
1820 states[statepos].dashStart = states[statepos-1].dashStart;
1821 states[statepos].dashLength = states[statepos-1].dashLength;
1824 void GFXOutputDev::restoreState(GfxState *state) {
1825 dbgindent-=2; dbg("restoreState %08x", state);
1828 msg("<fatal> Invalid restoreState");
1831 msg("<trace> restoreState %08x%s%s", state,
1832 states[statepos].softmask?" (end softmask)":"",
1833 states[statepos].clipping?" (end clipping)":"");
1834 if(states[statepos].softmask) {
1835 clearSoftMask(state);
1838 if(states[statepos].dashPattern) {
1839 if(!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern) {
1840 free(states[statepos].dashPattern);
1841 states[statepos].dashPattern = 0;
1847 while(states[statepos].clipping) {
1848 device->endclip(device);
1849 states[statepos].clipping--;
1851 if(states[statepos].state!=state) {
1852 msg("<fatal> bad state nesting");
1855 states[statepos].state=0;
1859 void GFXOutputDev::updateLineDash(GfxState *state)
1861 if(states[statepos].dashPattern &&
1862 (!statepos || states[statepos-1].dashPattern != states[statepos].dashPattern)) {
1863 free(states[statepos].dashPattern);
1864 states[statepos].dashPattern = 0;
1866 double *pattern = 0;
1869 state->getLineDash(&pattern, &dashLength, &dashStart);
1870 msg("<debug> updateLineDash, %d dashes", dashLength);
1872 states[statepos].dashPattern = 0;
1873 states[statepos].dashLength = 0;
1875 double*p = (double*)malloc(dashLength*sizeof(states[statepos].dashPattern[0]));
1876 memcpy(p, pattern, dashLength*sizeof(states[statepos].dashPattern[0]));
1877 states[statepos].dashPattern = p;
1878 states[statepos].dashLength = dashLength;
1879 states[statepos].dashStart = dashStart;
1883 void GFXOutputDev::setPageMap(int*page2page, int num_pages)
1885 this->page2page = page2page;
1886 this->num_pages = num_pages;
1889 void GFXOutputDev::updateLineWidth(GfxState *state)
1891 double width = state->getTransformedLineWidth();
1894 void GFXOutputDev::updateLineCap(GfxState *state)
1896 int c = state->getLineCap();
1899 void GFXOutputDev::updateLineJoin(GfxState *state)
1901 int j = state->getLineJoin();
1904 void GFXOutputDev::updateFillColor(GfxState *state)
1907 double opaq = state->getFillOpacity();
1908 state->getFillRGB(&rgb);
1910 void GFXOutputDev::updateFillOpacity(GfxState *state)
1913 double opaq = state->getFillOpacity();
1914 state->getFillRGB(&rgb);
1915 dbg("update fillopaq %f", opaq);
1917 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1919 double opaq = state->getFillOpacity();
1920 dbg("update strokeopaq %f", opaq);
1922 void GFXOutputDev::updateFillOverprint(GfxState *state)
1924 double opaq = state->getFillOverprint();
1925 dbg("update filloverprint %f", opaq);
1927 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1929 double opaq = state->getStrokeOverprint();
1930 dbg("update strokeoverprint %f", opaq);
1932 void GFXOutputDev::updateTransfer(GfxState *state)
1934 dbg("update transfer");
1938 void GFXOutputDev::updateStrokeColor(GfxState *state)
1941 double opaq = state->getStrokeOpacity();
1942 state->getStrokeRGB(&rgb);
1945 void GFXOutputDev::updateFont(GfxState *state)
1947 GfxFont* gfxFont = state->getFont();
1951 char*id = getFontID(gfxFont);
1952 msg("<verbose> Updating font to %s", id);
1953 if(gfxFont->getType() == fontType3) {
1954 infofeature("Type3 fonts");
1955 if(!config_extrafontdata) {
1960 msg("<error> Internal Error: FontID is null");
1964 this->current_fontinfo = this->info->getFont(id);
1966 if(!this->current_fontinfo) {
1967 msg("<error> Internal Error: no fontinfo for font %s", id);
1970 if(!this->current_fontinfo->seen) {
1971 dumpFontInfo("<verbose>", gfxFont);
1974 current_gfxfont = this->current_fontinfo->getGfxFont();
1975 device->addfont(device, current_gfxfont);
1978 updateFontMatrix(state);
1981 #define SQR(x) ((x)*(x))
1983 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1985 if((newwidth<1 || newheight<1) ||
1986 (width<=newwidth || height<=newheight))
1988 unsigned char*newdata;
1990 newdata= (unsigned char*)malloc(newwidth*newheight);
1991 double fx = ((double)width)/newwidth;
1992 double fy = ((double)height)/newheight;
1994 int blocksize = (int)(8192/(fx*fy));
1995 int r = 8192*256/palettesize;
1996 for(x=0;x<newwidth;x++) {
1997 double ex = px + fx;
1998 int fromx = (int)px;
2000 int xweight1 = (int)((1-(px-fromx))*256);
2001 int xweight2 = (int)((ex-tox)*256);
2003 for(y=0;y<newheight;y++) {
2004 double ey = py + fy;
2005 int fromy = (int)py;
2007 int yweight1 = (int)((1-(py-fromy))*256);
2008 int yweight2 = (int)((ey-toy)*256);
2015 for(xx=fromx;xx<=tox;xx++)
2016 for(yy=fromy;yy<=toy;yy++) {
2017 int b = 1-data[width*yy+xx];
2019 if(xx==fromx) weight = (weight*xweight1)/256;
2020 if(xx==tox) weight = (weight*xweight2)/256;
2021 if(yy==fromy) weight = (weight*yweight1)/256;
2022 if(yy==toy) weight = (weight*yweight2)/256;
2025 //if(a) a=(palettesize-1)*r/blocksize;
2026 newdata[y*newwidth+x] = (a*blocksize)/r;
2034 #define IMAGE_TYPE_JPEG 0
2035 #define IMAGE_TYPE_LOSSLESS 1
2037 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2038 double x1,double y1,
2039 double x2,double y2,
2040 double x3,double y3,
2041 double x4,double y4, int type, int multiply)
2043 gfxcolor_t*newpic=0;
2045 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2046 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2048 gfxline_t p1,p2,p3,p4,p5;
2049 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2050 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2051 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2052 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2053 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2055 {p1.x = (int)(p1.x*20)/20.0;
2056 p1.y = (int)(p1.y*20)/20.0;
2057 p2.x = (int)(p2.x*20)/20.0;
2058 p2.y = (int)(p2.y*20)/20.0;
2059 p3.x = (int)(p3.x*20)/20.0;
2060 p3.y = (int)(p3.y*20)/20.0;
2061 p4.x = (int)(p4.x*20)/20.0;
2062 p4.y = (int)(p4.y*20)/20.0;
2063 p5.x = (int)(p5.x*20)/20.0;
2064 p5.y = (int)(p5.y*20)/20.0;
2068 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2069 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2071 m.tx = p1.x - 0.5*multiply;
2072 m.ty = p1.y - 0.5*multiply;
2075 img.data = (gfxcolor_t*)data;
2079 if(type == IMAGE_TYPE_JPEG)
2080 /* TODO: pass image_dpi to device instead */
2081 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2084 dev->fillbitmap(dev, &p1, &img, &m, 0);
2087 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2088 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2090 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2093 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2094 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2096 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2100 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2101 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2102 GBool inlineImg, int mask, int*maskColors,
2103 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2105 /* the code in this function is *old*. It's not pretty, but it works. */
2107 double x1,y1,x2,y2,x3,y3,x4,y4;
2108 ImageStream *imgStr;
2113 unsigned char* maskbitmap = 0;
2116 ncomps = colorMap->getNumPixelComps();
2117 bits = colorMap->getBits();
2122 unsigned char buf[8];
2123 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2125 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2126 imgMaskStr->reset();
2127 unsigned char pal[256];
2128 int n = 1 << colorMap->getBits();
2133 maskColorMap->getGray(pixBuf, &gray);
2134 pal[t] = colToByte(gray);
2136 for (y = 0; y < maskHeight; y++) {
2137 for (x = 0; x < maskWidth; x++) {
2138 imgMaskStr->getPixel(buf);
2139 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2144 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2145 imgMaskStr->reset();
2146 for (y = 0; y < maskHeight; y++) {
2147 for (x = 0; x < maskWidth; x++) {
2148 imgMaskStr->getPixel(buf);
2150 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2158 imgStr = new ImageStream(str, width, ncomps,bits);
2161 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2163 msg("<verbose> Ignoring %d by %d image", width, height);
2164 unsigned char buf[8];
2166 for (y = 0; y < height; ++y)
2167 for (x = 0; x < width; ++x) {
2168 imgStr->getPixel(buf);
2176 this->transformXY(state, 0, 1, &x1, &y1);
2177 this->transformXY(state, 0, 0, &x2, &y2);
2178 this->transformXY(state, 1, 0, &x3, &y3);
2179 this->transformXY(state, 1, 1, &x4, &y4);
2182 /* as type 3 bitmaps are antialized, we need to place them
2183 at integer coordinates, otherwise flash player's antializing
2184 will kick in and make everything blurry */
2185 x1 = (int)(x1);y1 = (int)(y1);
2186 x2 = (int)(x2);y2 = (int)(y2);
2187 x3 = (int)(x3);y3 = (int)(y3);
2188 x4 = (int)(x4);y4 = (int)(y4);
2191 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2193 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2194 gfxglobals->pbminfo = 1;
2197 msg("<verbose> drawing %d by %d masked picture", width, height);
2199 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2200 msg("<notice> File contains jpeg pictures");
2201 gfxglobals->jpeginfo = 1;
2205 unsigned char buf[8];
2207 unsigned char*pic = new unsigned char[width*height];
2208 gfxcolor_t pal[256];
2210 state->getFillRGB(&rgb);
2212 memset(pal,255,sizeof(pal));
2213 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2214 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2215 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2216 pal[0].a = 255; pal[1].a = 0;
2219 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2220 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2221 for (y = 0; y < height; ++y)
2222 for (x = 0; x < width; ++x)
2224 imgStr->getPixel(buf);
2227 pic[width*y+x] = buf[0];
2231 unsigned char*pic2 = 0;
2234 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2243 height = realheight;
2247 /* make a black/white palette */
2249 float r = 255./(float)(numpalette-1);
2251 for(t=0;t<numpalette;t++) {
2252 pal[t].r = colToByte(rgb.r);
2253 pal[t].g = colToByte(rgb.g);
2254 pal[t].b = colToByte(rgb.b);
2255 pal[t].a = (unsigned char)(t*r);
2260 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2261 for (y = 0; y < height; ++y) {
2262 for (x = 0; x < width; ++x) {
2263 pic2[width*y+x] = pal[pic[y*width+x]];
2266 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2270 if(maskbitmap) free(maskbitmap);
2276 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2277 gfxcolor_t*pic=new gfxcolor_t[width*height];
2278 for (y = 0; y < height; ++y) {
2279 for (x = 0; x < width; ++x) {
2280 imgStr->getPixel(pixBuf);
2281 colorMap->getRGB(pixBuf, &rgb);
2282 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2283 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2284 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2285 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2287 int x1 = x*maskWidth/width;
2288 int y1 = y*maskHeight/height;
2289 int x2 = (x+1)*maskWidth/width;
2290 int y2 = (y+1)*maskHeight/height;
2292 unsigned int alpha=0;
2293 unsigned int count=0;
2294 for(xx=x1;xx<x2;xx++)
2295 for(yy=y1;yy<y2;yy++) {
2296 alpha += maskbitmap[yy*maskWidth+xx];
2300 pic[width*y+x].a = alpha / count;
2302 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2307 if(str->getKind()==strDCT)
2308 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2310 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2313 if(maskbitmap) free(maskbitmap);
2316 gfxcolor_t*pic=new gfxcolor_t[width*height];
2317 gfxcolor_t pal[256];
2318 int n = 1 << colorMap->getBits();
2320 for(t=0;t<256;t++) {
2322 colorMap->getRGB(pixBuf, &rgb);
2324 {/*if(maskColors && *maskColors==t) {
2325 msg("<notice> Color %d is transparent", t);
2326 if (imgData->maskColors) {
2328 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2329 if (pix[i] < imgData->maskColors[2*i] ||
2330 pix[i] > imgData->maskColors[2*i+1]) {
2345 pal[t].r = (unsigned char)(colToByte(rgb.r));
2346 pal[t].g = (unsigned char)(colToByte(rgb.g));
2347 pal[t].b = (unsigned char)(colToByte(rgb.b));
2348 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2351 for (y = 0; y < height; ++y) {
2352 for (x = 0; x < width; ++x) {
2353 imgStr->getPixel(pixBuf);
2354 pic[width*y+x] = pal[pixBuf[0]];
2358 if(maskWidth < width && maskHeight < height) {
2359 for(y = 0; y < height; y++) {
2360 for (x = 0; x < width; x++) {
2361 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2365 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2366 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2367 double dx = width / maskWidth;
2368 double dy = height / maskHeight;
2370 for(y = 0; y < maskHeight; y++) {
2372 for (x = 0; x < maskWidth; x++) {
2373 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2374 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2382 height = maskHeight;
2385 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2389 if(maskbitmap) free(maskbitmap);
2394 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2395 int width, int height, GBool invert,
2398 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2399 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2400 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2403 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2404 int width, int height, GfxImageColorMap *colorMap,
2405 int *maskColors, GBool inlineImg)
2407 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2408 colorMap?"colorMap":"no colorMap",
2409 maskColors?"maskColors":"no maskColors",
2411 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2412 colorMap?"colorMap":"no colorMap",
2413 maskColors?"maskColors":"no maskColors",
2416 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2417 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2418 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2421 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2422 int width, int height,
2423 GfxImageColorMap *colorMap,
2424 Stream *maskStr, int maskWidth, int maskHeight,
2427 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2428 colorMap?"colorMap":"no colorMap",
2429 maskWidth, maskHeight);
2430 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2431 colorMap?"colorMap":"no colorMap",
2432 maskWidth, maskHeight);
2434 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2435 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2436 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2439 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2440 int width, int height,
2441 GfxImageColorMap *colorMap,
2443 int maskWidth, int maskHeight,
2444 GfxImageColorMap *maskColorMap)
2446 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2447 colorMap?"colorMap":"no colorMap",
2448 maskWidth, maskHeight);
2449 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2450 colorMap?"colorMap":"no colorMap",
2451 maskWidth, maskHeight);
2453 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2454 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2455 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2458 void GFXOutputDev::stroke(GfxState *state)
2462 GfxPath * path = state->getPath();
2463 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2464 strokeGfxline(state, line, 0);
2468 void GFXOutputDev::fill(GfxState *state)
2470 gfxcolor_t col = getFillColor(state);
2471 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2473 GfxPath * path = state->getPath();
2474 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2475 if(!config_disable_polygon_conversion) {
2476 gfxline_t*line2 = gfxpoly_circular_to_evenodd(line, DEFAULT_GRID);
2480 fillGfxLine(state, line, 0);
2484 void GFXOutputDev::eoFill(GfxState *state)
2486 gfxcolor_t col = getFillColor(state);
2487 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2489 GfxPath * path = state->getPath();
2490 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2491 fillGfxLine(state, line, 1);
2496 static const char* dirseparator()
2505 void addGlobalFont(const char*filename)
2507 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2508 memset(f, 0, sizeof(fontfile_t));
2509 f->filename = filename;
2510 int len = strlen(filename);
2511 char*r1 = strrchr((char*)filename, '/');
2512 char*r2 = strrchr((char*)filename, '\\');
2520 msg("<verbose> Adding font \"%s\".", filename);
2521 if(global_fonts_next) {
2522 global_fonts_next->next = f;
2523 global_fonts_next = global_fonts_next->next;
2525 global_fonts_next = global_fonts = f;
2529 void addGlobalLanguageDir(const char*dir)
2531 msg("<notice> Adding %s to language pack directories", dir);
2534 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2535 strcpy(config_file, dir);
2536 strcat(config_file, dirseparator());
2537 strcat(config_file, "add-to-xpdfrc");
2539 fi = fopen(config_file, "rb");
2541 msg("<error> Could not open %s", config_file);
2544 globalParams->parseFile(new GString(config_file), fi);
2548 void addGlobalFontDir(const char*dirname)
2550 #ifdef HAVE_DIRENT_H
2551 DIR*dir = opendir(dirname);
2553 msg("<warning> Couldn't open directory %s", dirname);
2559 ent = readdir (dir);
2563 char*name = ent->d_name;
2569 if(!strncasecmp(&name[l-4], ".pfa", 4))
2571 if(!strncasecmp(&name[l-4], ".pfb", 4))
2573 if(!strncasecmp(&name[l-4], ".ttf", 4))
2576 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2577 strcpy(fontname, dirname);
2578 strcat(fontname, dirseparator());
2579 strcat(fontname, name);
2580 addGlobalFont(fontname);
2584 msg("<notice> Added %s to font directories (%d fonts)", dirname, fonts);
2587 msg("<warning> No dirent.h");
2591 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2592 GfxColorSpace *blendingColorSpace,
2593 GBool isolated, GBool knockout,
2596 const char*colormodename = "";
2598 if(blendingColorSpace) {
2599 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2601 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);
2602 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);
2604 //states[statepos].createsoftmask |= forSoftMask;
2605 states[statepos].createsoftmask = forSoftMask;
2606 states[statepos].transparencygroup = !forSoftMask;
2607 states[statepos].isolated = isolated;
2609 states[statepos].olddevice = this->device;
2610 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2611 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2613 gfxdevice_record_init(this->device);
2615 /*if(!forSoftMask) { ////???
2616 state->setFillOpacity(0.0);
2621 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2624 gfxdevice_t*r = this->device;
2626 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2628 this->device = states[statepos].olddevice;
2630 msg("<fatal> Bad state nesting in transparency group");
2631 msg("<fatal> Notice: this is a known problem, which will be fixed in 0.9.1");
2632 msg("<fatal> In the meantime, please convert the file with -s poly2bitmap");
2633 restoreState(state);
2634 this->device = states[statepos].olddevice;
2636 states[statepos].olddevice = 0;
2638 gfxresult_t*recording = r->finish(r);
2640 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2641 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2643 if(states[statepos].createsoftmask) {
2644 states[statepos-1].softmaskrecording = recording;
2646 states[statepos-1].grouprecording = recording;
2649 states[statepos].createsoftmask = 0;
2650 states[statepos].transparencygroup = 0;
2654 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2656 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2657 "colordodge","colorburn","hardlight","softlight","difference",
2658 "exclusion","hue","saturation","color","luminosity"};
2660 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2661 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2663 if(state->getBlendMode() == gfxBlendNormal)
2664 infofeature("transparency groups");
2667 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2668 warnfeature(buffer, 0);
2671 gfxresult_t*grouprecording = states[statepos].grouprecording;
2673 int blendmode = state->getBlendMode();
2674 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2675 int alpha = (int)(state->getFillOpacity()*255);
2676 if(blendmode == gfxBlendMultiply && alpha>200)
2679 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2680 gfxdevice_ops_init(&ops, this->device, alpha);
2681 gfxresult_record_replay(grouprecording, &ops);
2684 grouprecording->destroy(grouprecording);
2686 states[statepos].grouprecording = 0;
2689 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2691 if(states[statepos].softmask) {
2692 /* shouldn't happen, but *does* happen */
2693 clearSoftMask(state);
2696 /* alpha = 1: retrieve mask values from alpha layer
2697 alpha = 0: retrieve mask values from luminance */
2699 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2700 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2701 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2702 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2704 infofeature("soft masks");
2706 warnfeature("soft masks from alpha channel",0);
2708 if(states[statepos].olddevice) {
2709 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2712 states[statepos].olddevice = this->device;
2713 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2714 gfxdevice_record_init(this->device);
2716 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2718 states[statepos].softmask = 1;
2719 states[statepos].softmask_alpha = alpha;
2722 static inline Guchar div255(int x) {
2723 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2726 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2728 if(c < min) c = min;
2729 if(c > max) c = max;
2733 void GFXOutputDev::clearSoftMask(GfxState *state)
2735 if(!states[statepos].softmask)
2737 states[statepos].softmask = 0;
2738 dbg("clearSoftMask statepos=%d", statepos);
2739 msg("<verbose> clearSoftMask statepos=%d", statepos);
2741 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2742 msg("<error> Error in softmask/tgroup ordering");
2746 gfxresult_t*mask = states[statepos].softmaskrecording;
2747 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2748 this->device = states[statepos].olddevice;
2750 /* get outline of all objects below the soft mask */
2751 gfxdevice_t uniondev;
2752 gfxdevice_union_init(&uniondev, 0);
2753 gfxresult_record_replay(below, &uniondev);
2754 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2755 uniondev.finish(&uniondev);
2756 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2757 gfxline_free(belowoutline);belowoutline=0;
2759 this->device->startclip(this->device, belowoutline);
2760 gfxresult_record_replay(below, this->device);
2761 gfxresult_record_replay(mask, this->device);
2762 this->device->endclip(this->device);
2765 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2766 if(width<=0 || height<=0)
2769 gfxdevice_t belowrender;
2770 gfxdevice_render_init(&belowrender);
2771 if(states[statepos+1].isolated) {
2772 belowrender.setparameter(&belowrender, "fillwhite", "1");
2774 belowrender.setparameter(&belowrender, "antialize", "2");
2775 belowrender.startpage(&belowrender, width, height);
2776 gfxresult_record_replay(below, &belowrender);
2777 belowrender.endpage(&belowrender);
2778 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2779 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2780 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2782 gfxdevice_t maskrender;
2783 gfxdevice_render_init(&maskrender);
2784 maskrender.startpage(&maskrender, width, height);
2785 gfxresult_record_replay(mask, &maskrender);
2786 maskrender.endpage(&maskrender);
2787 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2788 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2790 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2791 msg("<fatal> Internal error in mask drawing");
2796 for(y=0;y<height;y++) {
2797 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2798 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2799 for(x=0;x<width;x++) {
2801 if(states[statepos].softmask_alpha) {
2804 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2807 l2->a = div255(alpha*l2->a);
2809 /* DON'T premultiply alpha- this is done by fillbitmap,
2810 depending on the output device */
2811 //l2->r = div255(alpha*l2->r);
2812 //l2->g = div255(alpha*l2->g);
2813 //l2->b = div255(alpha*l2->b);
2819 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2822 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2823 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2825 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2827 mask->destroy(mask);
2828 below->destroy(below);
2829 maskresult->destroy(maskresult);
2830 belowresult->destroy(belowresult);
2831 states[statepos].softmaskrecording = 0;
2836 // public: ~MemCheck()
2838 // delete globalParams;globalParams=0;
2839 // Object::memCheck(stderr);
2840 // gMemReport(stderr);