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;
100 static char* lastfontdir = 0;
111 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
112 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
113 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
114 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
115 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
116 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
117 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
118 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
119 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
120 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
121 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
122 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
123 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
124 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
127 static int verbose = 0;
128 static int dbgindent = 1;
129 static void dbg(const char*format, ...)
136 va_start(arglist, format);
137 vsprintf(buf, format, arglist);
140 while(l && buf[l-1]=='\n') {
145 int indent = dbgindent;
154 GFXOutputGlobals*gfxglobals=0;
156 GFXOutputGlobals::GFXOutputGlobals()
159 this->pagebuflen = 0;
161 this->featurewarnings = 0;
163 this->textmodeinfo = 0;
166 this->gfxfontlist = gfxfontlist_create();
168 GFXOutputGlobals::~GFXOutputGlobals()
171 free(this->pages); this->pages = 0;
173 feature_t*f = this->featurewarnings;
175 feature_t*next = f->next;
177 free(f->string);f->string =0;
183 this->featurewarnings = 0;
185 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
188 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
190 feature_t*f = gfxglobals->featurewarnings;
192 if(!strcmp(feature, f->string))
196 f = (feature_t*)malloc(sizeof(feature_t));
197 f->string = strdup(feature);
198 f->next = gfxglobals->featurewarnings;
199 gfxglobals->featurewarnings = f;
201 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
202 if(this->config_break_on_warning) {
203 msg("<fatal> Aborting conversion due to unsupported feature");
207 msg("<notice> File contains %s",feature);
210 void GFXOutputDev::warnfeature(const char*feature,char fully)
212 showfeature(feature,fully,1);
214 void GFXOutputDev::infofeature(const char*feature)
216 showfeature(feature,0,0);
219 GFXOutputState::GFXOutputState() {
221 this->createsoftmask = 0;
222 this->transparencygroup = 0;
224 this->grouprecording = 0;
228 GBool GFXOutputDev::interpretType3Chars()
233 typedef struct _drawnchar
251 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
252 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
257 free(chars);chars = 0;
264 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
268 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
271 chars[num_chars].x = x;
272 chars[num_chars].y = y;
273 chars[num_chars].color = color;
274 chars[num_chars].charid = charid;
278 char* writeOutStdFont(fontentry* f)
283 char* tmpFileName = mktmpname(namebuf1);
285 sprintf(namebuf2, "%s.afm", tmpFileName);
286 fi = fopen(namebuf2, "wb");
289 fwrite(f->afm, 1, f->afmlen, fi);
292 sprintf(namebuf2, "%s.pfb", tmpFileName);
293 fi = fopen(namebuf2, "wb");
296 fwrite(f->pfb, 1, f->pfblen, fi);
298 return strdup(namebuf2);
300 void unlinkfont(char* filename)
305 msg("<verbose> Removing temporary font file %s", filename);
308 if(!strncmp(&filename[l-4],".afm",4)) {
309 memcpy(&filename[l-4],".pfb",4); unlink(filename);
310 memcpy(&filename[l-4],".pfa",4); unlink(filename);
311 memcpy(&filename[l-4],".afm",4);
314 if(!strncmp(&filename[l-4],".pfa",4)) {
315 memcpy(&filename[l-4],".afm",4); unlink(filename);
316 memcpy(&filename[l-4],".pfa",4);
319 if(!strncmp(&filename[l-4],".pfb",4)) {
320 memcpy(&filename[l-4],".afm",4); unlink(filename);
321 memcpy(&filename[l-4],".pfb",4);
326 static int config_use_fontconfig = 1;
327 static int fcinitcalled = 0;
329 GFXGlobalParams::GFXGlobalParams()
330 : GlobalParams((char*)"")
332 //setupBaseFonts(char *dir); //not tested yet
334 GFXGlobalParams::~GFXGlobalParams()
336 msg("<verbose> Performing cleanups");
338 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
339 if(pdf2t1map[t].fullfilename) {
340 unlinkfont(pdf2t1map[t].fullfilename);
343 #ifdef HAVE_FONTCONFIG
344 if(config_use_fontconfig && fcinitcalled)
348 #ifdef HAVE_FONTCONFIG
349 static char fc_ismatch(FcPattern*match, char*family, char*style)
351 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
352 FcBool scalable=FcFalse, outline=FcFalse;
353 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
354 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
355 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
356 FcPatternGetBool(match, "outline", 0, &outline);
357 FcPatternGetBool(match, "scalable", 0, &scalable);
359 if(scalable!=FcTrue || outline!=FcTrue)
362 if (!strcasecmp(fcfamily, family)) {
363 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
366 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
372 char* fontconfig_searchForFont(char*name)
374 #ifdef HAVE_FONTCONFIG
375 if(!config_use_fontconfig)
378 // call init ony once
382 // check whether we have a config file
383 char* configfile = (char*)FcConfigFilename(0);
384 int configexists = 0;
385 FILE*fi = fopen(configfile, "rb");
387 configexists = 1;fclose(fi);
388 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
390 msg("<debug> Initializing FontConfig (no configfile)");
394 /* A fontconfig instance which didn't find a configfile is unbelievably
395 cranky, so let's just write out a small xml file and make fontconfig
397 FcConfig*c = FcConfigCreate();
399 char* tmpFileName = mktmpname(namebuf);
400 FILE*fi = fopen(tmpFileName, "wb");
401 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
403 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
405 fprintf(fi, "<dir>~/.fonts</dir>\n");
407 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
409 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
410 fprintf(fi, "</fontconfig>\n");
412 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
413 FcConfigBuildFonts(c);
414 FcConfigSetCurrent(c);
418 msg("<debug> FontConfig Initialization failed. Disabling.");
419 config_use_fontconfig = 0;
422 FcConfig * config = FcConfigGetCurrent();
424 msg("<debug> FontConfig Config Initialization failed. Disabling.");
425 config_use_fontconfig = 0;
428 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
429 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
430 if(!set || !set->nfont) {
431 msg("<debug> FontConfig has zero fonts. Disabling.");
432 config_use_fontconfig = 0;
436 if(getLogLevel() >= LOGLEVEL_TRACE) {
438 for(t=0;t<set->nfont;t++) {
439 char*fcfamily=0,*fcstyle=0,*filename=0;
440 FcBool scalable=FcFalse, outline=FcFalse;
441 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
442 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
443 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
444 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
445 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
446 if(scalable && outline) {
447 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
453 char*family = strdup(name);
455 char*dash = strchr(family, '-');
456 FcPattern*pattern = 0;
460 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
461 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
463 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
464 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
468 FcConfigSubstitute(0, pattern, FcMatchPattern);
469 FcDefaultSubstitute(pattern);
471 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
474 for(t=0;t<set->nfont;t++) {
475 FcPattern*match = set->fonts[t];
476 //FcPattern*match = FcFontMatch(0, pattern, &result);
477 if(fc_ismatch(match, family, style)) {
479 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
480 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
483 //FcPatternDestroy(match);
484 msg("<debug> fontconfig: returning filename %s", filename);
486 FcPatternDestroy(pattern);
487 FcFontSetDestroy(set);
488 return filename?strdup(filename):0;
493 FcPatternDestroy(pattern);
494 FcFontSetDestroy(set);
501 static DisplayFontParamKind detectFontType(const char*filename)
503 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
504 return displayFontTT;
505 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
506 return displayFontT1;
507 return displayFontTT;
510 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
512 msg("<verbose> looking for font %s in global params", fontName->getCString());
514 char*name = fontName->getCString();
516 /* see if it is a pdf standard font */
518 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
519 if(!strcmp(name, pdf2t1map[t].pdffont)) {
520 if(!pdf2t1map[t].fullfilename) {
521 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
522 if(!pdf2t1map[t].fullfilename) {
523 msg("<error> Couldn't save default font- is the Temp Directory writable?");
525 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
528 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
529 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
534 int bestlen = 0x7fffffff;
535 const char*bestfilename = 0;
537 fontfile_t*f = global_fonts;
539 if(strstr(f->filename, name)) {
540 if(f->len < bestlen) {
542 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 DisplayFontParamKind kind = detectFontType(filename);
559 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
560 if(kind == displayFontTT) {
561 dfp->tt.fileName = new GString(filename);
563 dfp->t1.fileName = new GString(filename);
568 return GlobalParams::getDisplayFont(fontName);
571 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
574 gfxglobals = new GFXOutputGlobals();
578 this->xref = doc->getXRef();
580 this->type3active = 0;
583 this->user_movex = 0;
584 this->user_movey = 0;
587 this->user_clipx1 = 0;
588 this->user_clipy1 = 0;
589 this->user_clipx2 = 0;
590 this->user_clipy2 = 0;
591 this->current_text_stroke = 0;
592 this->current_text_clip = 0;
593 this->outer_clip_box = 0;
594 this->config_bigchar=0;
595 this->config_convertgradients=1;
596 this->config_break_on_warning=0;
597 this->config_remapunicode=0;
598 this->config_transparent=0;
599 this->config_extrafontdata = 0;
600 this->config_fontquality = 10;
601 this->config_optimize_polygons = 0;
602 this->config_multiply = 1;
603 this->dashPattern = 0;
605 memset(states, 0, sizeof(states));
608 void GFXOutputDev::setParameter(const char*key, const char*value)
610 if(!strcmp(key,"breakonwarning")) {
611 this->config_break_on_warning = atoi(value);
612 } else if(!strcmp(key,"remapunicode")) {
613 this->config_remapunicode = atoi(value);
614 } else if(!strcmp(key,"transparent")) {
615 this->config_transparent = atoi(value);
616 } else if(!strcmp(key,"extrafontdata")) {
617 this->config_extrafontdata = atoi(value);
618 } else if(!strcmp(key,"convertgradients")) {
619 this->config_convertgradients = atoi(value);
620 } else if(!strcmp(key,"multiply")) {
621 this->config_multiply = atoi(value);
622 if(this->config_multiply<1)
623 this->config_multiply=1;
624 } else if(!strcmp(key,"optimize_polygons")) {
625 this->config_optimize_polygons = atoi(value);
626 } else if(!strcmp(key,"bigchar")) {
627 this->config_bigchar = atoi(value);
628 } else if(!strcmp(key,"fontquality")) {
629 this->config_fontquality = atof(value);
630 if(this->config_fontquality<=1)
631 this->config_fontquality=1;
636 void GFXOutputDev::setDevice(gfxdevice_t*dev)
641 void GFXOutputDev::setMove(int x,int y)
643 this->user_movex = x;
644 this->user_movey = y;
647 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
649 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
650 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
652 this->user_clipx1 = x1;
653 this->user_clipy1 = y1;
654 this->user_clipx2 = x2;
655 this->user_clipy2 = y2;
658 static char*getFontName(GfxFont*font)
661 GString*gstr = font->getName();
662 char* fname = gstr==0?0:gstr->getCString();
666 sprintf(buf, "UFONT%d", r->num);
667 fontid = strdup(buf);
669 fontid = strdup(fname);
673 char* plus = strchr(fontid, '+');
674 if(plus && plus < &fontid[strlen(fontid)-1]) {
675 fontname = strdup(plus+1);
677 fontname = strdup(fontid);
683 static void dumpFontInfo(const char*loglevel, GfxFont*font);
684 static int lastdumps[1024];
685 static int lastdumppos = 0;
690 static void showFontError(GfxFont*font, int nr)
694 for(t=0;t<lastdumppos;t++)
695 if(lastdumps[t] == r->num)
699 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
700 lastdumps[lastdumppos++] = r->num;
702 msg("<warning> The following font caused problems:");
704 msg("<warning> The following font caused problems (substituting):");
706 msg("<warning> The following Type 3 Font will be rendered as graphics:");
707 dumpFontInfo("<warning>", font);
710 static void dumpFontInfo(const char*loglevel, GfxFont*font)
712 char* id = getFontID(font);
713 char* name = getFontName(font);
714 Ref* r=font->getID();
715 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
717 GString*gstr = font->getTag();
719 msg("%s| Tag: %s", loglevel, id);
721 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
723 GfxFontType type=font->getType();
725 case fontUnknownType:
726 msg("%s| Type: unknown",loglevel);
729 msg("%s| Type: 1",loglevel);
732 msg("%s| Type: 1C",loglevel);
735 msg("%s| Type: 3",loglevel);
738 msg("%s| Type: TrueType",loglevel);
741 msg("%s| Type: CIDType0",loglevel);
744 msg("%s| Type: CIDType0C",loglevel);
747 msg("%s| Type: CIDType2",loglevel);
752 GBool embedded = font->getEmbeddedFontID(&embRef);
754 if(font->getEmbeddedFontName()) {
755 embeddedName = font->getEmbeddedFontName()->getCString();
758 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
760 gstr = font->getExtFontFile();
762 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
764 // Get font descriptor flags.
765 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
766 if(font->isSerif()) msg("%s| is serif", loglevel);
767 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
768 if(font->isItalic()) msg("%s| is italic", loglevel);
769 if(font->isBold()) msg("%s| is bold", loglevel);
775 //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");}
776 //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");}
778 void dump_outline(gfxline_t*line)
781 if(line->type == gfx_moveTo) {
782 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
783 } else if(line->type == gfx_lineTo) {
784 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
785 } else if(line->type == gfx_splineTo) {
786 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
792 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
794 int num = path->getNumSubpaths();
797 double lastx=0,lasty=0,posx=0,posy=0;
800 msg("<warning> empty path");
804 gfxdrawer_target_gfxline(&draw);
806 for(t = 0; t < num; t++) {
807 GfxSubpath *subpath = path->getSubpath(t);
808 int subnum = subpath->getNumPoints();
809 double bx=0,by=0,cx=0,cy=0;
811 for(s=0;s<subnum;s++) {
814 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
817 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
818 draw.lineTo(&draw, lastx, lasty);
820 draw.moveTo(&draw, x,y);
825 } else if(subpath->getCurve(s) && cpos==0) {
829 } else if(subpath->getCurve(s) && cpos==1) {
837 draw.lineTo(&draw, x,y);
839 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
846 /* fix non-closed lines */
847 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
848 draw.lineTo(&draw, lastx, lasty);
850 gfxline_t*result = (gfxline_t*)draw.result(&draw);
852 gfxline_optimize(result);
857 GBool GFXOutputDev::useTilingPatternFill()
859 infofeature("tiled patterns");
860 // if(config_convertgradients)
864 GBool GFXOutputDev::useShadedFills()
866 infofeature("shaded fills");
867 if(config_convertgradients)
872 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
874 state->transform(x,y,nx,ny);
875 *nx += user_movex + clipmovex;
876 *ny += user_movey + clipmovey;
880 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
881 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
882 int paintType, Dict *resDict,
883 double *mat, double *bbox,
884 int x0, int y0, int x1, int y1,
885 double xStep, double yStep)
887 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
888 int paintType, Dict *resDict,
889 double *mat, double *bbox,
890 int x0, int y0, int x1, int y1,
891 double xStep, double yStep)
894 msg("<debug> tilingPatternFill");
895 infofeature("tiling pattern fills");
898 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
900 msg("<error> functionShadedFill not supported yet");
901 infofeature("function shaded fills");
904 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
908 colspace->getRGB(col, &rgb);
909 c.r = colToByte(rgb.r);
910 c.g = colToByte(rgb.g);
911 c.b = colToByte(rgb.b);
916 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
918 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
919 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
922 this->transformXY(state, x0,y0, &x0,&y0);
923 this->transformXY(state, x1,y1, &x1,&y1);
924 this->transformXY(state, x2,y2, &x2,&y2);
929 shading->getColor(0.0, &color0);
930 shading->getColor(0.5, &color1);
931 shading->getColor(1.0, &color2);
933 GfxColorSpace* colspace = shading->getColorSpace();
935 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
936 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
937 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
938 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
939 infofeature("radial shaded fills");
941 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
945 g[0].color = col2col(colspace, &color0);
946 g[1].color = col2col(colspace, &color1);
947 g[2].color = col2col(colspace, &color2);
952 gfxbbox_t b = states[statepos].clipbbox;
953 gfxline_t p1,p2,p3,p4,p5;
954 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
955 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
956 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
957 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
958 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
961 //m.m00 = (x3-x0); m.m10 = (x1-x0);
962 //m.m01 = (y3-y0); m.m11 = (y1-y0);
963 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
964 m.m00 = (x1-x0); m.m10 = (x2-x0);
965 m.m01 = (y1-y0); m.m11 = (y2-y0);
969 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
973 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
976 shading->getCoords(&x0,&y0,&x1,&y1);
977 this->transformXY(state, x0,y0,&x0,&y0);
978 this->transformXY(state, x1,y1,&x1,&y1);
983 shading->getColor(0.0, &color0);
984 shading->getColor(0.5, &color1);
985 shading->getColor(1.0, &color2);
987 GfxColorSpace* colspace = shading->getColorSpace();
989 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
990 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
991 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
992 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
994 infofeature("axial shaded fills");
996 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
1000 g[0].color = col2col(colspace, &color0);
1001 g[1].color = col2col(colspace, &color1);
1002 g[2].color = col2col(colspace, &color2);
1007 double xMin,yMin,xMax,yMax;
1008 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
1009 this->transformXY(state, xMin, yMin, &xMin, &yMin);
1010 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
1013 xMin = 1024; yMin = 1024;
1015 gfxbbox_t b = states[statepos].clipbbox;
1016 gfxline_t p1,p2,p3,p4,p5;
1017 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
1018 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
1019 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1020 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1021 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1023 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1024 the middle of the two control points */
1026 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1027 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1028 m.tx = (x0 + x1)/2 - 0.5;
1029 m.ty = (y0 + y1)/2 - 0.5;
1031 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1037 GBool GFXOutputDev::useDrawForm()
1039 infofeature("forms");
1042 void GFXOutputDev::drawForm(Ref id)
1044 msg("<error> drawForm not implemented");
1046 GBool GFXOutputDev::needNonText()
1050 void GFXOutputDev::endPage()
1052 msg("<verbose> endPage (GfxOutputDev)");
1053 if(outer_clip_box) {
1054 device->endclip(device);
1057 this->dashPattern = 0;
1058 /* notice: we're not fully done yet with this page- there might still be
1059 a few calls to drawLink() yet to come */
1062 static inline double sqr(double x) {return x*x;}
1064 #define STROKE_FILL 1
1065 #define STROKE_CLIP 2
1066 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1068 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1069 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1070 double miterLimit = state->getMiterLimit();
1071 double width = state->getTransformedLineWidth();
1074 double opaq = state->getStrokeOpacity();
1076 state->getFillRGB(&rgb);
1078 state->getStrokeRGB(&rgb);
1080 col.r = colToByte(rgb.r);
1081 col.g = colToByte(rgb.g);
1082 col.b = colToByte(rgb.b);
1083 col.a = (unsigned char)(opaq*255);
1085 gfx_capType capType = gfx_capRound;
1086 if(lineCap == 0) capType = gfx_capButt;
1087 else if(lineCap == 1) capType = gfx_capRound;
1088 else if(lineCap == 2) capType = gfx_capSquare;
1090 gfx_joinType joinType = gfx_joinRound;
1091 if(lineJoin == 0) joinType = gfx_joinMiter;
1092 else if(lineJoin == 1) joinType = gfx_joinRound;
1093 else if(lineJoin == 2) joinType = gfx_joinBevel;
1095 gfxline_t*line2 = 0;
1097 if(this->dashLength && this->dashPattern) {
1098 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1101 /* try to find out how much the transformation matrix would
1102 stretch the dashes, and factor that into the dash lengths.
1103 This is not the entirely correct approach- it would be
1104 better to first convert the path to an unscaled version,
1105 then apply dashing, and then transform the path using
1106 the current transformation matrix. However there are few
1107 PDFs which actually stretch a dashed path in a non-orthonormal
1109 double tx1, ty1, tx2, ty2;
1110 this->transformXY(state, 0, 0, &tx1, &ty1);
1111 this->transformXY(state, 1, 1, &tx2, &ty2);
1112 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1114 msg("<trace> %d dashes", this->dashLength);
1115 msg("<trace> | phase: %f", this->dashStart);
1116 for(t=0;t<this->dashLength;t++) {
1117 dash[t] = (float)this->dashPattern[t] * f;
1120 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1122 dash[this->dashLength] = -1;
1123 if(getLogLevel() >= LOGLEVEL_TRACE) {
1127 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1130 msg("<trace> After dashing:");
1133 if(getLogLevel() >= LOGLEVEL_TRACE) {
1134 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1136 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1137 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1139 col.r,col.g,col.b,col.a
1144 if(flags&STROKE_FILL) {
1145 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1146 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1147 if(getLogLevel() >= LOGLEVEL_TRACE) {
1148 dump_outline(gfxline);
1151 msg("<warning> Empty polygon (resulting from stroked line)");
1153 if(flags&STROKE_CLIP) {
1154 device->startclip(device, gfxline);
1155 states[statepos].clipping++;
1157 device->fill(device, gfxline, &col);
1159 gfxline_free(gfxline);
1162 if(flags&STROKE_CLIP)
1163 msg("<error> Stroke&clip not supported at the same time");
1164 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1168 gfxline_free(line2);
1171 gfxcolor_t getFillColor(GfxState * state)
1174 double opaq = state->getFillOpacity();
1175 state->getFillRGB(&rgb);
1177 col.r = colToByte(rgb.r);
1178 col.g = colToByte(rgb.g);
1179 col.b = colToByte(rgb.b);
1180 col.a = (unsigned char)(opaq*255);
1184 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1186 gfxcolor_t col = getFillColor(state);
1188 if(getLogLevel() >= LOGLEVEL_TRACE) {
1189 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1192 device->fill(device, line, &col);
1195 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1197 if(getLogLevel() >= LOGLEVEL_TRACE) {
1200 gfxbbox_t bbox = gfxline_getbbox(line);
1201 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1203 device->startclip(device, line);
1204 states[statepos].clipping++;
1207 void GFXOutputDev::clip(GfxState *state)
1209 GfxPath * path = state->getPath();
1210 msg("<trace> clip");
1211 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1212 if(config_optimize_polygons) {
1213 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1217 clipToGfxLine(state, line);
1221 void GFXOutputDev::eoClip(GfxState *state)
1223 GfxPath * path = state->getPath();
1224 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1225 clipToGfxLine(state, line);
1228 void GFXOutputDev::clipToStrokePath(GfxState *state)
1230 GfxPath * path = state->getPath();
1231 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1233 if(getLogLevel() >= LOGLEVEL_TRACE) {
1234 double width = state->getTransformedLineWidth();
1235 msg("<trace> cliptostrokepath width=%f", width);
1239 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1243 void GFXOutputDev::finish()
1245 if(outer_clip_box) {
1247 device->endclip(device);
1253 GFXOutputDev::~GFXOutputDev()
1256 if(this->dashPattern) {
1257 free(this->dashPattern);this->dashPattern = 0;
1260 GBool GFXOutputDev::upsideDown()
1264 GBool GFXOutputDev::useDrawChar()
1269 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1270 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1272 static char tmp_printstr[4096];
1273 char* makeStringPrintable(char*str)
1275 int len = strlen(str);
1282 for(t=0;t<len;t++) {
1287 tmp_printstr[t] = c;
1290 tmp_printstr[len++] = '.';
1291 tmp_printstr[len++] = '.';
1292 tmp_printstr[len++] = '.';
1294 tmp_printstr[len] = 0;
1295 return tmp_printstr;
1297 #define INTERNAL_FONT_SIZE 1024.0
1298 void GFXOutputDev::updateFontMatrix(GfxState*state)
1300 double* ctm = state->getCTM();
1301 double fontSize = state->getFontSize();
1302 double*textMat = state->getTextMat();
1304 /* taking the absolute value of horizScaling seems to be required for
1305 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1306 double hscale = fabs(state->getHorizScaling());
1308 // from xpdf-3.02/SplashOutputDev:updateFont
1309 double mm11 = textMat[0] * fontSize * hscale;
1310 double mm12 = textMat[1] * fontSize * hscale;
1311 double mm21 = textMat[2] * fontSize;
1312 double mm22 = textMat[3] * fontSize;
1314 // multiply with ctm, like state->getFontTransMat() does
1315 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1316 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1317 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1318 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1319 this->current_font_matrix.tx = 0;
1320 this->current_font_matrix.ty = 0;
1323 void GFXOutputDev::beginString(GfxState *state, GString *s)
1325 int render = state->getRender();
1326 if(current_text_stroke) {
1327 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1330 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1333 static gfxline_t* mkEmptyGfxShape(double x, double y)
1335 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1336 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1340 static char isValidUnicode(int c)
1342 if(c>=32 && c<0x2fffe)
1347 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1348 double dx, double dy,
1349 double originX, double originY,
1350 CharCode charid, int nBytes, Unicode *_u, int uLen)
1352 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1353 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1357 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1359 int render = state->getRender();
1360 gfxcolor_t col = getFillColor(state);
1362 // check for invisible text -- this is used by Acrobat Capture
1363 if (render == RENDER_INVISIBLE) {
1365 if(!config_extrafontdata)
1369 GfxFont*font = state->getFont();
1371 if(font->getType() == fontType3) {
1372 /* type 3 chars are passed as graphics */
1373 msg("<debug> type3 char at %f/%f", x, y);
1377 Unicode u = uLen?(_u[0]):0;
1378 msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d render=%d glyphid=%d font=%08x",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render, glyphid, current_gfxfont);
1380 gfxmatrix_t m = this->current_font_matrix;
1381 this->transformXY(state, x-originX, y-originY, &m.tx, &m.ty);
1382 //m.tx += originX; m.ty += originY;
1384 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1385 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1387 msg("<debug> Drawing glyph %d as shape", charid);
1388 if(!gfxglobals->textmodeinfo) {
1389 msg("<notice> Some texts will be rendered as shape");
1390 gfxglobals->textmodeinfo = 1;
1392 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1393 gfxline_t*tglyph = gfxline_clone(glyph);
1394 gfxline_transform(tglyph, &m);
1395 if((render&3) != RENDER_INVISIBLE) {
1396 gfxline_t*add = gfxline_clone(tglyph);
1397 current_text_stroke = gfxline_append(current_text_stroke, add);
1399 if(render&RENDER_CLIP) {
1400 gfxline_t*add = gfxline_clone(tglyph);
1401 current_text_clip = gfxline_append(current_text_clip, add);
1402 if(!current_text_clip) {
1403 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1406 gfxline_free(tglyph);
1410 void GFXOutputDev::endString(GfxState *state)
1412 int render = state->getRender();
1413 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1415 if(current_text_stroke) {
1416 /* fillstroke and stroke text rendering objects we can process right
1417 now (as there may be texts of other rendering modes in this
1418 text object)- clipping objects have to wait until endTextObject,
1420 device->setparameter(device, "mark","TXT");
1421 if((render&3) == RENDER_FILL) {
1422 fillGfxLine(state, current_text_stroke);
1423 gfxline_free(current_text_stroke);
1424 current_text_stroke = 0;
1425 } else if((render&3) == RENDER_FILLSTROKE) {
1426 fillGfxLine(state, current_text_stroke);
1427 strokeGfxline(state, current_text_stroke,0);
1428 gfxline_free(current_text_stroke);
1429 current_text_stroke = 0;
1430 } else if((render&3) == RENDER_STROKE) {
1431 strokeGfxline(state, current_text_stroke,0);
1432 gfxline_free(current_text_stroke);
1433 current_text_stroke = 0;
1435 device->setparameter(device, "mark","");
1439 void GFXOutputDev::endTextObject(GfxState *state)
1441 int render = state->getRender();
1442 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1444 if(current_text_clip) {
1445 device->setparameter(device, "mark","TXT");
1446 clipToGfxLine(state, current_text_clip);
1447 device->setparameter(device, "mark","");
1448 gfxline_free(current_text_clip);
1449 current_text_clip = 0;
1453 /* the logic seems to be as following:
1454 first, beginType3Char is called, with the charcode and the coordinates.
1455 if this function returns true, it already knew about the char and has now drawn it.
1456 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1457 called with some parameters.
1458 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1459 at the position first passed to beginType3Char). the char ends with endType3Char.
1461 The drawing operations between beginType3Char and endType3Char are somewhat different to
1462 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1463 color determines the color of a font)
1466 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1468 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1471 if(config_extrafontdata && current_fontinfo) {
1473 gfxmatrix_t m = this->current_font_matrix;
1474 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1475 m.m00*=INTERNAL_FONT_SIZE;
1476 m.m01*=INTERNAL_FONT_SIZE;
1477 m.m10*=INTERNAL_FONT_SIZE;
1478 m.m11*=INTERNAL_FONT_SIZE;
1480 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1481 msg("<error> Invalid charid %d for font", charid);
1484 gfxcolor_t col={0,0,0,0};
1485 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1486 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1490 /* the character itself is going to be passed using the draw functions */
1491 return gFalse; /* gTrue= is_in_cache? */
1494 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1496 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1499 void GFXOutputDev::endType3Char(GfxState *state)
1502 msg("<debug> endType3Char");
1505 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1507 this->currentpage = pageNum;
1509 int rot = doc->getPageRotate(1);
1510 gfxcolor_t white = {255,255,255,255};
1511 gfxcolor_t black = {255,0,0,0};
1513 gfxline_t clippath[5];
1515 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1516 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1517 Use CropBox, not MediaBox, as page size
1524 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1525 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1527 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1528 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1530 this->clipmovex = -(int)x1;
1531 this->clipmovey = -(int)y1;
1533 /* apply user clip box */
1534 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1535 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1536 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1537 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1538 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1539 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1542 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1544 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);
1546 msg("<verbose> page is rotated %d degrees", rot);
1548 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1549 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1550 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1551 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1552 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1553 device->startclip(device, clippath); outer_clip_box = 1;
1554 if(!config_transparent) {
1555 device->fill(device, clippath, &white);
1557 states[statepos].clipbbox.xmin = x1;
1558 states[statepos].clipbbox.ymin = x1;
1559 states[statepos].clipbbox.xmax = x2;
1560 states[statepos].clipbbox.ymax = y2;
1562 this->dashPattern = 0;
1563 this->dashLength = 0;
1564 this->dashStart = 0;
1568 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1570 double x1, y1, x2, y2;
1571 gfxline_t points[5];
1574 msg("<debug> drawlink");
1576 link->getRect(&x1, &y1, &x2, &y2);
1577 cvtUserToDev(x1, y1, &x, &y);
1578 points[0].type = gfx_moveTo;
1579 points[0].x = points[4].x = x + user_movex + clipmovex;
1580 points[0].y = points[4].y = y + user_movey + clipmovey;
1581 points[0].next = &points[1];
1582 cvtUserToDev(x2, y1, &x, &y);
1583 points[1].type = gfx_lineTo;
1584 points[1].x = x + user_movex + clipmovex;
1585 points[1].y = y + user_movey + clipmovey;
1586 points[1].next = &points[2];
1587 cvtUserToDev(x2, y2, &x, &y);
1588 points[2].type = gfx_lineTo;
1589 points[2].x = x + user_movex + clipmovex;
1590 points[2].y = y + user_movey + clipmovey;
1591 points[2].next = &points[3];
1592 cvtUserToDev(x1, y2, &x, &y);
1593 points[3].type = gfx_lineTo;
1594 points[3].x = x + user_movex + clipmovex;
1595 points[3].y = y + user_movey + clipmovey;
1596 points[3].next = &points[4];
1597 cvtUserToDev(x1, y1, &x, &y);
1598 points[4].type = gfx_lineTo;
1599 points[4].x = x + user_movex + clipmovex;
1600 points[4].y = y + user_movey + clipmovey;
1603 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1604 points[0].x, points[0].y,
1605 points[1].x, points[1].y,
1606 points[2].x, points[2].y,
1607 points[3].x, points[3].y);
1609 if(getLogLevel() >= LOGLEVEL_TRACE) {
1610 dump_outline(points);
1613 LinkAction*action=link->getAction();
1616 const char*type = "-?-";
1619 msg("<trace> drawlink action=%d", action->getKind());
1620 switch(action->getKind())
1624 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1625 LinkDest *dest=NULL;
1626 if (ha->getDest()==NULL)
1627 dest=catalog->findDest(ha->getNamedDest());
1628 else dest=ha->getDest();
1630 if (dest->isPageRef()){
1631 Ref pageref=dest->getPageRef();
1632 page=catalog->findPage(pageref.num,pageref.gen);
1634 else page=dest->getPageNum();
1635 sprintf(buf, "%d", page);
1642 LinkGoToR*l = (LinkGoToR*)action;
1643 GString*g = l->getFileName();
1645 s = strdup(g->getCString());
1647 /* if the GoToR link has no filename, then
1648 try to find a refernce in the *local*
1650 GString*g = l->getNamedDest();
1652 s = strdup(g->getCString());
1658 LinkNamed*l = (LinkNamed*)action;
1659 GString*name = l->getName();
1661 s = strdup(name->lowerCase()->getCString());
1662 named = name->getCString();
1665 if(strstr(s, "next") || strstr(s, "forward"))
1667 page = currentpage + 1;
1669 else if(strstr(s, "prev") || strstr(s, "back"))
1671 page = currentpage - 1;
1673 else if(strstr(s, "last") || strstr(s, "end"))
1675 if(gfxglobals->pages && gfxglobals->pagepos>0)
1676 page = gfxglobals->pages[gfxglobals->pagepos-1];
1678 else if(strstr(s, "first") || strstr(s, "top"))
1686 case actionLaunch: {
1688 LinkLaunch*l = (LinkLaunch*)action;
1689 GString * str = new GString(l->getFileName());
1690 GString * params = l->getParams();
1692 str->append(params);
1693 s = strdup(str->getCString());
1700 LinkURI*l = (LinkURI*)action;
1701 GString*g = l->getURI();
1703 url = g->getCString();
1708 case actionUnknown: {
1710 LinkUnknown*l = (LinkUnknown*)action;
1715 msg("<error> Unknown link type!");
1720 if(!s) s = strdup("-?-");
1722 msg("<trace> drawlink s=%s", s);
1724 if(!gfxglobals->linkinfo && (page || s))
1726 msg("<notice> File contains links");
1727 gfxglobals->linkinfo = 1;
1734 for(t=1;t<=gfxglobals->pagepos;t++) {
1735 if(gfxglobals->pages[t]==page) {
1744 sprintf(buf, "page%d", lpage);
1745 device->drawlink(device, points, buf);
1749 device->drawlink(device, points, s);
1752 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1756 void GFXOutputDev::saveState(GfxState *state) {
1757 dbg("saveState %08x", state); dbgindent+=2;
1759 msg("<trace> saveState %08x", state);
1762 msg("<fatal> Too many nested states in pdf.");
1766 states[statepos].state = state;
1767 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1768 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1769 states[statepos].clipping = 0;
1770 states[statepos].olddevice = 0;
1771 states[statepos].clipbbox = states[statepos-1].clipbbox;
1774 void GFXOutputDev::restoreState(GfxState *state) {
1775 dbgindent-=2; dbg("restoreState %08x", state);
1778 msg("<fatal> Invalid restoreState");
1781 msg("<trace> restoreState %08x%s%s", state,
1782 states[statepos].softmask?" (end softmask)":"",
1783 states[statepos].clipping?" (end clipping)":"");
1784 if(states[statepos].softmask) {
1785 clearSoftMask(state);
1789 while(states[statepos].clipping) {
1790 device->endclip(device);
1791 states[statepos].clipping--;
1793 if(states[statepos].state!=state) {
1794 msg("<fatal> bad state nesting");
1797 states[statepos].state=0;
1801 void GFXOutputDev::updateLineDash(GfxState *state)
1803 if(this->dashPattern) {
1804 free(this->dashPattern);this->dashPattern = 0;
1806 double *pattern = 0;
1807 state->getLineDash(&pattern, &this->dashLength, &this->dashStart);
1808 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1809 if(!this->dashLength) {
1810 this->dashPattern = 0;
1812 double*p = (double*)malloc(this->dashLength*sizeof(this->dashPattern[0]));
1813 memcpy(p, pattern, this->dashLength*sizeof(this->dashPattern[0]));
1814 this->dashPattern = p;
1818 void GFXOutputDev::updateLineWidth(GfxState *state)
1820 double width = state->getTransformedLineWidth();
1823 void GFXOutputDev::updateLineCap(GfxState *state)
1825 int c = state->getLineCap();
1828 void GFXOutputDev::updateLineJoin(GfxState *state)
1830 int j = state->getLineJoin();
1833 void GFXOutputDev::updateFillColor(GfxState *state)
1836 double opaq = state->getFillOpacity();
1837 state->getFillRGB(&rgb);
1839 void GFXOutputDev::updateFillOpacity(GfxState *state)
1842 double opaq = state->getFillOpacity();
1843 state->getFillRGB(&rgb);
1844 dbg("update fillopaq %f", opaq);
1846 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1848 double opaq = state->getFillOpacity();
1849 dbg("update strokeopaq %f", opaq);
1851 void GFXOutputDev::updateFillOverprint(GfxState *state)
1853 double opaq = state->getFillOverprint();
1854 dbg("update filloverprint %f", opaq);
1856 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1858 double opaq = state->getStrokeOverprint();
1859 dbg("update strokeoverprint %f", opaq);
1861 void GFXOutputDev::updateTransfer(GfxState *state)
1863 dbg("update transfer");
1867 void GFXOutputDev::updateStrokeColor(GfxState *state)
1870 double opaq = state->getStrokeOpacity();
1871 state->getStrokeRGB(&rgb);
1875 gfxfont_t* GFXOutputDev::createGfxFont(GfxFont*xpdffont, FontInfo*src)
1877 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1878 memset(font, 0, sizeof(gfxfont_t));
1880 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1881 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1885 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1887 //printf("%d glyphs\n", font->num_glyphs);
1888 font->num_glyphs = 0;
1889 font->ascent = fabs(src->descender);
1890 font->descent = fabs(src->ascender);
1892 for(t=0;t<src->num_glyphs;t++) {
1893 if(src->glyphs[t]) {
1894 SplashPath*path = src->glyphs[t]->path;
1895 int len = path?path->getLength():0;
1896 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1897 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1898 src->glyphs[t]->glyphid = font->num_glyphs;
1899 glyph->unicode = src->glyphs[t]->unicode;
1900 if(glyph->unicode >= font->max_unicode)
1901 font->max_unicode = glyph->unicode+1;
1903 gfxdrawer_target_gfxline(&drawer);
1907 for(s=0;s<len;s++) {
1910 path->getPoint(s, &x, &y, &f);
1913 if(f&splashPathFirst) {
1914 drawer.moveTo(&drawer, x*scale, y*scale);
1916 if(f&splashPathCurve) {
1918 path->getPoint(++s, &x2, &y2, &f);
1919 if(f&splashPathCurve) {
1921 path->getPoint(++s, &x3, &y3, &f);
1922 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1924 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1927 drawer.lineTo(&drawer, x*scale, y*scale);
1929 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1930 // (f&splashPathFirst)?"first":"",
1931 // (f&splashPathLast)?"last":"");
1934 glyph->line = (gfxline_t*)drawer.result(&drawer);
1935 if(src->glyphs[t]->advance>0) {
1936 glyph->advance = src->glyphs[t]->advance;
1938 msg("<warning> Approximating advance value for glyph %d", t);
1939 glyph->advance = xmax*scale;
1941 if(this->config_bigchar) {
1942 double max = src->glyphs[t]->advance_max;
1943 if(max>0 && max > glyph->advance) {
1944 glyph->advance = max;
1951 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1952 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1953 for(t=0;t<font->num_glyphs;t++) {
1954 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1955 font->unicode2glyph[font->glyphs[t].unicode] = t;
1959 msg("<trace> %d glyphs.", t, font->num_glyphs);
1963 void GFXOutputDev::updateFont(GfxState *state)
1965 GfxFont* gfxFont = state->getFont();
1969 char*id = getFontID(gfxFont);
1970 msg("<verbose> Updating font to %s", id);
1971 if(gfxFont->getType() == fontType3) {
1972 infofeature("Type3 fonts");
1973 if(!config_extrafontdata) {
1978 msg("<error> Internal Error: FontID is null");
1982 this->current_fontinfo = this->info->getFont(id);
1983 if(!this->current_fontinfo) {
1984 msg("<error> Internal Error: no fontinfo for font %s", id);
1987 if(!this->current_fontinfo->seen) {
1988 dumpFontInfo("<verbose>", gfxFont);
1991 gfxfont_t*font = gfxfontlist_findfont(gfxglobals->gfxfontlist,id);
1993 font = this->createGfxFont(gfxFont, current_fontinfo);
1994 font->id = strdup(id);
1995 gfxglobals->gfxfontlist = gfxfontlist_addfont(gfxglobals->gfxfontlist, font);
1997 device->addfont(device, font);
1999 current_gfxfont = font;
2002 updateFontMatrix(state);
2005 #define SQR(x) ((x)*(x))
2007 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
2009 if((newwidth<1 || newheight<1) ||
2010 (width<=newwidth || height<=newheight))
2012 unsigned char*newdata;
2014 newdata= (unsigned char*)malloc(newwidth*newheight);
2015 double fx = ((double)width)/newwidth;
2016 double fy = ((double)height)/newheight;
2018 int blocksize = (int)(8192/(fx*fy));
2019 int r = 8192*256/palettesize;
2020 for(x=0;x<newwidth;x++) {
2021 double ex = px + fx;
2022 int fromx = (int)px;
2024 int xweight1 = (int)((1-(px-fromx))*256);
2025 int xweight2 = (int)((ex-tox)*256);
2027 for(y=0;y<newheight;y++) {
2028 double ey = py + fy;
2029 int fromy = (int)py;
2031 int yweight1 = (int)((1-(py-fromy))*256);
2032 int yweight2 = (int)((ey-toy)*256);
2039 for(xx=fromx;xx<=tox;xx++)
2040 for(yy=fromy;yy<=toy;yy++) {
2041 int b = 1-data[width*yy+xx];
2043 if(xx==fromx) weight = (weight*xweight1)/256;
2044 if(xx==tox) weight = (weight*xweight2)/256;
2045 if(yy==fromy) weight = (weight*yweight1)/256;
2046 if(yy==toy) weight = (weight*yweight2)/256;
2049 //if(a) a=(palettesize-1)*r/blocksize;
2050 newdata[y*newwidth+x] = (a*blocksize)/r;
2058 #define IMAGE_TYPE_JPEG 0
2059 #define IMAGE_TYPE_LOSSLESS 1
2061 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2062 double x1,double y1,
2063 double x2,double y2,
2064 double x3,double y3,
2065 double x4,double y4, int type, int multiply)
2067 gfxcolor_t*newpic=0;
2069 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2070 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2072 gfxline_t p1,p2,p3,p4,p5;
2073 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2074 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2075 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2076 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2077 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2079 {p1.x = (int)(p1.x*20)/20.0;
2080 p1.y = (int)(p1.y*20)/20.0;
2081 p2.x = (int)(p2.x*20)/20.0;
2082 p2.y = (int)(p2.y*20)/20.0;
2083 p3.x = (int)(p3.x*20)/20.0;
2084 p3.y = (int)(p3.y*20)/20.0;
2085 p4.x = (int)(p4.x*20)/20.0;
2086 p4.y = (int)(p4.y*20)/20.0;
2087 p5.x = (int)(p5.x*20)/20.0;
2088 p5.y = (int)(p5.y*20)/20.0;
2092 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2093 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2095 m.tx = p1.x - 0.5*multiply;
2096 m.ty = p1.y - 0.5*multiply;
2099 img.data = (gfxcolor_t*)data;
2103 if(type == IMAGE_TYPE_JPEG)
2104 /* TODO: pass image_dpi to device instead */
2105 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2108 dev->fillbitmap(dev, &p1, &img, &m, 0);
2111 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2112 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2114 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG, multiply);
2117 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2118 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4, int multiply)
2120 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS, multiply);
2124 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2125 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2126 GBool inlineImg, int mask, int*maskColors,
2127 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2129 /* the code in this function is *old*. It's not pretty, but it works. */
2131 double x1,y1,x2,y2,x3,y3,x4,y4;
2132 ImageStream *imgStr;
2137 unsigned char* maskbitmap = 0;
2140 ncomps = colorMap->getNumPixelComps();
2141 bits = colorMap->getBits();
2146 unsigned char buf[8];
2147 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2149 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2150 imgMaskStr->reset();
2151 unsigned char pal[256];
2152 int n = 1 << colorMap->getBits();
2157 maskColorMap->getGray(pixBuf, &gray);
2158 pal[t] = colToByte(gray);
2160 for (y = 0; y < maskHeight; y++) {
2161 for (x = 0; x < maskWidth; x++) {
2162 imgMaskStr->getPixel(buf);
2163 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2168 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2169 imgMaskStr->reset();
2170 for (y = 0; y < maskHeight; y++) {
2171 for (x = 0; x < maskWidth; x++) {
2172 imgMaskStr->getPixel(buf);
2174 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2182 imgStr = new ImageStream(str, width, ncomps,bits);
2185 if(!width || !height || ((height+width)<=1 && (maskWidth+maskHeight)<=1))
2187 msg("<verbose> Ignoring %d by %d image", width, height);
2188 unsigned char buf[8];
2190 for (y = 0; y < height; ++y)
2191 for (x = 0; x < width; ++x) {
2192 imgStr->getPixel(buf);
2200 this->transformXY(state, 0, 1, &x1, &y1);
2201 this->transformXY(state, 0, 0, &x2, &y2);
2202 this->transformXY(state, 1, 0, &x3, &y3);
2203 this->transformXY(state, 1, 1, &x4, &y4);
2206 /* as type 3 bitmaps are antialized, we need to place them
2207 at integer coordinates, otherwise flash player's antializing
2208 will kick in and make everything blurry */
2209 x1 = (int)(x1);y1 = (int)(y1);
2210 x2 = (int)(x2);y2 = (int)(y2);
2211 x3 = (int)(x3);y3 = (int)(y3);
2212 x4 = (int)(x4);y4 = (int)(y4);
2215 if(!gfxglobals->pbminfo && !(str->getKind()==strDCT)) {
2217 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2218 gfxglobals->pbminfo = 1;
2221 msg("<verbose> drawing %d by %d masked picture", width, height);
2223 if(!gfxglobals->jpeginfo && (str->getKind()==strDCT)) {
2224 msg("<notice> File contains jpeg pictures");
2225 gfxglobals->jpeginfo = 1;
2229 unsigned char buf[8];
2231 unsigned char*pic = new unsigned char[width*height];
2232 gfxcolor_t pal[256];
2234 state->getFillRGB(&rgb);
2236 memset(pal,255,sizeof(pal));
2237 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2238 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2239 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2240 pal[0].a = 255; pal[1].a = 0;
2243 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2244 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2245 for (y = 0; y < height; ++y)
2246 for (x = 0; x < width; ++x)
2248 imgStr->getPixel(buf);
2251 pic[width*y+x] = buf[0];
2255 unsigned char*pic2 = 0;
2258 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2267 height = realheight;
2271 /* make a black/white palette */
2273 float r = 255./(float)(numpalette-1);
2275 for(t=0;t<numpalette;t++) {
2276 pal[t].r = colToByte(rgb.r);
2277 pal[t].g = colToByte(rgb.g);
2278 pal[t].b = colToByte(rgb.b);
2279 pal[t].a = (unsigned char)(t*r);
2284 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2285 for (y = 0; y < height; ++y) {
2286 for (x = 0; x < width; ++x) {
2287 pic2[width*y+x] = pal[pic[y*width+x]];
2290 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2294 if(maskbitmap) free(maskbitmap);
2300 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2301 gfxcolor_t*pic=new gfxcolor_t[width*height];
2302 for (y = 0; y < height; ++y) {
2303 for (x = 0; x < width; ++x) {
2304 imgStr->getPixel(pixBuf);
2305 colorMap->getRGB(pixBuf, &rgb);
2306 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2307 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2308 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2309 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2311 int x1 = x*maskWidth/width;
2312 int y1 = y*maskHeight/height;
2313 int x2 = (x+1)*maskWidth/width;
2314 int y2 = (y+1)*maskHeight/height;
2316 unsigned int alpha=0;
2317 unsigned int count=0;
2318 for(xx=x1;xx<x2;xx++)
2319 for(yy=y1;yy<y2;yy++) {
2320 alpha += maskbitmap[yy*maskWidth+xx];
2324 pic[width*y+x].a = alpha / count;
2326 pic[width*y+x].a = maskbitmap[y1*maskWidth+x1];
2331 if(str->getKind()==strDCT)
2332 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2334 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2337 if(maskbitmap) free(maskbitmap);
2340 gfxcolor_t*pic=new gfxcolor_t[width*height];
2341 gfxcolor_t pal[256];
2342 int n = 1 << colorMap->getBits();
2344 for(t=0;t<256;t++) {
2346 colorMap->getRGB(pixBuf, &rgb);
2348 {/*if(maskColors && *maskColors==t) {
2349 msg("<notice> Color %d is transparent", t);
2350 if (imgData->maskColors) {
2352 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2353 if (pix[i] < imgData->maskColors[2*i] ||
2354 pix[i] > imgData->maskColors[2*i+1]) {
2369 pal[t].r = (unsigned char)(colToByte(rgb.r));
2370 pal[t].g = (unsigned char)(colToByte(rgb.g));
2371 pal[t].b = (unsigned char)(colToByte(rgb.b));
2372 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2375 for (y = 0; y < height; ++y) {
2376 for (x = 0; x < width; ++x) {
2377 imgStr->getPixel(pixBuf);
2378 pic[width*y+x] = pal[pixBuf[0]];
2382 if(maskWidth < width && maskHeight < height) {
2383 for(y = 0; y < height; y++) {
2384 for (x = 0; x < width; x++) {
2385 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2389 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2390 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2391 double dx = width / maskWidth;
2392 double dy = height / maskHeight;
2394 for(y = 0; y < maskHeight; y++) {
2396 for (x = 0; x < maskWidth; x++) {
2397 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2398 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2406 height = maskHeight;
2409 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4, config_multiply);
2413 if(maskbitmap) free(maskbitmap);
2418 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2419 int width, int height, GBool invert,
2422 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2423 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2424 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2427 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2428 int width, int height, GfxImageColorMap *colorMap,
2429 int *maskColors, GBool inlineImg)
2431 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2432 colorMap?"colorMap":"no colorMap",
2433 maskColors?"maskColors":"no maskColors",
2435 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2436 colorMap?"colorMap":"no colorMap",
2437 maskColors?"maskColors":"no maskColors",
2440 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2441 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2442 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2445 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2446 int width, int height,
2447 GfxImageColorMap *colorMap,
2448 Stream *maskStr, int maskWidth, int maskHeight,
2451 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2452 colorMap?"colorMap":"no colorMap",
2453 maskWidth, maskHeight);
2454 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2455 colorMap?"colorMap":"no colorMap",
2456 maskWidth, maskHeight);
2458 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2459 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2460 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2463 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2464 int width, int height,
2465 GfxImageColorMap *colorMap,
2467 int maskWidth, int maskHeight,
2468 GfxImageColorMap *maskColorMap)
2470 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2471 colorMap?"colorMap":"no colorMap",
2472 maskWidth, maskHeight);
2473 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2474 colorMap?"colorMap":"no colorMap",
2475 maskWidth, maskHeight);
2477 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2478 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2479 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2482 void GFXOutputDev::stroke(GfxState *state)
2486 GfxPath * path = state->getPath();
2487 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2488 strokeGfxline(state, line, 0);
2492 void GFXOutputDev::fill(GfxState *state)
2494 gfxcolor_t col = getFillColor(state);
2495 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2497 GfxPath * path = state->getPath();
2498 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2499 if(config_optimize_polygons) {
2500 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2504 fillGfxLine(state, line);
2508 void GFXOutputDev::eoFill(GfxState *state)
2510 gfxcolor_t col = getFillColor(state);
2511 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2513 GfxPath * path = state->getPath();
2514 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2515 fillGfxLine(state, line);
2520 static const char* dirseparator()
2529 void addGlobalFont(const char*filename)
2531 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2532 memset(f, 0, sizeof(fontfile_t));
2533 f->filename = filename;
2534 int len = strlen(filename);
2535 char*r1 = strrchr(filename, '/');
2536 char*r2 = strrchr(filename, '\\');
2544 msg("<notice> Adding font \"%s\".", filename);
2545 if(global_fonts_next) {
2546 global_fonts_next->next = f;
2547 global_fonts_next = global_fonts_next->next;
2549 global_fonts_next = global_fonts = f;
2553 void addGlobalLanguageDir(const char*dir)
2555 msg("<notice> Adding %s to language pack directories", dir);
2558 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2559 strcpy(config_file, dir);
2560 strcat(config_file, dirseparator());
2561 strcat(config_file, "add-to-xpdfrc");
2563 fi = fopen(config_file, "rb");
2565 msg("<error> Could not open %s", config_file);
2568 globalParams->parseFile(new GString(config_file), fi);
2572 void addGlobalFontDir(const char*dirname)
2574 #ifdef HAVE_DIRENT_H
2575 msg("<notice> Adding %s to font directories", dirname);
2576 lastfontdir = strdup(dirname);
2577 DIR*dir = opendir(dirname);
2579 msg("<warning> Couldn't open directory %s", dirname);
2584 ent = readdir (dir);
2588 char*name = ent->d_name;
2594 if(!strncasecmp(&name[l-4], ".pfa", 4))
2596 if(!strncasecmp(&name[l-4], ".pfb", 4))
2598 if(!strncasecmp(&name[l-4], ".ttf", 4))
2601 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2602 strcpy(fontname, dirname);
2603 strcat(fontname, dirseparator());
2604 strcat(fontname, name);
2605 addGlobalFont(fontname);
2610 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2614 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2619 if(!gfxglobals->pages) {
2620 gfxglobals->pagebuflen = 1024;
2621 if(pdfpage > gfxglobals->pagebuflen)
2622 gfxglobals->pagebuflen = pdfpage+1;
2623 gfxglobals->pages = (int*)malloc(gfxglobals->pagebuflen*sizeof(int));
2624 memset(gfxglobals->pages, -1, gfxglobals->pagebuflen*sizeof(int));
2627 while(pdfpage >= gfxglobals->pagebuflen)
2629 int oldlen = gfxglobals->pagebuflen;
2630 gfxglobals->pagebuflen+=1024;
2631 gfxglobals->pages = (int*)realloc(gfxglobals->pages, gfxglobals->pagebuflen*sizeof(int));
2632 memset(&gfxglobals->pages[oldlen], -1, (gfxglobals->pagebuflen-oldlen)*sizeof(int));
2635 gfxglobals->pages[pdfpage] = outputpage;
2636 if(pdfpage > gfxglobals->pagepos)
2637 gfxglobals->pagepos = pdfpage;
2640 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2641 GfxColorSpace *blendingColorSpace,
2642 GBool isolated, GBool knockout,
2645 const char*colormodename = "";
2647 if(blendingColorSpace) {
2648 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2650 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);
2651 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);
2653 //states[statepos].createsoftmask |= forSoftMask;
2654 states[statepos].createsoftmask = forSoftMask;
2655 states[statepos].transparencygroup = !forSoftMask;
2656 states[statepos].isolated = isolated;
2658 states[statepos].olddevice = this->device;
2659 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2660 dbg("this->device now %08x (old: %08x)", this->device, states[statepos].olddevice);
2662 gfxdevice_record_init(this->device);
2664 /*if(!forSoftMask) { ////???
2665 state->setFillOpacity(0.0);
2670 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2673 gfxdevice_t*r = this->device;
2675 dbg("endTransparencyGroup this->device now back to %08x (destroying %08x)", states[statepos].olddevice, this->device);
2677 this->device = states[statepos].olddevice;
2679 msg("<fatal> bad state nesting in transparency group- PDF file broken?");
2680 /* if these errors occur more often, we should build a seperate
2681 transparency group stack, like xpdf/SplashOutputDev.cc does */
2682 restoreState(state);
2683 this->device = states[statepos].olddevice;
2685 states[statepos].olddevice = 0;
2687 gfxresult_t*recording = r->finish(r);
2689 dbg(" forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2690 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2692 if(states[statepos].createsoftmask) {
2693 states[statepos-1].softmaskrecording = recording;
2695 states[statepos-1].grouprecording = recording;
2698 states[statepos].createsoftmask = 0;
2699 states[statepos].transparencygroup = 0;
2703 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2705 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2706 "colordodge","colorburn","hardlight","softlight","difference",
2707 "exclusion","hue","saturation","color","luminosity"};
2709 dbg("paintTransparencyGroup blend=%s softmaskon=%d recording=%08x", blendmodes[state->getBlendMode()], states[statepos].softmask, states[statepos].grouprecording);
2710 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2712 if(state->getBlendMode() == gfxBlendNormal)
2713 infofeature("transparency groups");
2716 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2717 warnfeature(buffer, 0);
2720 gfxresult_t*grouprecording = states[statepos].grouprecording;
2722 int blendmode = state->getBlendMode();
2723 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2724 int alpha = (int)(state->getFillOpacity()*255);
2725 if(blendmode == gfxBlendMultiply && alpha>200)
2728 dbg("this->device=%08x, this->device->name=%s\n", this->device, this->device->name);
2729 gfxdevice_ops_init(&ops, this->device, alpha);
2730 gfxresult_record_replay(grouprecording, &ops);
2733 grouprecording->destroy(grouprecording);
2735 states[statepos].grouprecording = 0;
2738 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2740 if(states[statepos].softmask) {
2741 /* shouldn't happen, but *does* happen */
2742 clearSoftMask(state);
2745 /* alpha = 1: retrieve mask values from alpha layer
2746 alpha = 0: retrieve mask values from luminance */
2748 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2749 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2750 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2751 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2753 infofeature("soft masks");
2755 warnfeature("soft masks from alpha channel",0);
2757 if(states[statepos].olddevice) {
2758 msg("<fatal> Internal error: badly balanced softmasks/transparency groups");
2761 states[statepos].olddevice = this->device;
2762 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2763 gfxdevice_record_init(this->device);
2765 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2767 states[statepos].softmask = 1;
2768 states[statepos].softmask_alpha = alpha;
2771 static inline Guchar div255(int x) {
2772 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2775 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2777 if(c < min) c = min;
2778 if(c > max) c = max;
2782 void GFXOutputDev::clearSoftMask(GfxState *state)
2784 if(!states[statepos].softmask)
2786 states[statepos].softmask = 0;
2787 dbg("clearSoftMask statepos=%d", statepos);
2788 msg("<verbose> clearSoftMask statepos=%d", statepos);
2790 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2791 msg("<error> Error in softmask/tgroup ordering");
2795 gfxresult_t*mask = states[statepos].softmaskrecording;
2796 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2797 this->device = states[statepos].olddevice;
2799 /* get outline of all objects below the soft mask */
2800 gfxdevice_t uniondev;
2801 gfxdevice_union_init(&uniondev, 0);
2802 gfxresult_record_replay(below, &uniondev);
2803 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2804 uniondev.finish(&uniondev);
2805 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2806 gfxline_free(belowoutline);belowoutline=0;
2808 this->device->startclip(this->device, belowoutline);
2809 gfxresult_record_replay(below, this->device);
2810 gfxresult_record_replay(mask, this->device);
2811 this->device->endclip(this->device);
2814 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2815 if(width<=0 || height<=0)
2818 gfxdevice_t belowrender;
2819 gfxdevice_render_init(&belowrender);
2820 if(states[statepos+1].isolated) {
2821 belowrender.setparameter(&belowrender, "fillwhite", "1");
2823 belowrender.setparameter(&belowrender, "antialize", "2");
2824 belowrender.startpage(&belowrender, width, height);
2825 gfxresult_record_replay(below, &belowrender);
2826 belowrender.endpage(&belowrender);
2827 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2828 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2829 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2831 gfxdevice_t maskrender;
2832 gfxdevice_render_init(&maskrender);
2833 maskrender.startpage(&maskrender, width, height);
2834 gfxresult_record_replay(mask, &maskrender);
2835 maskrender.endpage(&maskrender);
2836 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2837 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2839 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2840 msg("<fatal> Internal error in mask drawing");
2845 for(y=0;y<height;y++) {
2846 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2847 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2848 for(x=0;x<width;x++) {
2850 if(states[statepos].softmask_alpha) {
2853 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2856 l2->a = div255(alpha*l2->a);
2858 /* DON'T premultiply alpha- this is done by fillbitmap,
2859 depending on the output device */
2860 //l2->r = div255(alpha*l2->r);
2861 //l2->g = div255(alpha*l2->g);
2862 //l2->b = div255(alpha*l2->b);
2868 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2871 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2872 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2874 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2876 mask->destroy(mask);
2877 below->destroy(below);
2878 maskresult->destroy(maskresult);
2879 belowresult->destroy(belowresult);
2880 states[statepos].softmaskrecording = 0;
2885 // public: ~MemCheck()
2887 // delete globalParams;globalParams=0;
2888 // Object::memCheck(stderr);
2889 // gMemReport(stderr);