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"
76 #include "../art/libart.h"
83 #define SQRT2 1.41421356237309504880
85 typedef struct _fontfile
88 int len; // basename length
90 struct _fontfile*next;
95 static fontfile_t* global_fonts = 0;
96 static fontfile_t* global_fonts_next = 0;
98 static int fontnum = 0;
102 static char* lastfontdir = 0;
113 {"Times-Roman", "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
114 {"Times-Italic", "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
115 {"Times-Bold", "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
116 {"Times-BoldItalic", "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
117 {"Helvetica", "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
118 {"Helvetica-Oblique", "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
119 {"Helvetica-Bold", "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
120 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
121 {"Courier", "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
122 {"Courier-Oblique", "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
123 {"Courier-Bold", "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
124 {"Courier-BoldOblique", "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
125 {"Symbol", "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
126 {"ZapfDingbats", "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
129 static int verbose = 0;
130 static int dbgindent = 0;
131 static void dbg(const char*format, ...)
138 va_start(arglist, format);
139 vsprintf(buf, format, arglist);
142 while(l && buf[l-1]=='\n') {
147 int indent = dbgindent;
157 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
159 feature_t*f = this->featurewarnings;
161 if(!strcmp(feature, f->string))
165 f = (feature_t*)malloc(sizeof(feature_t));
166 f->string = strdup(feature);
167 f->next = this->featurewarnings;
168 this->featurewarnings = f;
170 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
171 if(this->config_break_on_warning) {
172 msg("<fatal> Aborting conversion due to unsupported feature");
176 msg("<notice> File contains %s",feature);
179 void GFXOutputDev::warnfeature(const char*feature,char fully)
181 showfeature(feature,fully,1);
183 void GFXOutputDev::infofeature(const char*feature)
185 showfeature(feature,0,0);
188 GFXOutputState::GFXOutputState() {
190 this->createsoftmask = 0;
191 this->transparencygroup = 0;
193 this->grouprecording = 0;
197 GBool GFXOutputDev::interpretType3Chars()
202 typedef struct _drawnchar
220 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
221 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
226 free(chars);chars = 0;
233 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
237 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
240 chars[num_chars].x = x;
241 chars[num_chars].y = y;
242 chars[num_chars].color = color;
243 chars[num_chars].charid = charid;
247 char* writeOutStdFont(fontentry* f)
252 char* tmpFileName = mktmpname(namebuf1);
254 sprintf(namebuf2, "%s.afm", tmpFileName);
255 fi = fopen(namebuf2, "wb");
258 fwrite(f->afm, 1, f->afmlen, fi);
261 sprintf(namebuf2, "%s.pfb", tmpFileName);
262 fi = fopen(namebuf2, "wb");
265 fwrite(f->pfb, 1, f->pfblen, fi);
267 return strdup(namebuf2);
269 void unlinkfont(char* filename)
274 msg("<verbose> Removing temporary font file %s", filename);
277 if(!strncmp(&filename[l-4],".afm",4)) {
278 memcpy(&filename[l-4],".pfb",4); unlink(filename);
279 memcpy(&filename[l-4],".pfa",4); unlink(filename);
280 memcpy(&filename[l-4],".afm",4);
283 if(!strncmp(&filename[l-4],".pfa",4)) {
284 memcpy(&filename[l-4],".afm",4); unlink(filename);
285 memcpy(&filename[l-4],".pfa",4);
288 if(!strncmp(&filename[l-4],".pfb",4)) {
289 memcpy(&filename[l-4],".afm",4); unlink(filename);
290 memcpy(&filename[l-4],".pfb",4);
295 static int config_use_fontconfig = 1;
296 static int fcinitcalled = 0;
298 GFXGlobalParams::GFXGlobalParams()
301 //setupBaseFonts(char *dir); //not tested yet
303 GFXGlobalParams::~GFXGlobalParams()
305 msg("<verbose> Performing cleanups");
307 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
308 if(pdf2t1map[t].fullfilename) {
309 unlinkfont(pdf2t1map[t].fullfilename);
312 #ifdef HAVE_FONTCONFIG
313 if(config_use_fontconfig && fcinitcalled)
317 #ifdef HAVE_FONTCONFIG
318 static char fc_ismatch(FcPattern*match, char*family, char*style)
320 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
321 FcBool scalable=FcFalse, outline=FcFalse;
322 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
323 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
324 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
325 FcPatternGetBool(match, "outline", 0, &outline);
326 FcPatternGetBool(match, "scalable", 0, &scalable);
328 if(scalable!=FcTrue || outline!=FcTrue)
331 if (!strcasecmp(fcfamily, family)) {
332 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
335 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
341 char* fontconfig_searchForFont(char*name)
343 #ifdef HAVE_FONTCONFIG
344 if(!config_use_fontconfig)
347 // call init ony once
351 // check whether we have a config file
352 char* configfile = (char*)FcConfigFilename(0);
353 int configexists = 0;
354 FILE*fi = fopen(configfile, "rb");
356 configexists = 1;fclose(fi);
357 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
359 msg("<debug> Initializing FontConfig (no configfile)");
363 /* A fontconfig instance which didn't find a configfile is unbelievably
364 cranky, so let's just write out a small xml file and make fontconfig
366 FcConfig*c = FcConfigCreate();
368 char* tmpFileName = mktmpname(namebuf);
369 FILE*fi = fopen(tmpFileName, "wb");
370 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
372 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
374 fprintf(fi, "<dir>~/.fonts</dir>\n");
376 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
378 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
379 fprintf(fi, "</fontconfig>\n");
381 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
382 FcConfigBuildFonts(c);
383 FcConfigSetCurrent(c);
387 msg("<debug> FontConfig Initialization failed. Disabling.");
388 config_use_fontconfig = 0;
391 FcConfig * config = FcConfigGetCurrent();
393 msg("<debug> FontConfig Config Initialization failed. Disabling.");
394 config_use_fontconfig = 0;
397 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
398 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
399 if(!set || !set->nfont) {
400 msg("<debug> FontConfig has zero fonts. Disabling.");
401 config_use_fontconfig = 0;
405 if(getLogLevel() >= LOGLEVEL_TRACE) {
407 for(t=0;t<set->nfont;t++) {
408 char*fcfamily=0,*fcstyle=0,*filename=0;
409 FcBool scalable=FcFalse, outline=FcFalse;
410 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
411 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
412 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
413 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
414 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
415 if(scalable && outline) {
416 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
422 char*family = strdup(name);
424 char*dash = strchr(family, '-');
425 FcPattern*pattern = 0;
429 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
430 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
432 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
433 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
437 FcConfigSubstitute(0, pattern, FcMatchPattern);
438 FcDefaultSubstitute(pattern);
440 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
443 for(t=0;t<set->nfont;t++) {
444 FcPattern*match = set->fonts[t];
445 //FcPattern*match = FcFontMatch(0, pattern, &result);
446 if(fc_ismatch(match, family, style)) {
448 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
449 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
452 //FcPatternDestroy(match);
453 msg("<debug> fontconfig: returning filename %s", filename);
455 FcPatternDestroy(pattern);
456 FcFontSetDestroy(set);
457 return filename?strdup(filename):0;
462 FcPatternDestroy(pattern);
463 FcFontSetDestroy(set);
470 static DisplayFontParamKind detectFontType(const char*filename)
472 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
473 return displayFontTT;
474 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
475 return displayFontT1;
476 return displayFontTT;
479 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
481 msg("<verbose> looking for font %s in global params", fontName->getCString());
483 char*name = fontName->getCString();
485 /* see if it is a pdf standard font */
487 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
488 if(!strcmp(name, pdf2t1map[t].pdffont)) {
489 if(!pdf2t1map[t].fullfilename) {
490 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
491 if(!pdf2t1map[t].fullfilename) {
492 msg("<error> Couldn't save default font- is the Temp Directory writable?");
494 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
497 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
498 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
503 int bestlen = 0x7fffffff;
504 const char*bestfilename = 0;
506 fontfile_t*f = global_fonts;
508 if(strstr(f->filename, name)) {
509 if(f->len < bestlen) {
511 bestfilename = f->filename;
517 /* if we didn't find anything up to now, try looking for the
518 font via fontconfig */
521 filename = fontconfig_searchForFont(name);
523 filename = strdup(bestfilename);
527 DisplayFontParamKind kind = detectFontType(filename);
528 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
529 if(kind == displayFontTT) {
530 dfp->tt.fileName = new GString(filename);
532 dfp->t1.fileName = new GString(filename);
537 return GlobalParams::getDisplayFont(fontName);
540 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
544 this->xref = doc->getXRef();
547 this->textmodeinfo = 0;
550 this->type3active = 0;
553 this->substitutepos = 0;
554 this->type3Warning = 0;
555 this->user_movex = 0;
556 this->user_movey = 0;
559 this->user_clipx1 = 0;
560 this->user_clipy1 = 0;
561 this->user_clipx2 = 0;
562 this->user_clipy2 = 0;
563 this->current_text_stroke = 0;
564 this->current_text_clip = 0;
565 this->outer_clip_box = 0;
567 this->pagebuflen = 0;
569 this->config_convertgradients=0;
570 this->config_break_on_warning=0;
571 this->config_remapunicode=0;
572 this->config_transparent=0;
573 this->config_extrafontdata = 0;
574 this->config_fontquality = 10;
575 this->config_optimize_polygons = 0;
577 this->gfxfontlist = gfxfontlist_create();
579 memset(states, 0, sizeof(states));
580 this->featurewarnings = 0;
583 void GFXOutputDev::setParameter(const char*key, const char*value)
585 if(!strcmp(key,"breakonwarning")) {
586 this->config_break_on_warning = atoi(value);
587 } else if(!strcmp(key,"remapunicode")) {
588 this->config_remapunicode = atoi(value);
589 } else if(!strcmp(key,"transparent")) {
590 this->config_transparent = atoi(value);
591 } else if(!strcmp(key,"extrafontdata")) {
592 this->config_extrafontdata = atoi(value);
593 } else if(!strcmp(key,"convertgradients")) {
594 this->config_convertgradients = atoi(value);
595 } else if(!strcmp(key,"optimize_polygons")) {
596 this->config_optimize_polygons = atoi(value);
597 } else if(!strcmp(key,"fontquality")) {
598 this->config_fontquality = atof(value);
599 if(this->config_fontquality<=1)
600 this->config_fontquality=1;
601 } else if(!strcmp(key,"help")) {
602 printf("\nPDF layer options:\n");
603 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
604 printf(" are not 100%% supported\n");
605 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
606 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
607 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
612 void GFXOutputDev::setDevice(gfxdevice_t*dev)
617 void GFXOutputDev::setMove(int x,int y)
619 this->user_movex = x;
620 this->user_movey = y;
623 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
625 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
626 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
628 this->user_clipx1 = x1;
629 this->user_clipy1 = y1;
630 this->user_clipx2 = x2;
631 this->user_clipy2 = y2;
634 static char*getFontName(GfxFont*font)
637 GString*gstr = font->getName();
638 char* fname = gstr==0?0:gstr->getCString();
642 sprintf(buf, "UFONT%d", r->num);
643 fontid = strdup(buf);
645 fontid = strdup(fname);
649 char* plus = strchr(fontid, '+');
650 if(plus && plus < &fontid[strlen(fontid)-1]) {
651 fontname = strdup(plus+1);
653 fontname = strdup(fontid);
659 static void dumpFontInfo(const char*loglevel, GfxFont*font);
660 static int lastdumps[1024];
661 static int lastdumppos = 0;
666 static void showFontError(GfxFont*font, int nr)
670 for(t=0;t<lastdumppos;t++)
671 if(lastdumps[t] == r->num)
675 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
676 lastdumps[lastdumppos++] = r->num;
678 msg("<warning> The following font caused problems:");
680 msg("<warning> The following font caused problems (substituting):");
682 msg("<warning> The following Type 3 Font will be rendered as graphics:");
683 dumpFontInfo("<warning>", font);
686 static void dumpFontInfo(const char*loglevel, GfxFont*font)
688 char* id = getFontID(font);
689 char* name = getFontName(font);
690 Ref* r=font->getID();
691 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
693 GString*gstr = font->getTag();
695 msg("%s| Tag: %s", loglevel, id);
697 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
699 GfxFontType type=font->getType();
701 case fontUnknownType:
702 msg("%s| Type: unknown",loglevel);
705 msg("%s| Type: 1",loglevel);
708 msg("%s| Type: 1C",loglevel);
711 msg("%s| Type: 3",loglevel);
714 msg("%s| Type: TrueType",loglevel);
717 msg("%s| Type: CIDType0",loglevel);
720 msg("%s| Type: CIDType0C",loglevel);
723 msg("%s| Type: CIDType2",loglevel);
728 GBool embedded = font->getEmbeddedFontID(&embRef);
730 if(font->getEmbeddedFontName()) {
731 embeddedName = font->getEmbeddedFontName()->getCString();
734 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
736 gstr = font->getExtFontFile();
738 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
740 // Get font descriptor flags.
741 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
742 if(font->isSerif()) msg("%s| is serif", loglevel);
743 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
744 if(font->isItalic()) msg("%s| is italic", loglevel);
745 if(font->isBold()) msg("%s| is bold", loglevel);
751 //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");}
752 //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");}
754 void dump_outline(gfxline_t*line)
757 if(line->type == gfx_moveTo) {
758 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
759 } else if(line->type == gfx_lineTo) {
760 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
761 } else if(line->type == gfx_splineTo) {
762 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
768 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
770 int num = path->getNumSubpaths();
773 double lastx=0,lasty=0,posx=0,posy=0;
776 msg("<warning> empty path");
780 gfxdrawer_target_gfxline(&draw);
782 for(t = 0; t < num; t++) {
783 GfxSubpath *subpath = path->getSubpath(t);
784 int subnum = subpath->getNumPoints();
785 double bx=0,by=0,cx=0,cy=0;
787 for(s=0;s<subnum;s++) {
790 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
793 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
794 draw.lineTo(&draw, lastx, lasty);
796 draw.moveTo(&draw, x,y);
801 } else if(subpath->getCurve(s) && cpos==0) {
805 } else if(subpath->getCurve(s) && cpos==1) {
813 draw.lineTo(&draw, x,y);
815 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
822 /* fix non-closed lines */
823 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
824 draw.lineTo(&draw, lastx, lasty);
826 gfxline_t*result = (gfxline_t*)draw.result(&draw);
828 gfxline_optimize(result);
833 GBool GFXOutputDev::useTilingPatternFill()
835 infofeature("tiled patterns");
836 // if(config_convertgradients)
840 GBool GFXOutputDev::useShadedFills()
842 infofeature("shaded fills");
843 if(config_convertgradients)
848 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
850 state->transform(x,y,nx,ny);
851 *nx += user_movex + clipmovex;
852 *ny += user_movey + clipmovey;
856 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
857 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
858 int paintType, Dict *resDict,
859 double *mat, double *bbox,
860 int x0, int y0, int x1, int y1,
861 double xStep, double yStep)
863 void GFXOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
864 int paintType, Dict *resDict,
865 double *mat, double *bbox,
866 int x0, int y0, int x1, int y1,
867 double xStep, double yStep)
870 msg("<debug> tilingPatternFill");
871 infofeature("tiling pattern fills");
874 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
876 msg("<error> functionShadedFill not supported yet");
877 infofeature("function shaded fills");
880 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
884 colspace->getRGB(col, &rgb);
885 c.r = colToByte(rgb.r);
886 c.g = colToByte(rgb.g);
887 c.b = colToByte(rgb.b);
892 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
894 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
895 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
898 this->transformXY(state, x0,y0, &x0,&y0);
899 this->transformXY(state, x1,y1, &x1,&y1);
900 this->transformXY(state, x2,y2, &x2,&y2);
905 shading->getColor(0.0, &color0);
906 shading->getColor(0.5, &color1);
907 shading->getColor(1.0, &color2);
909 GfxColorSpace* colspace = shading->getColorSpace();
911 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
912 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
913 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
914 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
915 infofeature("radial shaded fills");
917 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
921 g[0].color = col2col(colspace, &color0);
922 g[1].color = col2col(colspace, &color1);
923 g[2].color = col2col(colspace, &color2);
928 gfxbbox_t b = states[statepos].clipbbox;
929 gfxline_t p1,p2,p3,p4,p5;
930 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
931 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
932 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
933 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
934 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
937 //m.m00 = (x3-x0); m.m10 = (x1-x0);
938 //m.m01 = (y3-y0); m.m11 = (y1-y0);
939 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
940 m.m00 = (x1-x0); m.m10 = (x2-x0);
941 m.m01 = (y1-y0); m.m11 = (y2-y0);
945 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
949 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
952 shading->getCoords(&x0,&y0,&x1,&y1);
953 this->transformXY(state, x0,y0,&x0,&y0);
954 this->transformXY(state, x1,y1,&x1,&y1);
959 shading->getColor(0.0, &color0);
960 shading->getColor(0.5, &color1);
961 shading->getColor(1.0, &color2);
963 GfxColorSpace* colspace = shading->getColorSpace();
965 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
966 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
967 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
968 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
970 infofeature("axial shaded fills");
972 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
976 g[0].color = col2col(colspace, &color0);
977 g[1].color = col2col(colspace, &color1);
978 g[2].color = col2col(colspace, &color2);
983 double xMin,yMin,xMax,yMax;
984 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
985 this->transformXY(state, xMin, yMin, &xMin, &yMin);
986 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
989 xMin = 1024; yMin = 1024;
991 gfxbbox_t b = states[statepos].clipbbox;
992 gfxline_t p1,p2,p3,p4,p5;
993 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
994 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
995 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
996 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
997 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
999 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1000 the middle of the two control points */
1002 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1003 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1004 m.tx = (x0 + x1)/2 - 0.5;
1005 m.ty = (y0 + y1)/2 - 0.5;
1007 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1011 GBool GFXOutputDev::useDrawForm()
1013 infofeature("forms");
1016 void GFXOutputDev::drawForm(Ref id)
1018 msg("<error> drawForm not implemented");
1020 GBool GFXOutputDev::needNonText()
1024 void GFXOutputDev::endPage()
1026 msg("<verbose> endPage (GfxOutputDev)");
1027 if(outer_clip_box) {
1028 device->endclip(device);
1031 this->dashPattern = 0;
1032 /* notice: we're not fully done yet with this page- there might still be
1033 a few calls to drawLink() yet to come */
1036 static inline double sqr(double x) {return x*x;}
1038 #define STROKE_FILL 1
1039 #define STROKE_CLIP 2
1040 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1042 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1043 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1044 double miterLimit = state->getMiterLimit();
1045 double width = state->getTransformedLineWidth();
1048 double opaq = state->getStrokeOpacity();
1050 state->getFillRGB(&rgb);
1052 state->getStrokeRGB(&rgb);
1054 col.r = colToByte(rgb.r);
1055 col.g = colToByte(rgb.g);
1056 col.b = colToByte(rgb.b);
1057 col.a = (unsigned char)(opaq*255);
1059 gfx_capType capType = gfx_capRound;
1060 if(lineCap == 0) capType = gfx_capButt;
1061 else if(lineCap == 1) capType = gfx_capRound;
1062 else if(lineCap == 2) capType = gfx_capSquare;
1064 gfx_joinType joinType = gfx_joinRound;
1065 if(lineJoin == 0) joinType = gfx_joinMiter;
1066 else if(lineJoin == 1) joinType = gfx_joinRound;
1067 else if(lineJoin == 2) joinType = gfx_joinBevel;
1069 gfxline_t*line2 = 0;
1071 if(this->dashLength && this->dashPattern) {
1072 float * dash = (float*)malloc(sizeof(float)*(this->dashLength+1));
1075 /* try to find out how much the transformation matrix would
1076 stretch the dashes, and factor that into the dash lengths.
1077 This is not the entirely correct approach- it would be
1078 better to first convert the path to an unscaled version,
1079 then apply dashing, and then transform the path using
1080 the current transformation matrix. However there are few
1081 PDFs which actually stretch a dashed path in a non-orthonormal
1083 double tx1, ty1, tx2, ty2;
1084 this->transformXY(state, 0, 0, &tx1, &ty1);
1085 this->transformXY(state, 1, 1, &tx2, &ty2);
1086 double f = sqrt(sqr(tx2-tx1)+sqr(ty2-ty1)) / SQRT2;
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);
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 msg("<trace> cliptostrokepath");
1210 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1214 void GFXOutputDev::finish()
1216 if(outer_clip_box) {
1218 device->endclip(device);
1224 GFXOutputDev::~GFXOutputDev()
1229 free(this->pages); this->pages = 0;
1232 feature_t*f = this->featurewarnings;
1234 feature_t*next = f->next;
1236 free(f->string);f->string =0;
1242 this->featurewarnings = 0;
1244 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1246 GBool GFXOutputDev::upsideDown()
1250 GBool GFXOutputDev::useDrawChar()
1255 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1256 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1258 static char tmp_printstr[4096];
1259 char* makeStringPrintable(char*str)
1261 int len = strlen(str);
1268 for(t=0;t<len;t++) {
1273 tmp_printstr[t] = c;
1276 tmp_printstr[len++] = '.';
1277 tmp_printstr[len++] = '.';
1278 tmp_printstr[len++] = '.';
1280 tmp_printstr[len] = 0;
1281 return tmp_printstr;
1283 #define INTERNAL_FONT_SIZE 1024.0
1284 void GFXOutputDev::updateFontMatrix(GfxState*state)
1286 double* ctm = state->getCTM();
1287 double fontSize = state->getFontSize();
1288 double*textMat = state->getTextMat();
1290 /* taking the absolute value of horizScaling seems to be required for
1291 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1292 double hscale = fabs(state->getHorizScaling());
1294 // from xpdf-3.02/SplashOutputDev:updateFont
1295 double mm11 = textMat[0] * fontSize * hscale;
1296 double mm12 = textMat[1] * fontSize * hscale;
1297 double mm21 = textMat[2] * fontSize;
1298 double mm22 = textMat[3] * fontSize;
1300 // multiply with ctm, like state->getFontTransMat() does
1301 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1302 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1303 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1304 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1305 this->current_font_matrix.tx = 0;
1306 this->current_font_matrix.ty = 0;
1309 void GFXOutputDev::beginString(GfxState *state, GString *s)
1311 int render = state->getRender();
1312 if(current_text_stroke) {
1313 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1316 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1319 static gfxline_t* mkEmptyGfxShape(double x, double y)
1321 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1322 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1326 static char isValidUnicode(int c)
1328 if(c>=32 && c<0x2fffe)
1333 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1334 double dx, double dy,
1335 double originX, double originY,
1336 CharCode charid, int nBytes, Unicode *_u, int uLen)
1338 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1339 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1343 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1345 int render = state->getRender();
1346 gfxcolor_t col = getFillColor(state);
1348 // check for invisible text -- this is used by Acrobat Capture
1349 if (render == RENDER_INVISIBLE) {
1351 if(!config_extrafontdata)
1355 GfxFont*font = state->getFont();
1357 if(font->getType() == fontType3) {
1358 /* type 3 chars are passed as graphics */
1359 msg("<debug> type3 char at %f/%f", x, y);
1363 Unicode u = uLen?(_u[0]):0;
1364 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);
1366 gfxmatrix_t m = this->current_font_matrix;
1367 this->transformXY(state, x, y, &m.tx, &m.ty);
1369 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1370 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1372 msg("<debug> Drawing glyph %d as shape", charid);
1374 msg("<notice> Some texts will be rendered as shape");
1377 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1378 gfxline_t*tglyph = gfxline_clone(glyph);
1379 gfxline_transform(tglyph, &m);
1380 if((render&3) != RENDER_INVISIBLE) {
1381 gfxline_t*add = gfxline_clone(tglyph);
1382 current_text_stroke = gfxline_append(current_text_stroke, add);
1384 if(render&RENDER_CLIP) {
1385 gfxline_t*add = gfxline_clone(tglyph);
1386 current_text_clip = gfxline_append(current_text_clip, add);
1387 if(!current_text_clip) {
1388 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1391 gfxline_free(tglyph);
1395 void GFXOutputDev::endString(GfxState *state)
1397 int render = state->getRender();
1398 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1400 if(current_text_stroke) {
1401 /* fillstroke and stroke text rendering objects we can process right
1402 now (as there may be texts of other rendering modes in this
1403 text object)- clipping objects have to wait until endTextObject,
1405 device->setparameter(device, "mark","TXT");
1406 if((render&3) == RENDER_FILL) {
1407 fillGfxLine(state, current_text_stroke);
1408 gfxline_free(current_text_stroke);
1409 current_text_stroke = 0;
1410 } else if((render&3) == RENDER_FILLSTROKE) {
1411 fillGfxLine(state, current_text_stroke);
1412 strokeGfxline(state, current_text_stroke,0);
1413 gfxline_free(current_text_stroke);
1414 current_text_stroke = 0;
1415 } else if((render&3) == RENDER_STROKE) {
1416 strokeGfxline(state, current_text_stroke,0);
1417 gfxline_free(current_text_stroke);
1418 current_text_stroke = 0;
1420 device->setparameter(device, "mark","");
1424 void GFXOutputDev::endTextObject(GfxState *state)
1426 int render = state->getRender();
1427 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1429 if(current_text_clip) {
1430 device->setparameter(device, "mark","TXT");
1431 clipToGfxLine(state, current_text_clip);
1432 device->setparameter(device, "mark","");
1433 gfxline_free(current_text_clip);
1434 current_text_clip = 0;
1438 /* the logic seems to be as following:
1439 first, beginType3Char is called, with the charcode and the coordinates.
1440 if this function returns true, it already knew about the char and has now drawn it.
1441 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1442 called with some parameters.
1443 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1444 at the position first passed to beginType3Char). the char ends with endType3Char.
1446 The drawing operations between beginType3Char and endType3Char are somewhat different to
1447 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1448 color determines the color of a font)
1451 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1453 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1456 if(config_extrafontdata && current_fontinfo) {
1458 gfxmatrix_t m = this->current_font_matrix;
1459 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1460 m.m00*=INTERNAL_FONT_SIZE;
1461 m.m01*=INTERNAL_FONT_SIZE;
1462 m.m10*=INTERNAL_FONT_SIZE;
1463 m.m11*=INTERNAL_FONT_SIZE;
1465 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1466 msg("<error> Invalid charid %d for font", charid);
1469 gfxcolor_t col={0,0,0,0};
1470 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1471 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1475 /* the character itself is going to be passed using the draw functions */
1476 return gFalse; /* gTrue= is_in_cache? */
1479 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1481 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1484 void GFXOutputDev::endType3Char(GfxState *state)
1487 msg("<debug> endType3Char");
1490 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1492 this->currentpage = pageNum;
1494 int rot = doc->getPageRotate(1);
1495 gfxcolor_t white = {255,255,255,255};
1496 gfxcolor_t black = {255,0,0,0};
1498 gfxline_t clippath[5];
1500 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1501 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1502 Use CropBox, not MediaBox, as page size
1509 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1510 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1512 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1513 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1515 this->clipmovex = -(int)x1;
1516 this->clipmovey = -(int)y1;
1518 /* apply user clip box */
1519 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1520 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1521 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1522 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1523 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1524 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1527 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1529 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);
1531 msg("<verbose> page is rotated %d degrees", rot);
1533 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1534 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1535 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1536 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1537 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1538 device->startclip(device, clippath); outer_clip_box = 1;
1539 if(!config_transparent) {
1540 device->fill(device, clippath, &white);
1542 states[statepos].clipbbox.xmin = x1;
1543 states[statepos].clipbbox.ymin = x1;
1544 states[statepos].clipbbox.xmax = x2;
1545 states[statepos].clipbbox.ymax = y2;
1547 this->dashPattern = 0;
1548 this->dashLength = 0;
1549 this->dashStart = 0;
1553 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1555 double x1, y1, x2, y2;
1556 gfxline_t points[5];
1559 msg("<debug> drawlink");
1561 link->getRect(&x1, &y1, &x2, &y2);
1562 cvtUserToDev(x1, y1, &x, &y);
1563 points[0].type = gfx_moveTo;
1564 points[0].x = points[4].x = x + user_movex + clipmovex;
1565 points[0].y = points[4].y = y + user_movey + clipmovey;
1566 points[0].next = &points[1];
1567 cvtUserToDev(x2, y1, &x, &y);
1568 points[1].type = gfx_lineTo;
1569 points[1].x = x + user_movex + clipmovex;
1570 points[1].y = y + user_movey + clipmovey;
1571 points[1].next = &points[2];
1572 cvtUserToDev(x2, y2, &x, &y);
1573 points[2].type = gfx_lineTo;
1574 points[2].x = x + user_movex + clipmovex;
1575 points[2].y = y + user_movey + clipmovey;
1576 points[2].next = &points[3];
1577 cvtUserToDev(x1, y2, &x, &y);
1578 points[3].type = gfx_lineTo;
1579 points[3].x = x + user_movex + clipmovex;
1580 points[3].y = y + user_movey + clipmovey;
1581 points[3].next = &points[4];
1582 cvtUserToDev(x1, y1, &x, &y);
1583 points[4].type = gfx_lineTo;
1584 points[4].x = x + user_movex + clipmovex;
1585 points[4].y = y + user_movey + clipmovey;
1588 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1589 points[0].x, points[0].y,
1590 points[1].x, points[1].y,
1591 points[2].x, points[2].y,
1592 points[3].x, points[3].y);
1594 if(getLogLevel() >= LOGLEVEL_TRACE) {
1595 dump_outline(points);
1598 LinkAction*action=link->getAction();
1601 const char*type = "-?-";
1604 msg("<trace> drawlink action=%d", action->getKind());
1605 switch(action->getKind())
1609 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1610 LinkDest *dest=NULL;
1611 if (ha->getDest()==NULL)
1612 dest=catalog->findDest(ha->getNamedDest());
1613 else dest=ha->getDest();
1615 if (dest->isPageRef()){
1616 Ref pageref=dest->getPageRef();
1617 page=catalog->findPage(pageref.num,pageref.gen);
1619 else page=dest->getPageNum();
1620 sprintf(buf, "%d", page);
1627 LinkGoToR*l = (LinkGoToR*)action;
1628 GString*g = l->getFileName();
1630 s = strdup(g->getCString());
1632 /* if the GoToR link has no filename, then
1633 try to find a refernce in the *local*
1635 GString*g = l->getNamedDest();
1637 s = strdup(g->getCString());
1643 LinkNamed*l = (LinkNamed*)action;
1644 GString*name = l->getName();
1646 s = strdup(name->lowerCase()->getCString());
1647 named = name->getCString();
1650 if(strstr(s, "next") || strstr(s, "forward"))
1652 page = currentpage + 1;
1654 else if(strstr(s, "prev") || strstr(s, "back"))
1656 page = currentpage - 1;
1658 else if(strstr(s, "last") || strstr(s, "end"))
1660 if(pages && pagepos>0)
1661 page = pages[pagepos-1];
1663 else if(strstr(s, "first") || strstr(s, "top"))
1671 case actionLaunch: {
1673 LinkLaunch*l = (LinkLaunch*)action;
1674 GString * str = new GString(l->getFileName());
1675 GString * params = l->getParams();
1677 str->append(params);
1678 s = strdup(str->getCString());
1685 LinkURI*l = (LinkURI*)action;
1686 GString*g = l->getURI();
1688 url = g->getCString();
1693 case actionUnknown: {
1695 LinkUnknown*l = (LinkUnknown*)action;
1700 msg("<error> Unknown link type!");
1705 if(!s) s = strdup("-?-");
1707 msg("<trace> drawlink s=%s", s);
1709 if(!linkinfo && (page || s))
1711 msg("<notice> File contains links");
1719 for(t=1;t<=pagepos;t++) {
1720 if(pages[t]==page) {
1729 sprintf(buf, "page%d", lpage);
1730 device->drawlink(device, points, buf);
1734 device->drawlink(device, points, s);
1737 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1741 void GFXOutputDev::saveState(GfxState *state) {
1742 dbg("saveState");dbgindent+=2;
1744 msg("<trace> saveState");
1747 msg("<error> Too many nested states in pdf.");
1751 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1752 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1753 states[statepos].clipping = 0;
1754 states[statepos].clipbbox = states[statepos-1].clipbbox;
1757 void GFXOutputDev::restoreState(GfxState *state) {
1758 dbgindent-=2; dbg("restoreState");
1761 msg("<error> Invalid restoreState");
1764 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1765 states[statepos].clipping?" (end clipping)":"");
1766 if(states[statepos].softmask) {
1767 clearSoftMask(state);
1770 while(states[statepos].clipping) {
1771 device->endclip(device);
1772 states[statepos].clipping--;
1777 void GFXOutputDev::updateLineDash(GfxState *state)
1779 state->getLineDash(&this->dashPattern, &this->dashLength, &this->dashStart);
1780 msg("<debug> updateLineDash, %d dashes", this->dashLength);
1781 if(!this->dashLength) {
1782 this->dashPattern = 0;
1786 void GFXOutputDev::updateLineWidth(GfxState *state)
1788 double width = state->getTransformedLineWidth();
1791 void GFXOutputDev::updateLineCap(GfxState *state)
1793 int c = state->getLineCap();
1796 void GFXOutputDev::updateLineJoin(GfxState *state)
1798 int j = state->getLineJoin();
1801 void GFXOutputDev::updateFillColor(GfxState *state)
1804 double opaq = state->getFillOpacity();
1805 state->getFillRGB(&rgb);
1807 void GFXOutputDev::updateFillOpacity(GfxState *state)
1810 double opaq = state->getFillOpacity();
1811 state->getFillRGB(&rgb);
1812 dbg("update fillopaq %f", opaq);
1814 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1816 double opaq = state->getFillOpacity();
1817 dbg("update strokeopaq %f", opaq);
1819 void GFXOutputDev::updateFillOverprint(GfxState *state)
1821 double opaq = state->getFillOverprint();
1822 dbg("update filloverprint %f", opaq);
1824 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1826 double opaq = state->getStrokeOverprint();
1827 dbg("update strokeoverprint %f", opaq);
1829 void GFXOutputDev::updateTransfer(GfxState *state)
1831 dbg("update transfer");
1835 void GFXOutputDev::updateStrokeColor(GfxState *state)
1838 double opaq = state->getStrokeOpacity();
1839 state->getStrokeRGB(&rgb);
1843 static gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1845 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1846 memset(font, 0, sizeof(gfxfont_t));
1848 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1849 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1853 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1855 //printf("%d glyphs\n", font->num_glyphs);
1856 font->num_glyphs = 0;
1857 for(t=0;t<src->num_glyphs;t++) {
1858 if(src->glyphs[t]) {
1859 SplashPath*path = src->glyphs[t]->path;
1860 int len = path?path->getLength():0;
1861 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1862 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1863 src->glyphs[t]->glyphid = font->num_glyphs;
1864 glyph->unicode = src->glyphs[t]->unicode;
1865 if(glyph->unicode >= font->max_unicode)
1866 font->max_unicode = glyph->unicode+1;
1868 gfxdrawer_target_gfxline(&drawer);
1872 for(s=0;s<len;s++) {
1875 path->getPoint(s, &x, &y, &f);
1878 if(f&splashPathFirst) {
1879 drawer.moveTo(&drawer, x*scale, y*scale);
1881 if(f&splashPathCurve) {
1883 path->getPoint(++s, &x2, &y2, &f);
1884 if(f&splashPathCurve) {
1886 path->getPoint(++s, &x3, &y3, &f);
1887 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1889 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1892 drawer.lineTo(&drawer, x*scale, y*scale);
1894 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1895 // (f&splashPathFirst)?"first":"",
1896 // (f&splashPathLast)?"last":"");
1898 glyph->line = (gfxline_t*)drawer.result(&drawer);
1899 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1903 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1904 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1905 for(t=0;t<font->num_glyphs;t++) {
1906 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1907 font->unicode2glyph[font->glyphs[t].unicode] = t;
1911 msg("<trace> %d glyphs.", t, font->num_glyphs);
1915 void GFXOutputDev::updateFont(GfxState *state)
1917 GfxFont* gfxFont = state->getFont();
1921 char*id = getFontID(gfxFont);
1922 msg("<verbose> Updating font to %s", id);
1923 if(gfxFont->getType() == fontType3) {
1924 infofeature("Type3 fonts");
1925 if(!config_extrafontdata) {
1930 msg("<error> Internal Error: FontID is null");
1934 this->current_fontinfo = this->info->getFont(id);
1935 if(!this->current_fontinfo) {
1936 msg("<error> Internal Error: no fontinfo for font %s", id);
1939 if(!this->current_fontinfo->seen) {
1940 dumpFontInfo("<verbose>", gfxFont);
1943 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1945 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1946 font->id = strdup(id);
1947 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1949 device->addfont(device, font);
1951 current_gfxfont = font;
1954 updateFontMatrix(state);
1957 #define SQR(x) ((x)*(x))
1959 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1961 if((newwidth<2 || newheight<2) ||
1962 (width<=newwidth || height<=newheight))
1964 unsigned char*newdata;
1966 newdata= (unsigned char*)malloc(newwidth*newheight);
1967 double fx = (double)(width)/newwidth;
1968 double fy = (double)(height)/newheight;
1970 int blocksize = (int)(8192/(fx*fy));
1971 int r = 8192*256/palettesize;
1972 for(x=0;x<newwidth;x++) {
1973 double ex = px + fx;
1974 int fromx = (int)px;
1976 int xweight1 = (int)(((fromx+1)-px)*256);
1977 int xweight2 = (int)((ex-tox)*256);
1979 for(y=0;y<newheight;y++) {
1980 double ey = py + fy;
1981 int fromy = (int)py;
1983 int yweight1 = (int)(((fromy+1)-py)*256);
1984 int yweight2 = (int)((ey-toy)*256);
1987 for(xx=fromx;xx<=tox;xx++)
1988 for(yy=fromy;yy<=toy;yy++) {
1989 int b = 1-data[width*yy+xx];
1991 if(xx==fromx) weight = (weight*xweight1)/256;
1992 if(xx==tox) weight = (weight*xweight2)/256;
1993 if(yy==fromy) weight = (weight*yweight1)/256;
1994 if(yy==toy) weight = (weight*yweight2)/256;
1997 //if(a) a=(palettesize-1)*r/blocksize;
1998 newdata[y*newwidth+x] = (a*blocksize)/r;
2006 #define IMAGE_TYPE_JPEG 0
2007 #define IMAGE_TYPE_LOSSLESS 1
2009 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
2010 double x1,double y1,
2011 double x2,double y2,
2012 double x3,double y3,
2013 double x4,double y4, int type)
2015 gfxcolor_t*newpic=0;
2017 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
2018 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
2020 gfxline_t p1,p2,p3,p4,p5;
2021 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
2022 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
2023 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
2024 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
2025 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
2027 {p1.x = (int)(p1.x*20)/20.0;
2028 p1.y = (int)(p1.y*20)/20.0;
2029 p2.x = (int)(p2.x*20)/20.0;
2030 p2.y = (int)(p2.y*20)/20.0;
2031 p3.x = (int)(p3.x*20)/20.0;
2032 p3.y = (int)(p3.y*20)/20.0;
2033 p4.x = (int)(p4.x*20)/20.0;
2034 p4.y = (int)(p4.y*20)/20.0;
2035 p5.x = (int)(p5.x*20)/20.0;
2036 p5.y = (int)(p5.y*20)/20.0;
2040 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2041 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2046 img.data = (gfxcolor_t*)data;
2050 if(type == IMAGE_TYPE_JPEG)
2051 /* TODO: pass image_dpi to device instead */
2052 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2054 dev->fillbitmap(dev, &p1, &img, &m, 0);
2057 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2058 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2060 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2063 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2064 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2066 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2070 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2071 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2072 GBool inlineImg, int mask, int*maskColors,
2073 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2075 double x1,y1,x2,y2,x3,y3,x4,y4;
2076 ImageStream *imgStr;
2081 unsigned char* maskbitmap = 0;
2084 ncomps = colorMap->getNumPixelComps();
2085 bits = colorMap->getBits();
2090 unsigned char buf[8];
2091 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2093 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2094 imgMaskStr->reset();
2095 unsigned char pal[256];
2096 int n = 1 << colorMap->getBits();
2101 maskColorMap->getGray(pixBuf, &gray);
2102 pal[t] = colToByte(gray);
2104 for (y = 0; y < maskHeight; y++) {
2105 for (x = 0; x < maskWidth; x++) {
2106 imgMaskStr->getPixel(buf);
2107 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2112 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2113 imgMaskStr->reset();
2114 for (y = 0; y < maskHeight; y++) {
2115 for (x = 0; x < maskWidth; x++) {
2116 imgMaskStr->getPixel(buf);
2118 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2126 imgStr = new ImageStream(str, width, ncomps,bits);
2129 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2131 msg("<verbose> Ignoring %d by %d image", width, height);
2132 unsigned char buf[8];
2134 for (y = 0; y < height; ++y)
2135 for (x = 0; x < width; ++x) {
2136 imgStr->getPixel(buf);
2144 this->transformXY(state, 0, 1, &x1, &y1);
2145 this->transformXY(state, 0, 0, &x2, &y2);
2146 this->transformXY(state, 1, 0, &x3, &y3);
2147 this->transformXY(state, 1, 1, &x4, &y4);
2149 if(!pbminfo && !(str->getKind()==strDCT)) {
2151 msg("<notice> File contains pbm pictures %s",mask?"(masked)":"");
2155 msg("<verbose> drawing %d by %d masked picture", width, height);
2157 if(!jpeginfo && (str->getKind()==strDCT)) {
2158 msg("<notice> File contains jpeg pictures");
2163 unsigned char buf[8];
2165 unsigned char*pic = new unsigned char[width*height];
2166 gfxcolor_t pal[256];
2168 state->getFillRGB(&rgb);
2170 memset(pal,255,sizeof(pal));
2171 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2172 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2173 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2174 pal[0].a = 255; pal[1].a = 0;
2177 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2178 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2179 for (y = 0; y < height; ++y)
2180 for (x = 0; x < width; ++x)
2182 imgStr->getPixel(buf);
2185 pic[width*y+x] = buf[0];
2188 /* the size of the drawn image is added to the identifier
2189 as the same image may require different bitmaps if displayed
2190 at different sizes (due to antialiasing): */
2193 unsigned char*pic2 = 0;
2196 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2205 height = realheight;
2209 /* make a black/white palette */
2211 float r = 255./(float)(numpalette-1);
2213 for(t=0;t<numpalette;t++) {
2214 pal[t].r = colToByte(rgb.r);
2215 pal[t].g = colToByte(rgb.g);
2216 pal[t].b = colToByte(rgb.b);
2217 pal[t].a = (unsigned char)(t*r);
2221 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2222 for (y = 0; y < height; ++y) {
2223 for (x = 0; x < width; ++x) {
2224 pic2[width*y+x] = pal[pic[y*width+x]];
2227 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2231 if(maskbitmap) free(maskbitmap);
2237 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2238 gfxcolor_t*pic=new gfxcolor_t[width*height];
2239 for (y = 0; y < height; ++y) {
2240 for (x = 0; x < width; ++x) {
2241 imgStr->getPixel(pixBuf);
2242 colorMap->getRGB(pixBuf, &rgb);
2243 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2244 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2245 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2246 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2248 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2252 if(str->getKind()==strDCT)
2253 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2255 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2258 if(maskbitmap) free(maskbitmap);
2261 gfxcolor_t*pic=new gfxcolor_t[width*height];
2262 gfxcolor_t pal[256];
2263 int n = 1 << colorMap->getBits();
2265 for(t=0;t<256;t++) {
2267 colorMap->getRGB(pixBuf, &rgb);
2269 {/*if(maskColors && *maskColors==t) {
2270 msg("<notice> Color %d is transparent", t);
2271 if (imgData->maskColors) {
2273 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2274 if (pix[i] < imgData->maskColors[2*i] ||
2275 pix[i] > imgData->maskColors[2*i+1]) {
2290 pal[t].r = (unsigned char)(colToByte(rgb.r));
2291 pal[t].g = (unsigned char)(colToByte(rgb.g));
2292 pal[t].b = (unsigned char)(colToByte(rgb.b));
2293 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2296 for (y = 0; y < height; ++y) {
2297 for (x = 0; x < width; ++x) {
2298 imgStr->getPixel(pixBuf);
2299 pic[width*y+x] = pal[pixBuf[0]];
2303 if(maskWidth < width && maskHeight < height) {
2304 for(y = 0; y < height; y++) {
2305 for (x = 0; x < width; x++) {
2306 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2310 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2311 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2312 double dx = width / maskWidth;
2313 double dy = height / maskHeight;
2315 for(y = 0; y < maskHeight; y++) {
2317 for (x = 0; x < maskWidth; x++) {
2318 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2319 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2327 height = maskHeight;
2330 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2334 if(maskbitmap) free(maskbitmap);
2339 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2340 int width, int height, GBool invert,
2343 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2344 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2345 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2348 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2349 int width, int height, GfxImageColorMap *colorMap,
2350 int *maskColors, GBool inlineImg)
2352 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2353 colorMap?"colorMap":"no colorMap",
2354 maskColors?"maskColors":"no maskColors",
2356 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2357 colorMap?"colorMap":"no colorMap",
2358 maskColors?"maskColors":"no maskColors",
2361 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2362 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2363 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2366 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2367 int width, int height,
2368 GfxImageColorMap *colorMap,
2369 Stream *maskStr, int maskWidth, int maskHeight,
2372 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2373 colorMap?"colorMap":"no colorMap",
2374 maskWidth, maskHeight);
2375 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2376 colorMap?"colorMap":"no colorMap",
2377 maskWidth, maskHeight);
2379 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2380 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2381 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2384 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2385 int width, int height,
2386 GfxImageColorMap *colorMap,
2388 int maskWidth, int maskHeight,
2389 GfxImageColorMap *maskColorMap)
2391 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2392 colorMap?"colorMap":"no colorMap",
2393 maskWidth, maskHeight);
2394 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2395 colorMap?"colorMap":"no colorMap",
2396 maskWidth, maskHeight);
2398 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2399 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2400 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2403 void GFXOutputDev::stroke(GfxState *state)
2407 GfxPath * path = state->getPath();
2408 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2409 strokeGfxline(state, line, 0);
2413 void GFXOutputDev::fill(GfxState *state)
2415 gfxcolor_t col = getFillColor(state);
2416 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2418 GfxPath * path = state->getPath();
2419 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2420 if(config_optimize_polygons) {
2421 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2425 fillGfxLine(state, line);
2429 void GFXOutputDev::eoFill(GfxState *state)
2431 gfxcolor_t col = getFillColor(state);
2432 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2434 GfxPath * path = state->getPath();
2435 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2436 fillGfxLine(state, line);
2441 static const char* dirseparator()
2450 void addGlobalFont(const char*filename)
2452 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2453 memset(f, 0, sizeof(fontfile_t));
2454 f->filename = filename;
2455 int len = strlen(filename);
2456 char*r1 = strrchr(filename, '/');
2457 char*r2 = strrchr(filename, '\\');
2465 msg("<notice> Adding font \"%s\".", filename);
2466 if(global_fonts_next) {
2467 global_fonts_next->next = f;
2468 global_fonts_next = global_fonts_next->next;
2470 global_fonts_next = global_fonts = f;
2474 void addGlobalLanguageDir(const char*dir)
2476 msg("<notice> Adding %s to language pack directories", dir);
2479 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2480 strcpy(config_file, dir);
2481 strcat(config_file, dirseparator());
2482 strcat(config_file, "add-to-xpdfrc");
2484 fi = fopen(config_file, "rb");
2486 msg("<error> Could not open %s", config_file);
2489 globalParams->parseFile(new GString(config_file), fi);
2493 void addGlobalFontDir(const char*dirname)
2495 #ifdef HAVE_DIRENT_H
2496 msg("<notice> Adding %s to font directories", dirname);
2497 lastfontdir = strdup(dirname);
2498 DIR*dir = opendir(dirname);
2500 msg("<warning> Couldn't open directory %s", dirname);
2505 ent = readdir (dir);
2509 char*name = ent->d_name;
2515 if(!strncasecmp(&name[l-4], ".pfa", 4))
2517 if(!strncasecmp(&name[l-4], ".pfb", 4))
2519 if(!strncasecmp(&name[l-4], ".ttf", 4))
2522 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2523 strcpy(fontname, dirname);
2524 strcat(fontname, dirseparator());
2525 strcat(fontname, name);
2526 addGlobalFont(fontname);
2531 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2535 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2541 this->pagebuflen = 1024;
2542 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2543 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2545 while(pdfpage >= this->pagebuflen)
2547 int oldlen = this->pagebuflen;
2548 this->pagebuflen+=1024;
2549 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2550 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2553 this->pages[pdfpage] = outputpage;
2554 if(pdfpage>this->pagepos)
2555 this->pagepos = pdfpage;
2558 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2559 GfxColorSpace *blendingColorSpace,
2560 GBool isolated, GBool knockout,
2563 const char*colormodename = "";
2565 if(blendingColorSpace) {
2566 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2568 dbg("beginTransparencyGroup %.1f/%.1f/%.1f/%.1f %s isolated=%d knockout=%d forsoftmask=%d", bbox[0],bbox[1],bbox[2],bbox[3], colormodename, isolated, knockout, forSoftMask);
2569 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);
2571 //states[statepos].createsoftmask |= forSoftMask;
2572 states[statepos].createsoftmask = forSoftMask;
2573 states[statepos].transparencygroup = !forSoftMask;
2574 states[statepos].isolated = isolated;
2576 states[statepos].olddevice = this->device;
2577 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2579 gfxdevice_record_init(this->device);
2581 /*if(!forSoftMask) { ////???
2582 state->setFillOpacity(0.0);
2587 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2590 gfxdevice_t*r = this->device;
2592 this->device = states[statepos].olddevice;
2594 gfxresult_t*recording = r->finish(r);
2596 dbg("endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2597 msg("<verbose> endTransparencyGroup forsoftmask=%d recording=%08x/%08x", states[statepos].createsoftmask, r, recording);
2599 if(states[statepos].createsoftmask) {
2600 states[statepos-1].softmaskrecording = recording;
2602 states[statepos-1].grouprecording = recording;
2605 states[statepos].createsoftmask = 0;
2606 states[statepos].transparencygroup = 0;
2610 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2612 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2613 "colordodge","colorburn","hardlight","softlight","difference",
2614 "exclusion","hue","saturation","color","luminosity"};
2616 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2617 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2619 if(state->getBlendMode() == gfxBlendNormal)
2620 infofeature("transparency groups");
2623 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2624 warnfeature(buffer, 0);
2627 gfxresult_t*grouprecording = states[statepos].grouprecording;
2629 int blendmode = state->getBlendMode();
2630 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2631 int alpha = (int)(state->getFillOpacity()*255);
2632 if(blendmode == gfxBlendMultiply && alpha>200)
2635 gfxdevice_ops_init(&ops, this->device, alpha);
2636 gfxresult_record_replay(grouprecording, &ops);
2639 grouprecording->destroy(grouprecording);
2641 states[statepos].grouprecording = 0;
2644 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2646 /* alpha = 1: retrieve mask values from alpha layer
2647 alpha = 0: retrieve mask values from luminance */
2648 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2649 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2650 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2651 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2653 infofeature("soft masks");
2655 warnfeature("soft masks from alpha channel",0);
2657 states[statepos].olddevice = this->device;
2658 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2659 gfxdevice_record_init(this->device);
2661 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2663 states[statepos].softmask = 1;
2664 states[statepos].softmask_alpha = alpha;
2667 static inline Guchar div255(int x) {
2668 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2671 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2673 if(c < min) c = min;
2674 if(c > max) c = max;
2678 void GFXOutputDev::clearSoftMask(GfxState *state)
2680 if(!states[statepos].softmask)
2682 states[statepos].softmask = 0;
2683 dbg("clearSoftMask statepos=%d", statepos);
2684 msg("<verbose> clearSoftMask");
2686 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2687 msg("<error> Error in softmask/tgroup ordering");
2691 gfxresult_t*mask = states[statepos].softmaskrecording;
2692 gfxresult_t*below = this->device->finish(this->device);free(this->device);
2693 this->device = states[statepos].olddevice;
2695 /* get outline of all objects below the soft mask */
2696 gfxdevice_t uniondev;
2697 gfxdevice_union_init(&uniondev, 0);
2698 gfxresult_record_replay(below, &uniondev);
2699 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2700 uniondev.finish(&uniondev);
2701 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2702 gfxline_free(belowoutline);belowoutline=0;
2704 this->device->startclip(this->device, belowoutline);
2705 gfxresult_record_replay(below, this->device);
2706 gfxresult_record_replay(mask, this->device);
2707 this->device->endclip(this->device);
2710 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2711 if(width<=0 || height<=0)
2714 gfxdevice_t belowrender;
2715 gfxdevice_render_init(&belowrender);
2716 if(states[statepos+1].isolated) {
2717 belowrender.setparameter(&belowrender, "fillwhite", "1");
2719 belowrender.setparameter(&belowrender, "antialize", "2");
2720 belowrender.startpage(&belowrender, width, height);
2721 gfxresult_record_replay(below, &belowrender);
2722 belowrender.endpage(&belowrender);
2723 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2724 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2725 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2727 gfxdevice_t maskrender;
2728 gfxdevice_render_init(&maskrender);
2729 maskrender.startpage(&maskrender, width, height);
2730 gfxresult_record_replay(mask, &maskrender);
2731 maskrender.endpage(&maskrender);
2732 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2733 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2735 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2736 msg("<fatal> Internal error in mask drawing");
2741 for(y=0;y<height;y++) {
2742 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2743 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2744 for(x=0;x<width;x++) {
2746 if(states[statepos].softmask_alpha) {
2749 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2752 l2->a = div255(alpha*l2->a);
2754 /* DON'T premultiply alpha- this is done by fillbitmap,
2755 depending on the output device */
2756 //l2->r = div255(alpha*l2->r);
2757 //l2->g = div255(alpha*l2->g);
2758 //l2->b = div255(alpha*l2->b);
2764 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2767 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2768 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2770 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2772 mask->destroy(mask);
2773 below->destroy(below);
2774 maskresult->destroy(maskresult);
2775 belowresult->destroy(belowresult);
2776 states[statepos].softmaskrecording = 0;
2781 // public: ~MemCheck()
2783 // delete globalParams;globalParams=0;
2784 // Object::memCheck(stderr);
2785 // gMemReport(stderr);