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>
54 #include "OutputDev.h"
57 #include "CharCodeToUnicode.h"
58 #include "NameToUnicodeTable.h"
59 #include "GlobalParams.h"
60 #include "FoFiType1C.h"
61 #include "FoFiTrueType.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 = 0;
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;
155 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
157 feature_t*f = this->featurewarnings;
159 if(!strcmp(feature, f->string))
163 f = (feature_t*)malloc(sizeof(feature_t));
164 f->string = strdup(feature);
165 f->next = this->featurewarnings;
166 this->featurewarnings = f;
168 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
169 if(this->config_break_on_warning) {
170 msg("<fatal> Aborting conversion due to unsupported feature");
174 msg("<notice> File contains %s",feature);
177 void GFXOutputDev::warnfeature(const char*feature,char fully)
179 showfeature(feature,fully,1);
181 void GFXOutputDev::infofeature(const char*feature)
183 showfeature(feature,0,0);
186 GFXOutputState::GFXOutputState() {
188 this->createsoftmask = 0;
189 this->transparencygroup = 0;
191 this->grouprecording = 0;
195 GBool GFXOutputDev::interpretType3Chars()
200 typedef struct _drawnchar
218 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
219 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224 free(chars);chars = 0;
231 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
235 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
238 chars[num_chars].x = x;
239 chars[num_chars].y = y;
240 chars[num_chars].color = color;
241 chars[num_chars].charid = charid;
245 char* writeOutStdFont(fontentry* f)
250 char* tmpFileName = mktmpname(namebuf1);
252 sprintf(namebuf2, "%s.afm", tmpFileName);
253 fi = fopen(namebuf2, "wb");
256 fwrite(f->afm, 1, f->afmlen, fi);
259 sprintf(namebuf2, "%s.pfb", tmpFileName);
260 fi = fopen(namebuf2, "wb");
263 fwrite(f->pfb, 1, f->pfblen, fi);
265 return strdup(namebuf2);
267 void unlinkfont(char* filename)
272 msg("<verbose> Removing temporary font file %s", filename);
275 if(!strncmp(&filename[l-4],".afm",4)) {
276 memcpy(&filename[l-4],".pfb",4); unlink(filename);
277 memcpy(&filename[l-4],".pfa",4); unlink(filename);
278 memcpy(&filename[l-4],".afm",4);
281 if(!strncmp(&filename[l-4],".pfa",4)) {
282 memcpy(&filename[l-4],".afm",4); unlink(filename);
283 memcpy(&filename[l-4],".pfa",4);
286 if(!strncmp(&filename[l-4],".pfb",4)) {
287 memcpy(&filename[l-4],".afm",4); unlink(filename);
288 memcpy(&filename[l-4],".pfb",4);
293 static int config_use_fontconfig = 1;
294 static int fcinitcalled = 0;
296 GFXGlobalParams::GFXGlobalParams()
299 //setupBaseFonts(char *dir); //not tested yet
301 GFXGlobalParams::~GFXGlobalParams()
303 msg("<verbose> Performing cleanups");
305 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
306 if(pdf2t1map[t].fullfilename) {
307 unlinkfont(pdf2t1map[t].fullfilename);
310 #ifdef HAVE_FONTCONFIG
311 if(config_use_fontconfig && fcinitcalled)
315 #ifdef HAVE_FONTCONFIG
316 static char fc_ismatch(FcPattern*match, char*family, char*style)
318 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
319 FcBool scalable=FcFalse, outline=FcFalse;
320 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
321 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
322 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
323 FcPatternGetBool(match, "outline", 0, &outline);
324 FcPatternGetBool(match, "scalable", 0, &scalable);
326 if(scalable!=FcTrue || outline!=FcTrue)
329 if (!strcasecmp(fcfamily, family)) {
330 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
333 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
339 char* fontconfig_searchForFont(char*name)
341 #ifdef HAVE_FONTCONFIG
342 if(!config_use_fontconfig)
345 // call init ony once
349 // check whether we have a config file
350 char* configfile = (char*)FcConfigFilename(0);
351 int configexists = 0;
352 FILE*fi = fopen(configfile, "rb");
354 configexists = 1;fclose(fi);
355 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
357 msg("<debug> Initializing FontConfig (no configfile)");
361 /* A fontconfig instance which didn't find a configfile is unbelievably
362 cranky, so let's just write out a small xml file and make fontconfig
364 FcConfig*c = FcConfigCreate();
366 char* tmpFileName = mktmpname(namebuf);
367 FILE*fi = fopen(tmpFileName, "wb");
368 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
370 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
372 fprintf(fi, "<dir>~/.fonts</dir>\n");
374 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
376 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
377 fprintf(fi, "</fontconfig>\n");
379 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
380 FcConfigBuildFonts(c);
381 FcConfigSetCurrent(c);
385 msg("<debug> FontConfig Initialization failed. Disabling.");
386 config_use_fontconfig = 0;
389 FcConfig * config = FcConfigGetCurrent();
391 msg("<debug> FontConfig Config Initialization failed. Disabling.");
392 config_use_fontconfig = 0;
395 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
396 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
397 if(!set || !set->nfont) {
398 msg("<debug> FontConfig has zero fonts. Disabling.");
399 config_use_fontconfig = 0;
403 if(getLogLevel() >= LOGLEVEL_TRACE) {
405 for(t=0;t<set->nfont;t++) {
406 char*fcfamily=0,*fcstyle=0,*filename=0;
407 FcBool scalable=FcFalse, outline=FcFalse;
408 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
409 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
410 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
411 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
412 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
413 if(scalable && outline) {
414 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
420 char*family = strdup(name);
422 char*dash = strchr(family, '-');
423 FcPattern*pattern = 0;
427 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
428 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
430 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
431 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
435 FcConfigSubstitute(0, pattern, FcMatchPattern);
436 FcDefaultSubstitute(pattern);
438 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
441 for(t=0;t<set->nfont;t++) {
442 FcPattern*match = set->fonts[t];
443 //FcPattern*match = FcFontMatch(0, pattern, &result);
444 if(fc_ismatch(match, family, style)) {
446 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
447 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
450 //FcPatternDestroy(match);
451 msg("<debug> fontconfig: returning filename %s", filename);
453 FcPatternDestroy(pattern);
454 FcFontSetDestroy(set);
455 return filename?strdup(filename):0;
460 FcPatternDestroy(pattern);
461 FcFontSetDestroy(set);
468 static DisplayFontParamKind detectFontType(const char*filename)
470 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
471 return displayFontTT;
472 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
473 return displayFontT1;
474 return displayFontTT;
477 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
479 msg("<verbose> looking for font %s in global params", fontName->getCString());
481 char*name = fontName->getCString();
483 /* see if it is a pdf standard font */
485 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
486 if(!strcmp(name, pdf2t1map[t].pdffont)) {
487 if(!pdf2t1map[t].fullfilename) {
488 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
489 if(!pdf2t1map[t].fullfilename) {
490 msg("<error> Couldn't save default font- is the Temp Directory writable?");
492 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
495 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
496 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
501 int bestlen = 0x7fffffff;
502 const char*bestfilename = 0;
504 fontfile_t*f = global_fonts;
506 if(strstr(f->filename, name)) {
507 if(f->len < bestlen) {
509 bestfilename = f->filename;
515 /* if we didn't find anything up to now, try looking for the
516 font via fontconfig */
519 filename = fontconfig_searchForFont(name);
521 filename = strdup(bestfilename);
525 DisplayFontParamKind kind = detectFontType(filename);
526 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
527 if(kind == displayFontTT) {
528 dfp->tt.fileName = new GString(filename);
530 dfp->t1.fileName = new GString(filename);
535 return GlobalParams::getDisplayFont(fontName);
538 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
542 this->xref = doc->getXRef();
545 this->textmodeinfo = 0;
548 this->type3active = 0;
551 this->substitutepos = 0;
552 this->type3Warning = 0;
553 this->user_movex = 0;
554 this->user_movey = 0;
557 this->user_clipx1 = 0;
558 this->user_clipy1 = 0;
559 this->user_clipx2 = 0;
560 this->user_clipy2 = 0;
561 this->current_text_stroke = 0;
562 this->current_text_clip = 0;
563 this->outer_clip_box = 0;
565 this->pagebuflen = 0;
567 this->config_convertgradients=1;
568 this->config_break_on_warning=0;
569 this->config_remapunicode=0;
570 this->config_transparent=0;
571 this->config_extrafontdata = 0;
572 this->config_fontquality = 10;
573 this->config_optimize_polygons = 0;
575 this->gfxfontlist = gfxfontlist_create();
577 memset(states, 0, sizeof(states));
578 this->featurewarnings = 0;
581 void GFXOutputDev::setParameter(const char*key, const char*value)
583 if(!strcmp(key,"breakonwarning")) {
584 this->config_break_on_warning = atoi(value);
585 } else if(!strcmp(key,"remapunicode")) {
586 this->config_remapunicode = atoi(value);
587 } else if(!strcmp(key,"transparent")) {
588 this->config_transparent = atoi(value);
589 } else if(!strcmp(key,"extrafontdata")) {
590 this->config_extrafontdata = atoi(value);
591 } else if(!strcmp(key,"convertgradients")) {
592 this->config_convertgradients = atoi(value);
593 } else if(!strcmp(key,"optimize_polygons")) {
594 this->config_optimize_polygons = atoi(value);
595 } else if(!strcmp(key,"fontquality")) {
596 this->config_fontquality = atof(value);
597 if(this->config_fontquality<=1)
598 this->config_fontquality=1;
599 } else if(!strcmp(key,"help")) {
600 printf("\nPDF layer options:\n");
601 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
602 printf(" are not 100%% supported\n");
603 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
604 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
605 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
610 void GFXOutputDev::setDevice(gfxdevice_t*dev)
615 void GFXOutputDev::setMove(int x,int y)
617 this->user_movex = x;
618 this->user_movey = y;
621 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
623 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
624 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
626 this->user_clipx1 = x1;
627 this->user_clipy1 = y1;
628 this->user_clipx2 = x2;
629 this->user_clipy2 = y2;
632 static char*getFontName(GfxFont*font)
635 GString*gstr = font->getName();
636 char* fname = gstr==0?0:gstr->getCString();
640 sprintf(buf, "UFONT%d", r->num);
641 fontid = strdup(buf);
643 fontid = strdup(fname);
647 char* plus = strchr(fontid, '+');
648 if(plus && plus < &fontid[strlen(fontid)-1]) {
649 fontname = strdup(plus+1);
651 fontname = strdup(fontid);
657 static void dumpFontInfo(const char*loglevel, GfxFont*font);
658 static int lastdumps[1024];
659 static int lastdumppos = 0;
664 static void showFontError(GfxFont*font, int nr)
668 for(t=0;t<lastdumppos;t++)
669 if(lastdumps[t] == r->num)
673 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
674 lastdumps[lastdumppos++] = r->num;
676 msg("<warning> The following font caused problems:");
678 msg("<warning> The following font caused problems (substituting):");
680 msg("<warning> The following Type 3 Font will be rendered as graphics:");
681 dumpFontInfo("<warning>", font);
684 static void dumpFontInfo(const char*loglevel, GfxFont*font)
686 char* id = getFontID(font);
687 char* name = getFontName(font);
688 Ref* r=font->getID();
689 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
691 GString*gstr = font->getTag();
693 msg("%s| Tag: %s", loglevel, id);
695 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
697 GfxFontType type=font->getType();
699 case fontUnknownType:
700 msg("%s| Type: unknown",loglevel);
703 msg("%s| Type: 1",loglevel);
706 msg("%s| Type: 1C",loglevel);
709 msg("%s| Type: 3",loglevel);
712 msg("%s| Type: TrueType",loglevel);
715 msg("%s| Type: CIDType0",loglevel);
718 msg("%s| Type: CIDType0C",loglevel);
721 msg("%s| Type: CIDType2",loglevel);
726 GBool embedded = font->getEmbeddedFontID(&embRef);
728 if(font->getEmbeddedFontName()) {
729 embeddedName = font->getEmbeddedFontName()->getCString();
732 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
734 gstr = font->getExtFontFile();
736 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
738 // Get font descriptor flags.
739 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
740 if(font->isSerif()) msg("%s| is serif", loglevel);
741 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
742 if(font->isItalic()) msg("%s| is italic", loglevel);
743 if(font->isBold()) msg("%s| is bold", loglevel);
749 //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");}
750 //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");}
752 void dump_outline(gfxline_t*line)
755 if(line->type == gfx_moveTo) {
756 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
757 } else if(line->type == gfx_lineTo) {
758 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
759 } else if(line->type == gfx_splineTo) {
760 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
766 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
768 int num = path->getNumSubpaths();
771 double lastx=0,lasty=0,posx=0,posy=0;
774 msg("<warning> empty path");
778 gfxdrawer_target_gfxline(&draw);
780 for(t = 0; t < num; t++) {
781 GfxSubpath *subpath = path->getSubpath(t);
782 int subnum = subpath->getNumPoints();
783 double bx=0,by=0,cx=0,cy=0;
785 for(s=0;s<subnum;s++) {
788 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
791 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
792 draw.lineTo(&draw, lastx, lasty);
794 draw.moveTo(&draw, x,y);
799 } else if(subpath->getCurve(s) && cpos==0) {
803 } else if(subpath->getCurve(s) && cpos==1) {
811 draw.lineTo(&draw, x,y);
813 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
820 /* fix non-closed lines */
821 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
822 draw.lineTo(&draw, lastx, lasty);
824 gfxline_t*result = (gfxline_t*)draw.result(&draw);
826 gfxline_optimize(result);
831 GBool GFXOutputDev::useTilingPatternFill()
833 infofeature("tiled patterns");
834 // if(config_convertgradients)
838 GBool GFXOutputDev::useShadedFills()
840 infofeature("shaded fills");
841 if(config_convertgradients)
846 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
848 state->transform(x,y,nx,ny);
849 *nx += user_movex + clipmovex;
850 *ny += user_movey + clipmovey;
854 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
855 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
856 int paintType, Dict *resDict,
857 double *mat, double *bbox,
858 int x0, int y0, int x1, int y1,
859 double xStep, double yStep)
861 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
862 int paintType, Dict *resDict,
863 double *mat, double *bbox,
864 int x0, int y0, int x1, int y1,
865 double xStep, double yStep)
868 msg("<debug> tilingPatternFill");
869 infofeature("tiling pattern fills");
872 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
874 msg("<error> functionShadedFill not supported yet");
875 infofeature("function shaded fills");
878 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
882 colspace->getRGB(col, &rgb);
883 c.r = colToByte(rgb.r);
884 c.g = colToByte(rgb.g);
885 c.b = colToByte(rgb.b);
890 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
892 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
893 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
896 this->transformXY(state, x0,y0, &x0,&y0);
897 this->transformXY(state, x1,y1, &x1,&y1);
898 this->transformXY(state, x2,y2, &x2,&y2);
903 shading->getColor(0.0, &color0);
904 shading->getColor(0.5, &color1);
905 shading->getColor(1.0, &color2);
907 GfxColorSpace* colspace = shading->getColorSpace();
909 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
910 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
911 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
912 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
913 infofeature("radial shaded fills");
915 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
919 g[0].color = col2col(colspace, &color0);
920 g[1].color = col2col(colspace, &color1);
921 g[2].color = col2col(colspace, &color2);
926 gfxbbox_t b = states[statepos].clipbbox;
927 gfxline_t p1,p2,p3,p4,p5;
928 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
929 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
930 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
931 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
932 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
935 //m.m00 = (x3-x0); m.m10 = (x1-x0);
936 //m.m01 = (y3-y0); m.m11 = (y1-y0);
937 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
938 m.m00 = (x1-x0); m.m10 = (x2-x0);
939 m.m01 = (y1-y0); m.m11 = (y2-y0);
943 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
947 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
950 shading->getCoords(&x0,&y0,&x1,&y1);
951 this->transformXY(state, x0,y0,&x0,&y0);
952 this->transformXY(state, x1,y1,&x1,&y1);
957 shading->getColor(0.0, &color0);
958 shading->getColor(0.5, &color1);
959 shading->getColor(1.0, &color2);
961 GfxColorSpace* colspace = shading->getColorSpace();
963 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
964 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
965 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
966 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
968 infofeature("axial shaded fills");
970 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
974 g[0].color = col2col(colspace, &color0);
975 g[1].color = col2col(colspace, &color1);
976 g[2].color = col2col(colspace, &color2);
981 double xMin,yMin,xMax,yMax;
982 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
983 this->transformXY(state, xMin, yMin, &xMin, &yMin);
984 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
987 xMin = 1024; yMin = 1024;
989 gfxbbox_t b = states[statepos].clipbbox;
990 gfxline_t p1,p2,p3,p4,p5;
991 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
992 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
993 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
994 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
995 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
997 /* the gradient starts at (-1.0,0.0), so move (0,0) to
998 the middle of the two control points */
1000 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1001 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1002 m.tx = (x0 + x1)/2 - 0.5;
1003 m.ty = (y0 + y1)/2 - 0.5;
1005 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1009 GBool GFXOutputDev::useDrawForm()
1011 infofeature("forms");
1014 void GFXOutputDev::drawForm(Ref id)
1016 msg("<error> drawForm not implemented");
1018 GBool GFXOutputDev::needNonText()
1022 void GFXOutputDev::endPage()
1024 msg("<verbose> endPage (GfxOutputDev)");
1025 if(outer_clip_box) {
1026 device->endclip(device);
1029 this->dashPattern = 0;
1030 /* notice: we're not fully done yet with this page- there might still be
1031 a few calls to drawLink() yet to come */
1034 static inline double sqr(double x) {return x*x;}
1036 #define STROKE_FILL 1
1037 #define STROKE_CLIP 2
1038 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1040 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1041 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1042 double miterLimit = state->getMiterLimit();
1043 double width = state->getTransformedLineWidth();
1046 double opaq = state->getStrokeOpacity();
1048 state->getFillRGB(&rgb);
1050 state->getStrokeRGB(&rgb);
1052 col.r = colToByte(rgb.r);
1053 col.g = colToByte(rgb.g);
1054 col.b = colToByte(rgb.b);
1055 col.a = (unsigned char)(opaq*255);
1057 gfx_capType capType = gfx_capRound;
1058 if(lineCap == 0) capType = gfx_capButt;
1059 else if(lineCap == 1) capType = gfx_capRound;
1060 else if(lineCap == 2) capType = gfx_capSquare;
1062 gfx_joinType joinType = gfx_joinRound;
1063 if(lineJoin == 0) joinType = gfx_joinMiter;
1064 else if(lineJoin == 1) joinType = gfx_joinRound;
1065 else if(lineJoin == 2) joinType = gfx_joinBevel;
1067 gfxline_t*line2 = 0;
1069 if(this->dashLength && this->dashPattern) {
1070 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1073 /* try to find out how much the transformation matrix would
1074 stretch the dashes, and factor that into the dash lengths.
1075 This is not the entirely correct approach- it would be
1076 better to first convert the path to an unscaled version,
1077 then apply dashing, and then transform the path using
1078 the current transformation matrix. However there are few
1079 PDFs which actually stretch a dashed path in a non-orthonormal
1081 double tx1, ty1, tx2, ty2;
1082 this->transformXY(state, 0, 0, &tx1, &ty1);
1083 this->transformXY(state, 1, 1, &tx2, &ty2);
1084 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
1086 f = 1.0; //disable dash length transform for now
1088 msg("<trace> %d dashes", this->dashLength);
1089 msg("<trace> | phase: %f", this->dashStart);
1090 for(t=0;t<this->dashLength;t++) {
1091 dash[t] = (float)this->dashPattern[t] * f;
1092 msg("<trace> | d%-3d: %f", t, this->dashPattern[t]);
1094 dash[this->dashLength] = -1;
1095 if(getLogLevel() >= LOGLEVEL_TRACE) {
1099 line2 = gfxtool_dash_line(line, dash, (float)(this->dashStart*f));
1102 msg("<trace> After dashing:");
1105 if(getLogLevel() >= LOGLEVEL_TRACE) {
1106 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1108 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1109 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1111 col.r,col.g,col.b,col.a
1116 if(flags&STROKE_FILL) {
1117 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1118 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1119 if(getLogLevel() >= LOGLEVEL_TRACE) {
1120 dump_outline(gfxline);
1123 msg("<warning> Empty polygon (resulting from stroked line)");
1125 if(flags&STROKE_CLIP) {
1126 device->startclip(device, gfxline);
1127 states[statepos].clipping++;
1129 device->fill(device, gfxline, &col);
1131 gfxline_free(gfxline);
1134 if(flags&STROKE_CLIP)
1135 msg("<error> Stroke&clip not supported at the same time");
1136 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1140 gfxline_free(line2);
1143 gfxcolor_t getFillColor(GfxState * state)
1146 double opaq = state->getFillOpacity();
1147 state->getFillRGB(&rgb);
1149 col.r = colToByte(rgb.r);
1150 col.g = colToByte(rgb.g);
1151 col.b = colToByte(rgb.b);
1152 col.a = (unsigned char)(opaq*255);
1156 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1158 gfxcolor_t col = getFillColor(state);
1160 if(getLogLevel() >= LOGLEVEL_TRACE) {
1161 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1164 device->fill(device, line, &col);
1167 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1169 if(getLogLevel() >= LOGLEVEL_TRACE) {
1172 gfxbbox_t bbox = gfxline_getbbox(line);
1173 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1175 device->startclip(device, line);
1176 states[statepos].clipping++;
1179 void GFXOutputDev::clip(GfxState *state)
1181 GfxPath * path = state->getPath();
1182 msg("<trace> clip");
1183 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1184 if(config_optimize_polygons) {
1185 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1189 clipToGfxLine(state, line);
1193 void GFXOutputDev::eoClip(GfxState *state)
1195 GfxPath * path = state->getPath();
1196 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1197 clipToGfxLine(state, line);
1200 void GFXOutputDev::clipToStrokePath(GfxState *state)
1202 GfxPath * path = state->getPath();
1203 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1205 if(getLogLevel() >= LOGLEVEL_TRACE) {
1206 double width = state->getTransformedLineWidth();
1207 msg("<trace> cliptostrokepath width=%f", width);
1211 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1215 void GFXOutputDev::finish()
1217 if(outer_clip_box) {
1219 device->endclip(device);
1225 GFXOutputDev::~GFXOutputDev()
1230 free(this->pages); this->pages = 0;
1233 feature_t*f = this->featurewarnings;
1235 feature_t*next = f->next;
1237 free(f->string);f->string =0;
1243 this->featurewarnings = 0;
1245 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1247 GBool GFXOutputDev::upsideDown()
1251 GBool GFXOutputDev::useDrawChar()
1256 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1257 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1259 static char tmp_printstr[4096];
1260 char* makeStringPrintable(char*str)
1262 int len = strlen(str);
1269 for(t=0;t<len;t++) {
1274 tmp_printstr[t] = c;
1277 tmp_printstr[len++] = '.';
1278 tmp_printstr[len++] = '.';
1279 tmp_printstr[len++] = '.';
1281 tmp_printstr[len] = 0;
1282 return tmp_printstr;
1284 #define INTERNAL_FONT_SIZE 1024.0
1285 void GFXOutputDev::updateFontMatrix(GfxState*state)
1287 double* ctm = state->getCTM();
1288 double fontSize = state->getFontSize();
1289 double*textMat = state->getTextMat();
1291 /* taking the absolute value of horizScaling seems to be required for
1292 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1293 double hscale = fabs(state->getHorizScaling());
1295 // from xpdf-3.02/SplashOutputDev:updateFont
1296 double mm11 = textMat[0] * fontSize * hscale;
1297 double mm12 = textMat[1] * fontSize * hscale;
1298 double mm21 = textMat[2] * fontSize;
1299 double mm22 = textMat[3] * fontSize;
1301 // multiply with ctm, like state->getFontTransMat() does
1302 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1303 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1304 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1305 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1306 this->current_font_matrix.tx = 0;
1307 this->current_font_matrix.ty = 0;
1310 void GFXOutputDev::beginString(GfxState *state, GString *s)
1312 int render = state->getRender();
1313 if(current_text_stroke) {
1314 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1317 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1320 static gfxline_t* mkEmptyGfxShape(double x, double y)
1322 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1323 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1327 static char isValidUnicode(int c)
1329 if(c>=32 && c<0x2fffe)
1334 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1335 double dx, double dy,
1336 double originX, double originY,
1337 CharCode charid, int nBytes, Unicode *_u, int uLen)
1339 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1340 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1344 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1346 int render = state->getRender();
1347 gfxcolor_t col = getFillColor(state);
1349 // check for invisible text -- this is used by Acrobat Capture
1350 if (render == RENDER_INVISIBLE) {
1352 if(!config_extrafontdata)
1356 GfxFont*font = state->getFont();
1358 if(font->getType() == fontType3) {
1359 /* type 3 chars are passed as graphics */
1360 msg("<debug> type3 char at %f/%f", x, y);
1364 Unicode u = uLen?(_u[0]):0;
1365 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);
1367 gfxmatrix_t m = this->current_font_matrix;
1368 this->transformXY(state, x, y, &m.tx, &m.ty);
1370 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1371 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1373 msg("<debug> Drawing glyph %d as shape", charid);
1375 msg("<notice> Some texts will be rendered as shape");
1378 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1379 gfxline_t*tglyph = gfxline_clone(glyph);
1380 gfxline_transform(tglyph, &m);
1381 if((render&3) != RENDER_INVISIBLE) {
1382 gfxline_t*add = gfxline_clone(tglyph);
1383 current_text_stroke = gfxline_append(current_text_stroke, add);
1385 if(render&RENDER_CLIP) {
1386 gfxline_t*add = gfxline_clone(tglyph);
1387 current_text_clip = gfxline_append(current_text_clip, add);
1388 if(!current_text_clip) {
1389 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1392 gfxline_free(tglyph);
1396 void GFXOutputDev::endString(GfxState *state)
1398 int render = state->getRender();
1399 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1401 if(current_text_stroke) {
1402 /* fillstroke and stroke text rendering objects we can process right
1403 now (as there may be texts of other rendering modes in this
1404 text object)- clipping objects have to wait until endTextObject,
1406 device->setparameter(device, "mark","TXT");
1407 if((render&3) == RENDER_FILL) {
1408 fillGfxLine(state, current_text_stroke);
1409 gfxline_free(current_text_stroke);
1410 current_text_stroke = 0;
1411 } else if((render&3) == RENDER_FILLSTROKE) {
1412 fillGfxLine(state, current_text_stroke);
1413 strokeGfxline(state, current_text_stroke,0);
1414 gfxline_free(current_text_stroke);
1415 current_text_stroke = 0;
1416 } else if((render&3) == RENDER_STROKE) {
1417 strokeGfxline(state, current_text_stroke,0);
1418 gfxline_free(current_text_stroke);
1419 current_text_stroke = 0;
1421 device->setparameter(device, "mark","");
1425 void GFXOutputDev::endTextObject(GfxState *state)
1427 int render = state->getRender();
1428 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1430 if(current_text_clip) {
1431 device->setparameter(device, "mark","TXT");
1432 clipToGfxLine(state, current_text_clip);
1433 device->setparameter(device, "mark","");
1434 gfxline_free(current_text_clip);
1435 current_text_clip = 0;
1439 /* the logic seems to be as following:
1440 first, beginType3Char is called, with the charcode and the coordinates.
1441 if this function returns true, it already knew about the char and has now drawn it.
1442 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1443 called with some parameters.
1444 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1445 at the position first passed to beginType3Char). the char ends with endType3Char.
1447 The drawing operations between beginType3Char and endType3Char are somewhat different to
1448 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1449 color determines the color of a font)
1452 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1454 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1457 if(config_extrafontdata && current_fontinfo) {
1459 gfxmatrix_t m = this->current_font_matrix;
1460 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1461 m.m00*=INTERNAL_FONT_SIZE;
1462 m.m01*=INTERNAL_FONT_SIZE;
1463 m.m10*=INTERNAL_FONT_SIZE;
1464 m.m11*=INTERNAL_FONT_SIZE;
1466 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1467 msg("<error> Invalid charid %d for font", charid);
1470 gfxcolor_t col={0,0,0,0};
1471 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1472 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1476 /* the character itself is going to be passed using the draw functions */
1477 return gFalse; /* gTrue= is_in_cache? */
1480 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1482 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1485 void GFXOutputDev::endType3Char(GfxState *state)
1488 msg("<debug> endType3Char");
1491 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1493 this->currentpage = pageNum;
1495 int rot = doc->getPageRotate(1);
1496 gfxcolor_t white = {255,255,255,255};
1497 gfxcolor_t black = {255,0,0,0};
1499 gfxline_t clippath[5];
1501 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1502 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1503 Use CropBox, not MediaBox, as page size
1510 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1511 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1513 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1514 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1516 this->clipmovex = -(int)x1;
1517 this->clipmovey = -(int)y1;
1519 /* apply user clip box */
1520 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1521 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1522 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1523 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1524 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1525 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1528 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1530 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);
1532 msg("<verbose> page is rotated %d degrees", rot);
1534 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1535 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1536 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1537 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1538 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1539 device->startclip(device, clippath); outer_clip_box = 1;
1540 if(!config_transparent) {
1541 device->fill(device, clippath, &white);
1543 states[statepos].clipbbox.xmin = x1;
1544 states[statepos].clipbbox.ymin = x1;
1545 states[statepos].clipbbox.xmax = x2;
1546 states[statepos].clipbbox.ymax = y2;
1548 this->dashPattern = 0;
1549 this->dashLength = 0;
1550 this->dashStart = 0;
1554 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1556 double x1, y1, x2, y2;
1557 gfxline_t points[5];
1560 msg("<debug> drawlink");
1562 link->getRect(&x1, &y1, &x2, &y2);
1563 cvtUserToDev(x1, y1, &x, &y);
1564 points[0].type = gfx_moveTo;
1565 points[0].x = points[4].x = x + user_movex + clipmovex;
1566 points[0].y = points[4].y = y + user_movey + clipmovey;
1567 points[0].next = &points[1];
1568 cvtUserToDev(x2, y1, &x, &y);
1569 points[1].type = gfx_lineTo;
1570 points[1].x = x + user_movex + clipmovex;
1571 points[1].y = y + user_movey + clipmovey;
1572 points[1].next = &points[2];
1573 cvtUserToDev(x2, y2, &x, &y);
1574 points[2].type = gfx_lineTo;
1575 points[2].x = x + user_movex + clipmovex;
1576 points[2].y = y + user_movey + clipmovey;
1577 points[2].next = &points[3];
1578 cvtUserToDev(x1, y2, &x, &y);
1579 points[3].type = gfx_lineTo;
1580 points[3].x = x + user_movex + clipmovex;
1581 points[3].y = y + user_movey + clipmovey;
1582 points[3].next = &points[4];
1583 cvtUserToDev(x1, y1, &x, &y);
1584 points[4].type = gfx_lineTo;
1585 points[4].x = x + user_movex + clipmovex;
1586 points[4].y = y + user_movey + clipmovey;
1589 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1590 points[0].x, points[0].y,
1591 points[1].x, points[1].y,
1592 points[2].x, points[2].y,
1593 points[3].x, points[3].y);
1595 if(getLogLevel() >= LOGLEVEL_TRACE) {
1596 dump_outline(points);
1599 LinkAction*action=link->getAction();
1602 const char*type = "-?-";
1605 msg("<trace> drawlink action=%d", action->getKind());
1606 switch(action->getKind())
1610 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1611 LinkDest *dest=NULL;
1612 if (ha->getDest()==NULL)
1613 dest=catalog->findDest(ha->getNamedDest());
1614 else dest=ha->getDest();
1616 if (dest->isPageRef()){
1617 Ref pageref=dest->getPageRef();
1618 page=catalog->findPage(pageref.num,pageref.gen);
1620 else page=dest->getPageNum();
1621 sprintf(buf, "%d", page);
1628 LinkGoToR*l = (LinkGoToR*)action;
1629 GString*g = l->getFileName();
1631 s = strdup(g->getCString());
1633 /* if the GoToR link has no filename, then
1634 try to find a refernce in the *local*
1636 GString*g = l->getNamedDest();
1638 s = strdup(g->getCString());
1644 LinkNamed*l = (LinkNamed*)action;
1645 GString*name = l->getName();
1647 s = strdup(name->lowerCase()->getCString());
1648 named = name->getCString();
1651 if(strstr(s, "next") || strstr(s, "forward"))
1653 page = currentpage + 1;
1655 else if(strstr(s, "prev") || strstr(s, "back"))
1657 page = currentpage - 1;
1659 else if(strstr(s, "last") || strstr(s, "end"))
1661 if(pages && pagepos>0)
1662 page = pages[pagepos-1];
1664 else if(strstr(s, "first") || strstr(s, "top"))
1672 case actionLaunch: {
1674 LinkLaunch*l = (LinkLaunch*)action;
1675 GString * str = new GString(l->getFileName());
1676 GString * params = l->getParams();
1678 str->append(params);
1679 s = strdup(str->getCString());
1686 LinkURI*l = (LinkURI*)action;
1687 GString*g = l->getURI();
1689 url = g->getCString();
1694 case actionUnknown: {
1696 LinkUnknown*l = (LinkUnknown*)action;
1701 msg("<error> Unknown link type!");
1706 if(!s) s = strdup("-?-");
1708 msg("<trace> drawlink s=%s", s);
1710 if(!linkinfo && (page || s))
1712 msg("<notice> File contains links");
1720 for(t=1;t<=pagepos;t++) {
1721 if(pages[t]==page) {
1730 sprintf(buf, "page%d", lpage);
1731 device->drawlink(device, points, buf);
1735 device->drawlink(device, points, s);
1738 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1742 void GFXOutputDev::saveState(GfxState *state) {
1743 dbg("saveState"); dbgindent+=2;
1745 msg("<trace> saveState");
1748 msg("<error> Too many nested states in pdf.");
1752 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1753 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1754 states[statepos].clipping = 0;
1755 states[statepos].clipbbox = states[statepos-1].clipbbox;
1758 void GFXOutputDev::restoreState(GfxState *state) {
1759 dbgindent-=2; dbg("restoreState");
1762 msg("<error> Invalid restoreState");
1765 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1766 states[statepos].clipping?" (end clipping)":"");
1767 if(states[statepos].softmask) {
1768 clearSoftMask(state);
1771 while(states[statepos].clipping) {
1772 device->endclip(device);
1773 states[statepos].clipping--;
1778 void GFXOutputDev::updateLineDash(GfxState *state)
1780 state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1781 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1782 if(!this->dashLength) {
1783 this->dashPattern = 0;
1787 void GFXOutputDev::updateLineWidth(GfxState *state)
1789 double width = state->getTransformedLineWidth();
1792 void GFXOutputDev::updateLineCap(GfxState *state)
1794 int c = state->getLineCap();
1797 void GFXOutputDev::updateLineJoin(GfxState *state)
1799 int j = state->getLineJoin();
1802 void GFXOutputDev::updateFillColor(GfxState *state)
1805 double opaq = state->getFillOpacity();
1806 state->getFillRGB(&rgb);
1808 void GFXOutputDev::updateFillOpacity(GfxState *state)
1811 double opaq = state->getFillOpacity();
1812 state->getFillRGB(&rgb);
1813 dbg("update fillopaq %f", opaq);
1815 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1817 double opaq = state->getFillOpacity();
1818 dbg("update strokeopaq %f", opaq);
1820 void GFXOutputDev::updateFillOverprint(GfxState *state)
1822 double opaq = state->getFillOverprint();
1823 dbg("update filloverprint %f", opaq);
1825 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1827 double opaq = state->getStrokeOverprint();
1828 dbg("update strokeoverprint %f", opaq);
1830 void GFXOutputDev::updateTransfer(GfxState *state)
1832 dbg("update transfer");
1836 void GFXOutputDev::updateStrokeColor(GfxState *state)
1839 double opaq = state->getStrokeOpacity();
1840 state->getStrokeRGB(&rgb);
1844 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1846 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1847 memset(font, 0, sizeof(gfxfont_t));
1849 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1850 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1854 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1856 //printf("%d glyphs\n", font->num_glyphs);
1857 font->num_glyphs = 0;
1858 for(t=0;t<src->num_glyphs;t++) {
1859 if(src->glyphs[t]) {
1860 SplashPath*path = src->glyphs[t]->path;
1861 int len = path?path->getLength():0;
1862 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1863 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1864 src->glyphs[t]->glyphid = font->num_glyphs;
1865 glyph->unicode = src->glyphs[t]->unicode;
1866 if(glyph->unicode >= font->max_unicode)
1867 font->max_unicode = glyph->unicode+1;
1869 gfxdrawer_target_gfxline(&drawer);
1873 for(s=0;s<len;s++) {
1876 path->getPoint(s, &x, &y, &f);
1879 if(f&splashPathFirst) {
1880 drawer.moveTo(&drawer, x*scale, y*scale);
1882 if(f&splashPathCurve) {
1884 path->getPoint(++s, &x2, &y2, &f);
1885 if(f&splashPathCurve) {
1887 path->getPoint(++s, &x3, &y3, &f);
1888 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1890 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1893 drawer.lineTo(&drawer, x*scale, y*scale);
1895 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1896 // (f&splashPathFirst)?"first":"",
1897 // (f&splashPathLast)?"last":"");
1899 glyph->line = (gfxline_t*)drawer.result(&drawer);
1900 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1904 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1905 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1906 for(t=0;t<font->num_glyphs;t++) {
1907 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1908 font->unicode2glyph[font->glyphs[t].unicode] = t;
1912 msg("<trace> %d glyphs.", t, font->num_glyphs);
1916 void GFXOutputDev::updateFont(GfxState *state)
1918 GfxFont* gfxFont = state->getFont();
1922 char*id = getFontID(gfxFont);
1923 msg("<verbose> Updating font to %s", id);
1924 if(gfxFont->getType() == fontType3) {
1925 infofeature("Type3 fonts");
1926 if(!config_extrafontdata) {
1931 msg("<error> Internal Error: FontID is null");
1935 this->current_fontinfo = this->info->getFont(id);
1936 if(!this->current_fontinfo) {
1937 msg("<error> Internal Error: no fontinfo for font %s", id);
1940 if(!this->current_fontinfo->seen) {
1941 dumpFontInfo("<verbose>", gfxFont);
1944 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1946 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1947 font->id = strdup(id);
1948 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1950 device->addfont(device, font);
1952 current_gfxfont = font;
1955 updateFontMatrix(state);
1958 #define SQR(x) ((x)*(x))
1960 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1962 if((newwidth<2 || newheight<2) ||
1963 (width<=newwidth || height<=newheight))
1965 unsigned char*newdata;
1967 newdata= (unsigned char*)malloc(newwidth*newheight);
1968 double fx = ((double)width)/newwidth;
1969 double fy = ((double)height)/newheight;
1971 int blocksize = (int)(8192/(fx*fy));
1972 int r = 8192*256/palettesize;
1973 for(x=0;x<newwidth;x++) {
1974 double ex = px + fx;
1975 int fromx = (int)px;
1977 int xweight1 = (int)(((fromx+1)-px)*256);
1978 int xweight2 = (int)((ex-tox)*256);
1980 for(y=0;y<newheight;y++) {
1981 double ey = py + fy;
1982 int fromy = (int)py;
1984 int yweight1 = (int)(((fromy+1)-py)*256);
1985 int yweight2 = (int)((ey-toy)*256);
1988 for(xx=fromx;xx<tox;xx++)
1989 for(yy=fromy;yy<toy;yy++) {
1990 int b = 1-data[width*yy+xx];
1992 if(xx==fromx) weight = (weight*xweight1)/256;
1993 if(xx==tox) weight = (weight*xweight2)/256;
1994 if(yy==fromy) weight = (weight*yweight1)/256;
1995 if(yy==toy) weight = (weight*yweight2)/256;
1998 //if(a) a=(palettesize-1)*r/blocksize;
1999 newdata[y*newwidth+x] = (a*blocksize)/r;
2007 #define IMAGE_TYPE_JPEG 0
2008 #define IMAGE_TYPE_LOSSLESS 1
2010 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2011 double x1,double y1,
2012 double x2,double y2,
2013 double x3,double y3,
2014 double x4,double y4, int type)
2016 gfxcolor_t*newpic=0;
2018 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2019 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2021 gfxline_t p1,p2,p3,p4,p5;
2022 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2023 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2024 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2025 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2026 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2028 {p1.x = (int)(p1.x*20)/20.0;
2029 p1.y = (int)(p1.y*20)/20.0;
2030 p2.x = (int)(p2.x*20)/20.0;
2031 p2.y = (int)(p2.y*20)/20.0;
2032 p3.x = (int)(p3.x*20)/20.0;
2033 p3.y = (int)(p3.y*20)/20.0;
2034 p4.x = (int)(p4.x*20)/20.0;
2035 p4.y = (int)(p4.y*20)/20.0;
2036 p5.x = (int)(p5.x*20)/20.0;
2037 p5.y = (int)(p5.y*20)/20.0;
2041 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2042 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2047 img.data = (gfxcolor_t*)data;
2051 if(type == IMAGE_TYPE_JPEG)
2052 /* TODO: pass image_dpi to device instead */
2053 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2055 dev->fillbitmap(dev, &p1, &img, &m, 0);
2058 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2059 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2061 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2064 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2065 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2067 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2071 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2072 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2073 GBool inlineImg, int mask, int*maskColors,
2074 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2076 double x1,y1,x2,y2,x3,y3,x4,y4;
2077 ImageStream *imgStr;
2082 unsigned char* maskbitmap = 0;
2085 ncomps = colorMap->getNumPixelComps();
2086 bits = colorMap->getBits();
2091 unsigned char buf[8];
2092 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2094 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2095 imgMaskStr->reset();
2096 unsigned char pal[256];
2097 int n = 1 << colorMap->getBits();
2102 maskColorMap->getGray(pixBuf, &gray);
2103 pal[t] = colToByte(gray);
2105 for (y = 0; y < maskHeight; y++) {
2106 for (x = 0; x < maskWidth; x++) {
2107 imgMaskStr->getPixel(buf);
2108 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2113 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2114 imgMaskStr->reset();
2115 for (y = 0; y < maskHeight; y++) {
2116 for (x = 0; x < maskWidth; x++) {
2117 imgMaskStr->getPixel(buf);
2119 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2127 imgStr = new ImageStream(str, width, ncomps,bits);
2130 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2132 msg("<verbose> Ignoring %d by %d image", width, height);
2133 unsigned char buf[8];
2135 for (y = 0; y < height; ++y)
2136 for (x = 0; x < width; ++x) {
2137 imgStr->getPixel(buf);
2145 this->transformXY(state, 0, 1, &x1, &y1); x1 = (int)(x1);y1 = (int)(y1);
2146 this->transformXY(state, 0, 0, &x2, &y2); x2 = (int)(x2);y2 = (int)(y2);
2147 this->transformXY(state, 1, 0, &x3, &y3); x3 = (int)(x3);y3 = (int)(y3);
2148 this->transformXY(state, 1, 1, &x4, &y4); x4 = (int)(x4);y4 = (int)(y4);
2150 if(!pbminfo && !(str->getKind()==strDCT)) {
2152 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2156 msg("<verbose> drawing %d by %d masked picture", width, height);
2158 if(!jpeginfo && (str->getKind()==strDCT)) {
2159 msg("<notice> File contains jpeg pictures");
2164 unsigned char buf[8];
2166 unsigned char*pic = new unsigned char[width*height];
2167 gfxcolor_t pal[256];
2169 state->getFillRGB(&rgb);
2171 memset(pal,255,sizeof(pal));
2172 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2173 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2174 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2175 pal[0].a = 255; pal[1].a = 0;
2178 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2179 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2180 for (y = 0; y < height; ++y)
2181 for (x = 0; x < width; ++x)
2183 imgStr->getPixel(buf);
2186 pic[width*y+x] = buf[0];
2189 /* the size of the drawn image is added to the identifier
2190 as the same image may require different bitmaps if displayed
2191 at different sizes (due to antialiasing): */
2194 unsigned char*pic2 = 0;
2197 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2206 height = realheight;
2210 /* make a black/white palette */
2212 float r = 255./(float)(numpalette-1);
2214 for(t=0;t<numpalette;t++) {
2215 pal[t].r = colToByte(rgb.r);
2216 pal[t].g = colToByte(rgb.g);
2217 pal[t].b = colToByte(rgb.b);
2218 pal[t].a = (unsigned char)(t*r);
2223 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2224 for (y = 0; y < height; ++y) {
2225 for (x = 0; x < width; ++x) {
2226 pic2[width*y+x] = pal[pic[y*width+x]];
2229 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2233 if(maskbitmap) free(maskbitmap);
2239 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2240 gfxcolor_t*pic=new gfxcolor_t[width*height];
2241 for (y = 0; y < height; ++y) {
2242 for (x = 0; x < width; ++x) {
2243 imgStr->getPixel(pixBuf);
2244 colorMap->getRGB(pixBuf, &rgb);
2245 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2246 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2247 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2248 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2250 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2254 if(str->getKind()==strDCT)
2255 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2257 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2260 if(maskbitmap) free(maskbitmap);
2263 gfxcolor_t*pic=new gfxcolor_t[width*height];
2264 gfxcolor_t pal[256];
2265 int n = 1 << colorMap->getBits();
2267 for(t=0;t<256;t++) {
2269 colorMap->getRGB(pixBuf, &rgb);
2271 {/*if(maskColors && *maskColors==t) {
2272 msg("<notice> Color %d is transparent", t);
2273 if (imgData->maskColors) {
2275 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2276 if (pix[i] < imgData->maskColors[2*i] ||
2277 pix[i] > imgData->maskColors[2*i+1]) {
2292 pal[t].r = (unsigned char)(colToByte(rgb.r));
2293 pal[t].g = (unsigned char)(colToByte(rgb.g));
2294 pal[t].b = (unsigned char)(colToByte(rgb.b));
2295 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2298 for (y = 0; y < height; ++y) {
2299 for (x = 0; x < width; ++x) {
2300 imgStr->getPixel(pixBuf);
2301 pic[width*y+x] = pal[pixBuf[0]];
2305 if(maskWidth < width && maskHeight < height) {
2306 for(y = 0; y < height; y++) {
2307 for (x = 0; x < width; x++) {
2308 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2312 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2313 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2314 double dx = width / maskWidth;
2315 double dy = height / maskHeight;
2317 for(y = 0; y < maskHeight; y++) {
2319 for (x = 0; x < maskWidth; x++) {
2320 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2321 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2329 height = maskHeight;
2332 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2336 if(maskbitmap) free(maskbitmap);
2341 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2342 int width, int height, GBool invert,
2345 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2346 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2347 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2350 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2351 int width, int height, GfxImageColorMap *colorMap,
2352 int *maskColors, GBool inlineImg)
2354 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2355 colorMap?"colorMap":"no colorMap",
2356 maskColors?"maskColors":"no maskColors",
2358 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2359 colorMap?"colorMap":"no colorMap",
2360 maskColors?"maskColors":"no maskColors",
2363 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2364 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2365 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2368 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2369 int width, int height,
2370 GfxImageColorMap *colorMap,
2371 Stream *maskStr, int maskWidth, int maskHeight,
2374 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2375 colorMap?"colorMap":"no colorMap",
2376 maskWidth, maskHeight);
2377 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2378 colorMap?"colorMap":"no colorMap",
2379 maskWidth, maskHeight);
2381 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2382 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2383 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2386 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2387 int width, int height,
2388 GfxImageColorMap *colorMap,
2390 int maskWidth, int maskHeight,
2391 GfxImageColorMap *maskColorMap)
2393 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2394 colorMap?"colorMap":"no colorMap",
2395 maskWidth, maskHeight);
2396 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2397 colorMap?"colorMap":"no colorMap",
2398 maskWidth, maskHeight);
2400 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2401 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2402 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2405 void GFXOutputDev::stroke(GfxState *state)
2409 GfxPath * path = state->getPath();
2410 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2411 strokeGfxline(state, line, 0);
2415 void GFXOutputDev::fill(GfxState *state)
2417 gfxcolor_t col = getFillColor(state);
2418 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2420 GfxPath * path = state->getPath();
2421 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2422 if(config_optimize_polygons) {
2423 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2427 fillGfxLine(state, line);
2431 void GFXOutputDev::eoFill(GfxState *state)
2433 gfxcolor_t col = getFillColor(state);
2434 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2436 GfxPath * path = state->getPath();
2437 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2438 fillGfxLine(state, line);
2443 static const char* dirseparator()
2452 void addGlobalFont(const char*filename)
2454 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2455 memset(f, 0, sizeof(fontfile_t));
2456 f->filename = filename;
2457 int len = strlen(filename);
2458 char*r1 = strrchr(filename, '/');
2459 char*r2 = strrchr(filename, '\\');
2467 msg("<notice> Adding font \"%s\".", filename);
2468 if(global_fonts_next) {
2469 global_fonts_next->next = f;
2470 global_fonts_next = global_fonts_next->next;
2472 global_fonts_next = global_fonts = f;
2476 void addGlobalLanguageDir(const char*dir)
2478 msg("<notice> Adding %s to language pack directories", dir);
2481 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2482 strcpy(config_file, dir);
2483 strcat(config_file, dirseparator());
2484 strcat(config_file, "add-to-xpdfrc");
2486 fi = fopen(config_file, "rb");
2488 msg("<error> Could not open %s", config_file);
2491 globalParams->parseFile(new GString(config_file), fi);
2495 void addGlobalFontDir(const char*dirname)
2497 #ifdef HAVE_DIRENT_H
2498 msg("<notice> Adding %s to font directories", dirname);
2499 lastfontdir = strdup(dirname);
2500 DIR*dir = opendir(dirname);
2502 msg("<warning> Couldn't open directory %s", dirname);
2507 ent = readdir (dir);
2511 char*name = ent->d_name;
2517 if(!strncasecmp(&name[l-4], ".pfa", 4))
2519 if(!strncasecmp(&name[l-4], ".pfb", 4))
2521 if(!strncasecmp(&name[l-4], ".ttf", 4))
2524 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2525 strcpy(fontname, dirname);
2526 strcat(fontname, dirseparator());
2527 strcat(fontname, name);
2528 addGlobalFont(fontname);
2533 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2537 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2543 this->pagebuflen = 1024;
2544 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2545 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2547 while(pdfpage >= this->pagebuflen)
2549 int oldlen = this->pagebuflen;
2550 this->pagebuflen+=1024;
2551 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2552 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2555 this->pages[pdfpage] = outputpage;
2556 if(pdfpage>this->pagepos)
2557 this->pagepos = pdfpage;
2560 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2561 GfxColorSpace *blendingColorSpace,
2562 GBool isolated, GBool knockout,
2565 const char*colormodename = "";
2567 if(blendingColorSpace) {
2568 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2570 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);
2571 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);
2573 //states[statepos].createsoftmask |= forSoftMask;
2574 states[statepos].createsoftmask = forSoftMask;
2575 states[statepos].transparencygroup = !forSoftMask;
2576 states[statepos].isolated = isolated;
2578 states[statepos].olddevice = this->device;
2579 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2581 gfxdevice_record_init(this->device);
2583 /*if(!forSoftMask) { ////???
2584 state->setFillOpacity(0.0);
2589 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2592 gfxdevice_t*r = this->device;
2594 this->device = states[statepos].olddevice;
2596 gfxresult_t*recording = r->finish(r);
2598 dbg("endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2599 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2601 if(states[statepos].createsoftmask) {
2602 states[statepos-1].softmaskrecording = recording;
2604 states[statepos-1].grouprecording = recording;
2607 states[statepos].createsoftmask = 0;
2608 states[statepos].transparencygroup = 0;
2612 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2614 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2615 "colordodge","colorburn","hardlight","softlight","difference",
2616 "exclusion","hue","saturation","color","luminosity"};
2618 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2619 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2621 if(state->getBlendMode() == gfxBlendNormal)
2622 infofeature("transparency groups");
2625 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2626 warnfeature(buffer, 0);
2629 gfxresult_t*grouprecording = states[statepos].grouprecording;
2631 int blendmode = state->getBlendMode();
2632 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2633 int alpha = (int)(state->getFillOpacity()*255);
2634 if(blendmode == gfxBlendMultiply && alpha>200)
2637 gfxdevice_ops_init(&ops, this->device, alpha);
2638 gfxresult_record_replay(grouprecording, &ops);
2641 grouprecording->destroy(grouprecording);
2643 states[statepos].grouprecording = 0;
2646 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2648 if(states[statepos].softmask) {
2649 /* shouldn't happen, but *does* happen */
2650 clearSoftMask(state);
2653 /* alpha = 1: retrieve mask values from alpha layer
2654 alpha = 0: retrieve mask values from luminance */
2656 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2657 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2658 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2659 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2661 infofeature("soft masks");
2663 warnfeature("soft masks from alpha channel",0);
2665 states[statepos].olddevice = this->device;
2666 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2667 gfxdevice_record_init(this->device);
2669 dbg("softmaskrecording is %08x (dev=%08x) at statepos %d\n", states[statepos].softmaskrecording, this->device, statepos);
2671 states[statepos].softmask = 1;
2672 states[statepos].softmask_alpha = alpha;
2675 static inline Guchar div255(int x) {
2676 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2679 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2681 if(c < min) c = min;
2682 if(c > max) c = max;
2686 void GFXOutputDev::clearSoftMask(GfxState *state)
2688 if(!states[statepos].softmask)
2690 states[statepos].softmask = 0;
2691 dbg("clearSoftMask statepos=%d", statepos);
2692 msg("<verbose> clearSoftMask statepos=%d", statepos);
2694 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2695 msg("<error> Error in softmask/tgroup ordering");
2699 gfxresult_t*mask = states[statepos].softmaskrecording;
2700 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2701 this->device = states[statepos].olddevice;
2703 /* get outline of all objects below the soft mask */
2704 gfxdevice_t uniondev;
2705 gfxdevice_union_init(&uniondev, 0);
2706 gfxresult_record_replay(below, &uniondev);
2707 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2708 uniondev.finish(&uniondev);
2709 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2710 gfxline_free(belowoutline);belowoutline=0;
2712 this->device->startclip(this->device, belowoutline);
2713 gfxresult_record_replay(below, this->device);
2714 gfxresult_record_replay(mask, this->device);
2715 this->device->endclip(this->device);
2718 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2719 if(width<=0 || height<=0)
2722 gfxdevice_t belowrender;
2723 gfxdevice_render_init(&belowrender);
2724 if(states[statepos+1].isolated) {
2725 belowrender.setparameter(&belowrender, "fillwhite", "1");
2727 belowrender.setparameter(&belowrender, "antialize", "2");
2728 belowrender.startpage(&belowrender, width, height);
2729 gfxresult_record_replay(below, &belowrender);
2730 belowrender.endpage(&belowrender);
2731 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2732 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2733 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2735 gfxdevice_t maskrender;
2736 gfxdevice_render_init(&maskrender);
2737 maskrender.startpage(&maskrender, width, height);
2738 gfxresult_record_replay(mask, &maskrender);
2739 maskrender.endpage(&maskrender);
2740 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2741 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2743 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2744 msg("<fatal> Internal error in mask drawing");
2749 for(y=0;y<height;y++) {
2750 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2751 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2752 for(x=0;x<width;x++) {
2754 if(states[statepos].softmask_alpha) {
2757 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2760 l2->a = div255(alpha*l2->a);
2762 /* DON'T premultiply alpha- this is done by fillbitmap,
2763 depending on the output device */
2764 //l2->r = div255(alpha*l2->r);
2765 //l2->g = div255(alpha*l2->g);
2766 //l2->b = div255(alpha*l2->b);
2772 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2775 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2776 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2778 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2780 mask->destroy(mask);
2781 below->destroy(below);
2782 maskresult->destroy(maskresult);
2783 belowresult->destroy(belowresult);
2784 states[statepos].softmaskrecording = 0;
2789 // public: ~MemCheck()
2791 // delete globalParams;globalParams=0;
2792 // Object::memCheck(stderr);
2793 // gMemReport(stderr);