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 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 typedef struct _feature
158 struct _feature*next;
160 feature_t*featurewarnings = 0;
162 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
164 feature_t*f = featurewarnings;
166 if(!strcmp(feature, f->string))
170 f = (feature_t*)malloc(sizeof(feature_t));
171 f->string = strdup(feature);
172 f->next = featurewarnings;
175 msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
176 if(this->config_break_on_warning) {
177 msg("<fatal> Aborting conversion due to unsupported feature");
181 msg("<notice> File contains %s",feature);
184 void GFXOutputDev::warnfeature(const char*feature,char fully)
186 showfeature(feature,fully,1);
188 void GFXOutputDev::infofeature(const char*feature)
190 showfeature(feature,0,0);
193 GFXOutputState::GFXOutputState() {
195 this->createsoftmask = 0;
196 this->transparencygroup = 0;
198 this->grouprecording = 0;
202 GBool GFXOutputDev::interpretType3Chars()
207 typedef struct _drawnchar
225 chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
226 memset(chars, 0, sizeof(drawnchar_t)*buf_size);
231 free(chars);chars = 0;
238 chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
242 void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
245 chars[num_chars].x = x;
246 chars[num_chars].y = y;
247 chars[num_chars].color = color;
248 chars[num_chars].charid = charid;
252 char* writeOutStdFont(fontentry* f)
257 char* tmpFileName = mktmpname(namebuf1);
259 sprintf(namebuf2, "%s.afm", tmpFileName);
260 fi = fopen(namebuf2, "wb");
263 fwrite(f->afm, 1, f->afmlen, fi);
266 sprintf(namebuf2, "%s.pfb", tmpFileName);
267 fi = fopen(namebuf2, "wb");
270 fwrite(f->pfb, 1, f->pfblen, fi);
272 return strdup(namebuf2);
274 void unlinkfont(char* filename)
279 msg("<verbose> Removing temporary font file %s", filename);
282 if(!strncmp(&filename[l-4],".afm",4)) {
283 memcpy(&filename[l-4],".pfb",4); unlink(filename);
284 memcpy(&filename[l-4],".pfa",4); unlink(filename);
285 memcpy(&filename[l-4],".afm",4);
288 if(!strncmp(&filename[l-4],".pfa",4)) {
289 memcpy(&filename[l-4],".afm",4); unlink(filename);
290 memcpy(&filename[l-4],".pfa",4);
293 if(!strncmp(&filename[l-4],".pfb",4)) {
294 memcpy(&filename[l-4],".afm",4); unlink(filename);
295 memcpy(&filename[l-4],".pfb",4);
300 static int config_use_fontconfig = 1;
301 static int fcinitcalled = 0;
303 GFXGlobalParams::GFXGlobalParams()
306 //setupBaseFonts(char *dir); //not tested yet
308 GFXGlobalParams::~GFXGlobalParams()
310 msg("<verbose> Performing cleanups");
312 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
313 if(pdf2t1map[t].fullfilename) {
314 unlinkfont(pdf2t1map[t].fullfilename);
317 #ifdef HAVE_FONTCONFIG
318 if(config_use_fontconfig && fcinitcalled)
322 #ifdef HAVE_FONTCONFIG
323 static char fc_ismatch(FcPattern*match, char*family, char*style)
325 char*fcfamily=0,*fcstyle=0,*fcfullname=0,*filename=0;
326 FcBool scalable=FcFalse, outline=FcFalse;
327 FcPatternGetString(match, "family", 0, (FcChar8**)&fcfamily);
328 FcPatternGetString(match, "style", 0, (FcChar8**)&fcstyle);
329 FcPatternGetString(match, "file", 0, (FcChar8**)&filename);
330 FcPatternGetBool(match, "outline", 0, &outline);
331 FcPatternGetBool(match, "scalable", 0, &scalable);
333 if(scalable!=FcTrue || outline!=FcTrue)
336 if (!strcasecmp(fcfamily, family)) {
337 msg("<debug> Font %s-%s (%s) is a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
340 //msg("<debug> Font %s-%s (%s) is NOT a match for %s%s%s", fcfamily, fcstyle, filename, family, style?"-":"", style?style:"");
346 char* fontconfig_searchForFont(char*name)
348 #ifdef HAVE_FONTCONFIG
349 if(!config_use_fontconfig)
352 // call init ony once
356 // check whether we have a config file
357 char* configfile = (char*)FcConfigFilename(0);
358 int configexists = 0;
359 FILE*fi = fopen(configfile, "rb");
361 configexists = 1;fclose(fi);
362 msg("<debug> Initializing FontConfig (configfile=%s)", configfile);
364 msg("<debug> Initializing FontConfig (no configfile)");
368 /* A fontconfig instance which didn't find a configfile is unbelievably
369 cranky, so let's just write out a small xml file and make fontconfig
371 FcConfig*c = FcConfigCreate();
373 char* tmpFileName = mktmpname(namebuf);
374 FILE*fi = fopen(tmpFileName, "wb");
375 fprintf(fi, "<?xml version=\"1.0\"?>\n<fontconfig>\n");//<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
377 fprintf(fi, "<dir>WINDOWSFONTDIR</dir>\n");
379 fprintf(fi, "<dir>~/.fonts</dir>\n");
381 fprintf(fi, "<cachedir>WINDOWSTEMPDIR_FONTCONFIG_CACHE</cachedir>\n");
383 fprintf(fi, "<cachedir>~/.fontconfig</cachedir>\n");
384 fprintf(fi, "</fontconfig>\n");
386 FcConfigParseAndLoad(c, (FcChar8*)tmpFileName, 1);
387 FcConfigBuildFonts(c);
388 FcConfigSetCurrent(c);
392 msg("<debug> FontConfig Initialization failed. Disabling.");
393 config_use_fontconfig = 0;
396 FcConfig * config = FcConfigGetCurrent();
398 msg("<debug> FontConfig Config Initialization failed. Disabling.");
399 config_use_fontconfig = 0;
402 FcFontSet * set = FcConfigGetFonts(config, FcSetSystem);
403 msg("<verbose> FontConfig initialized. Found %d fonts", set?set->nfont:0);
404 if(!set || !set->nfont) {
405 msg("<debug> FontConfig has zero fonts. Disabling.");
406 config_use_fontconfig = 0;
410 if(getLogLevel() >= LOGLEVEL_TRACE) {
412 for(t=0;t<set->nfont;t++) {
413 char*fcfamily=0,*fcstyle=0,*filename=0;
414 FcBool scalable=FcFalse, outline=FcFalse;
415 FcPatternGetString(set->fonts[t], "family", 0, (FcChar8**)&fcfamily);
416 FcPatternGetString(set->fonts[t], "style", 0, (FcChar8**)&fcstyle);
417 FcPatternGetString(set->fonts[t], "file", 0, (FcChar8**)&filename);
418 FcPatternGetBool(set->fonts[t], "outline", 0, &outline);
419 FcPatternGetBool(set->fonts[t], "scalable", 0, &scalable);
420 if(scalable && outline) {
421 msg("<trace> %s-%s -> %s", fcfamily, fcstyle, filename);
427 char*family = strdup(name);
429 char*dash = strchr(family, '-');
430 FcPattern*pattern = 0;
434 msg("<debug> FontConfig: Looking for font %s (family=%s style=%s)", name, family, style);
435 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, FC_STYLE, FcTypeString, style, NULL);
437 msg("<debug> FontConfig: Looking for font %s (family=%s)", name, family);
438 pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, FcTrue, FC_SCALABLE, FcTypeBool, FcTrue, FC_FAMILY, FcTypeString, family, NULL);
442 FcConfigSubstitute(0, pattern, FcMatchPattern);
443 FcDefaultSubstitute(pattern);
445 FcFontSet*set = FcFontSort(0, pattern, 1, 0, &result);
448 for(t=0;t<set->nfont;t++) {
449 FcPattern*match = set->fonts[t];
450 //FcPattern*match = FcFontMatch(0, pattern, &result);
451 if(fc_ismatch(match, family, style)) {
453 if(FcPatternGetString(match, "file", 0, (FcChar8**)&filename) != FcResultMatch) {
454 msg("<debug> FontConfig: Couldn't get fontconfig's filename for font %s", name);
457 //FcPatternDestroy(match);
458 msg("<debug> fontconfig: returning filename %s", filename);
460 FcPatternDestroy(pattern);
461 FcFontSetDestroy(set);
462 return filename?strdup(filename):0;
467 FcPatternDestroy(pattern);
468 FcFontSetDestroy(set);
475 static DisplayFontParamKind detectFontType(const char*filename)
477 if(strstr(filename, ".ttf") || strstr(filename, ".TTF"))
478 return displayFontTT;
479 if(strstr(filename, ".pfa") || strstr(filename, ".PFA") || strstr(filename, ".pfb"))
480 return displayFontT1;
481 return displayFontTT;
484 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
486 msg("<verbose> looking for font %s in global params", fontName->getCString());
488 char*name = fontName->getCString();
490 /* see if it is a pdf standard font */
492 for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
493 if(!strcmp(name, pdf2t1map[t].pdffont)) {
494 if(!pdf2t1map[t].fullfilename) {
495 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
496 if(!pdf2t1map[t].fullfilename) {
497 msg("<error> Couldn't save default font- is the Temp Directory writable?");
499 msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
502 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
503 dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
508 int bestlen = 0x7fffffff;
509 const char*bestfilename = 0;
511 fontfile_t*f = global_fonts;
513 if(strstr(f->filename, name)) {
514 if(f->len < bestlen) {
516 bestfilename = f->filename;
522 /* if we didn't find anything up to now, try looking for the
523 font via fontconfig */
526 filename = fontconfig_searchForFont(name);
528 filename = strdup(bestfilename);
532 DisplayFontParamKind kind = detectFontType(filename);
533 DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), kind);
534 if(kind == displayFontTT) {
535 dfp->tt.fileName = new GString(filename);
537 dfp->t1.fileName = new GString(filename);
542 return GlobalParams::getDisplayFont(fontName);
545 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
549 this->xref = doc->getXRef();
552 this->textmodeinfo = 0;
555 this->type3active = 0;
558 this->substitutepos = 0;
559 this->type3Warning = 0;
560 this->user_movex = 0;
561 this->user_movey = 0;
564 this->user_clipx1 = 0;
565 this->user_clipy1 = 0;
566 this->user_clipx2 = 0;
567 this->user_clipy2 = 0;
568 this->current_text_stroke = 0;
569 this->current_text_clip = 0;
570 this->outer_clip_box = 0;
572 this->pagebuflen = 0;
574 this->config_convertgradients=0;
575 this->config_break_on_warning=0;
576 this->config_remapunicode=0;
577 this->config_transparent=0;
578 this->config_extrafontdata = 0;
579 this->config_fontquality = 10;
580 this->config_optimize_polygons = 0;
582 this->gfxfontlist = gfxfontlist_create();
584 memset(states, 0, sizeof(states));
587 void GFXOutputDev::setParameter(const char*key, const char*value)
589 if(!strcmp(key,"breakonwarning")) {
590 this->config_break_on_warning = atoi(value);
591 } else if(!strcmp(key,"remapunicode")) {
592 this->config_remapunicode = atoi(value);
593 } else if(!strcmp(key,"transparent")) {
594 this->config_transparent = atoi(value);
595 } else if(!strcmp(key,"extrafontdata")) {
596 this->config_extrafontdata = atoi(value);
597 } else if(!strcmp(key,"convertgradients")) {
598 this->config_convertgradients = atoi(value);
599 } else if(!strcmp(key,"optimize_polygons")) {
600 this->config_optimize_polygons = atoi(value);
601 } else if(!strcmp(key,"fontquality")) {
602 this->config_fontquality = atof(value);
603 if(this->config_fontquality<=1)
604 this->config_fontquality=1;
605 } else if(!strcmp(key,"help")) {
606 printf("\nPDF layer options:\n");
607 printf("breakonwarning=0/1 Abort conversion if graphic objects are found which\n");
608 printf(" are not 100%% supported\n");
609 printf("transparent=0/1 Make PDF transparent (alpha background)\n");
610 printf("extrafontdata=0/1 Store Type3 characters and capture characters\n");
611 printf("fontquality=1..100 Curve approximation quality of the fonts\n");
616 void GFXOutputDev::setDevice(gfxdevice_t*dev)
621 void GFXOutputDev::setMove(int x,int y)
623 this->user_movex = x;
624 this->user_movey = y;
627 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
629 if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
630 if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
632 this->user_clipx1 = x1;
633 this->user_clipy1 = y1;
634 this->user_clipx2 = x2;
635 this->user_clipy2 = y2;
638 static char*getFontName(GfxFont*font)
641 GString*gstr = font->getName();
642 char* fname = gstr==0?0:gstr->getCString();
646 sprintf(buf, "UFONT%d", r->num);
647 fontid = strdup(buf);
649 fontid = strdup(fname);
653 char* plus = strchr(fontid, '+');
654 if(plus && plus < &fontid[strlen(fontid)-1]) {
655 fontname = strdup(plus+1);
657 fontname = strdup(fontid);
663 static void dumpFontInfo(const char*loglevel, GfxFont*font);
664 static int lastdumps[1024];
665 static int lastdumppos = 0;
670 static void showFontError(GfxFont*font, int nr)
674 for(t=0;t<lastdumppos;t++)
675 if(lastdumps[t] == r->num)
679 if(lastdumppos<sizeof(lastdumps)/sizeof(int))
680 lastdumps[lastdumppos++] = r->num;
682 msg("<warning> The following font caused problems:");
684 msg("<warning> The following font caused problems (substituting):");
686 msg("<warning> The following Type 3 Font will be rendered as graphics:");
687 dumpFontInfo("<warning>", font);
690 static void dumpFontInfo(const char*loglevel, GfxFont*font)
692 char* id = getFontID(font);
693 char* name = getFontName(font);
694 Ref* r=font->getID();
695 msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
697 GString*gstr = font->getTag();
699 msg("%s| Tag: %s", loglevel, id);
701 if(font->isCIDFont()) msg("%s| is CID font", loglevel);
703 GfxFontType type=font->getType();
705 case fontUnknownType:
706 msg("%s| Type: unknown",loglevel);
709 msg("%s| Type: 1",loglevel);
712 msg("%s| Type: 1C",loglevel);
715 msg("%s| Type: 3",loglevel);
718 msg("%s| Type: TrueType",loglevel);
721 msg("%s| Type: CIDType0",loglevel);
724 msg("%s| Type: CIDType0C",loglevel);
727 msg("%s| Type: CIDType2",loglevel);
732 GBool embedded = font->getEmbeddedFontID(&embRef);
734 if(font->getEmbeddedFontName()) {
735 embeddedName = font->getEmbeddedFontName()->getCString();
738 msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
740 gstr = font->getExtFontFile();
742 msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
744 // Get font descriptor flags.
745 if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
746 if(font->isSerif()) msg("%s| is serif", loglevel);
747 if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
748 if(font->isItalic()) msg("%s| is italic", loglevel);
749 if(font->isBold()) msg("%s| is bold", loglevel);
755 //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");}
756 //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");}
758 void dump_outline(gfxline_t*line)
761 if(line->type == gfx_moveTo) {
762 msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
763 } else if(line->type == gfx_lineTo) {
764 msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
765 } else if(line->type == gfx_splineTo) {
766 msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
772 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
774 int num = path->getNumSubpaths();
777 double lastx=0,lasty=0,posx=0,posy=0;
780 msg("<warning> empty path");
784 gfxdrawer_target_gfxline(&draw);
786 for(t = 0; t < num; t++) {
787 GfxSubpath *subpath = path->getSubpath(t);
788 int subnum = subpath->getNumPoints();
789 double bx=0,by=0,cx=0,cy=0;
791 for(s=0;s<subnum;s++) {
794 this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
797 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
798 draw.lineTo(&draw, lastx, lasty);
800 draw.moveTo(&draw, x,y);
805 } else if(subpath->getCurve(s) && cpos==0) {
809 } else if(subpath->getCurve(s) && cpos==1) {
817 draw.lineTo(&draw, x,y);
819 gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
826 /* fix non-closed lines */
827 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
828 draw.lineTo(&draw, lastx, lasty);
830 gfxline_t*result = (gfxline_t*)draw.result(&draw);
832 gfxline_optimize(result);
837 GBool GFXOutputDev::useTilingPatternFill()
839 infofeature("tiled patterns");
840 // if(config_convertgradients)
844 GBool GFXOutputDev::useShadedFills()
846 infofeature("shaded fills");
847 if(config_convertgradients)
852 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
854 state->transform(x,y,nx,ny);
855 *nx += user_movex + clipmovex;
856 *ny += user_movey + clipmovey;
860 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
861 void GFXOutputDev::tilingPatternFill(GfxState *state, 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)
867 void GFXBitmapOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
868 int paintType, Dict *resDict,
869 double *mat, double *bbox,
870 int x0, int y0, int x1, int y1,
871 double xStep, double yStep)
874 msg("<debug> tilingPatternFill");
875 infofeature("tiling pattern fills");
878 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading)
880 msg("<error> functionShadedFill not supported yet");
881 infofeature("function shaded fills");
884 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
888 colspace->getRGB(col, &rgb);
889 c.r = colToByte(rgb.r);
890 c.g = colToByte(rgb.g);
891 c.b = colToByte(rgb.b);
896 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
898 double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
899 shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
902 this->transformXY(state, x0,y0, &x0,&y0);
903 this->transformXY(state, x1,y1, &x1,&y1);
904 this->transformXY(state, x2,y2, &x2,&y2);
909 shading->getColor(0.0, &color0);
910 shading->getColor(0.5, &color1);
911 shading->getColor(1.0, &color2);
913 GfxColorSpace* colspace = shading->getColorSpace();
915 msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
916 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
917 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
918 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
919 infofeature("radial shaded fills");
921 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
925 g[0].color = col2col(colspace, &color0);
926 g[1].color = col2col(colspace, &color1);
927 g[2].color = col2col(colspace, &color2);
932 gfxbbox_t b = states[statepos].clipbbox;
933 gfxline_t p1,p2,p3,p4,p5;
934 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
935 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
936 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
937 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
938 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
941 //m.m00 = (x3-x0); m.m10 = (x1-x0);
942 //m.m01 = (y3-y0); m.m11 = (y1-y0);
943 //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
944 m.m00 = (x1-x0); m.m10 = (x2-x0);
945 m.m01 = (y1-y0); m.m11 = (y2-y0);
949 device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
953 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
956 shading->getCoords(&x0,&y0,&x1,&y1);
957 this->transformXY(state, x0,y0,&x0,&y0);
958 this->transformXY(state, x1,y1,&x1,&y1);
963 shading->getColor(0.0, &color0);
964 shading->getColor(0.5, &color1);
965 shading->getColor(1.0, &color2);
967 GfxColorSpace* colspace = shading->getColorSpace();
969 msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
970 colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]),
971 colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
972 colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
974 infofeature("axial shaded fills");
976 gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
980 g[0].color = col2col(colspace, &color0);
981 g[1].color = col2col(colspace, &color1);
982 g[2].color = col2col(colspace, &color2);
987 double xMin,yMin,xMax,yMax;
988 state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
989 this->transformXY(state, xMin, yMin, &xMin, &yMin);
990 msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
993 xMin = 1024; yMin = 1024;
995 gfxbbox_t b = states[statepos].clipbbox;
996 gfxline_t p1,p2,p3,p4,p5;
997 p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
998 p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
999 p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
1000 p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
1001 p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
1003 /* the gradient starts at (-1.0,0.0), so move (0,0) to
1004 the middle of the two control points */
1006 m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
1007 m.m01 = (y1-y0)/2; m.m11 = (x1-x0)/2;
1008 m.tx = (x0 + x1)/2 - 0.5;
1009 m.ty = (y0 + y1)/2 - 0.5;
1011 device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
1015 GBool GFXOutputDev::useDrawForm()
1017 infofeature("forms");
1020 void GFXOutputDev::drawForm(Ref id)
1022 msg("<error> drawForm not implemented");
1024 GBool GFXOutputDev::needNonText()
1028 void GFXOutputDev::endPage()
1030 msg("<verbose> endPage (GfxOutputDev)");
1031 if(outer_clip_box) {
1032 device->endclip(device);
1037 #define STROKE_FILL 1
1038 #define STROKE_CLIP 2
1039 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
1041 int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
1042 int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
1043 double miterLimit = state->getMiterLimit();
1044 double width = state->getTransformedLineWidth();
1047 double opaq = state->getStrokeOpacity();
1049 state->getFillRGB(&rgb);
1051 state->getStrokeRGB(&rgb);
1053 col.r = colToByte(rgb.r);
1054 col.g = colToByte(rgb.g);
1055 col.b = colToByte(rgb.b);
1056 col.a = (unsigned char)(opaq*255);
1058 gfx_capType capType = gfx_capRound;
1059 if(lineCap == 0) capType = gfx_capButt;
1060 else if(lineCap == 1) capType = gfx_capRound;
1061 else if(lineCap == 2) capType = gfx_capSquare;
1063 gfx_joinType joinType = gfx_joinRound;
1064 if(lineJoin == 0) joinType = gfx_joinMiter;
1065 else if(lineJoin == 1) joinType = gfx_joinRound;
1066 else if(lineJoin == 2) joinType = gfx_joinBevel;
1069 double dashphase = 0;
1071 state->getLineDash(&ldash, &dashnum, &dashphase);
1073 gfxline_t*line2 = 0;
1075 if(dashnum && ldash) {
1076 float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
1078 msg("<trace> %d dashes", dashnum);
1079 msg("<trace> | phase: %f", dashphase);
1080 for(t=0;t<dashnum;t++) {
1081 dash[t] = (float)ldash[t];
1082 msg("<trace> | d%-3d: %f", t, ldash[t]);
1085 if(getLogLevel() >= LOGLEVEL_TRACE) {
1089 line2 = gfxtool_dash_line(line, dash, (float)dashphase);
1092 msg("<trace> After dashing:");
1095 if(getLogLevel() >= LOGLEVEL_TRACE) {
1096 msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
1098 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
1099 lineCap==0?"butt": (lineJoin==1?"round":"square"),
1101 col.r,col.g,col.b,col.a
1106 if(flags&STROKE_FILL) {
1107 gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
1108 gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
1109 if(getLogLevel() >= LOGLEVEL_TRACE) {
1110 dump_outline(gfxline);
1113 msg("<warning> Empty polygon (resulting from stroked line)");
1115 if(flags&STROKE_CLIP) {
1116 device->startclip(device, gfxline);
1117 states[statepos].clipping++;
1119 device->fill(device, gfxline, &col);
1124 if(flags&STROKE_CLIP)
1125 msg("<error> Stroke&clip not supported at the same time");
1126 device->stroke(device, line, width, &col, capType, joinType, miterLimit);
1130 gfxline_free(line2);
1133 gfxcolor_t getFillColor(GfxState * state)
1136 double opaq = state->getFillOpacity();
1137 state->getFillRGB(&rgb);
1139 col.r = colToByte(rgb.r);
1140 col.g = colToByte(rgb.g);
1141 col.b = colToByte(rgb.b);
1142 col.a = (unsigned char)(opaq*255);
1146 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
1148 gfxcolor_t col = getFillColor(state);
1150 if(getLogLevel() >= LOGLEVEL_TRACE) {
1151 msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
1154 device->fill(device, line, &col);
1157 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
1159 if(getLogLevel() >= LOGLEVEL_TRACE) {
1162 gfxbbox_t bbox = gfxline_getbbox(line);
1163 gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
1165 device->startclip(device, line);
1166 states[statepos].clipping++;
1169 void GFXOutputDev::clip(GfxState *state)
1171 GfxPath * path = state->getPath();
1172 msg("<trace> clip");
1173 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1174 if(config_optimize_polygons) {
1175 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
1179 clipToGfxLine(state, line);
1183 void GFXOutputDev::eoClip(GfxState *state)
1185 GfxPath * path = state->getPath();
1186 gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1187 clipToGfxLine(state, line);
1190 void GFXOutputDev::clipToStrokePath(GfxState *state)
1192 GfxPath * path = state->getPath();
1193 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1195 if(getLogLevel() >= LOGLEVEL_TRACE) {
1196 msg("<trace> cliptostrokepath");
1200 strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1204 void GFXOutputDev::finish()
1206 if(outer_clip_box) {
1208 device->endclip(device);
1214 GFXOutputDev::~GFXOutputDev()
1219 free(this->pages); this->pages = 0;
1222 gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1224 GBool GFXOutputDev::upsideDown()
1228 GBool GFXOutputDev::useDrawChar()
1233 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1234 "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1236 static char tmp_printstr[4096];
1237 char* makeStringPrintable(char*str)
1239 int len = strlen(str);
1246 for(t=0;t<len;t++) {
1251 tmp_printstr[t] = c;
1254 tmp_printstr[len++] = '.';
1255 tmp_printstr[len++] = '.';
1256 tmp_printstr[len++] = '.';
1258 tmp_printstr[len] = 0;
1259 return tmp_printstr;
1261 #define INTERNAL_FONT_SIZE 1024.0
1262 void GFXOutputDev::updateFontMatrix(GfxState*state)
1264 double* ctm = state->getCTM();
1265 double fontSize = state->getFontSize();
1266 double*textMat = state->getTextMat();
1268 /* taking the absolute value of horizScaling seems to be required for
1269 some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1270 double hscale = fabs(state->getHorizScaling());
1272 // from xpdf-3.02/SplashOutputDev:updateFont
1273 double mm11 = textMat[0] * fontSize * hscale;
1274 double mm12 = textMat[1] * fontSize * hscale;
1275 double mm21 = textMat[2] * fontSize;
1276 double mm22 = textMat[3] * fontSize;
1278 // multiply with ctm, like state->getFontTransMat() does
1279 this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1280 this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1281 this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1282 this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1283 this->current_font_matrix.tx = 0;
1284 this->current_font_matrix.ty = 0;
1287 void GFXOutputDev::beginString(GfxState *state, GString *s)
1289 int render = state->getRender();
1290 if(current_text_stroke) {
1291 msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1294 msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1297 static gfxline_t* mkEmptyGfxShape(double x, double y)
1299 gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1300 line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1304 static char isValidUnicode(int c)
1306 if(c>=32 && c<0x2fffe)
1311 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1312 double dx, double dy,
1313 double originX, double originY,
1314 CharCode charid, int nBytes, Unicode *_u, int uLen)
1316 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1317 msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1321 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1323 int render = state->getRender();
1324 gfxcolor_t col = getFillColor(state);
1326 // check for invisible text -- this is used by Acrobat Capture
1327 if (render == RENDER_INVISIBLE) {
1329 if(!config_extrafontdata)
1333 GfxFont*font = state->getFont();
1335 if(font->getType() == fontType3) {
1336 /* type 3 chars are passed as graphics */
1337 msg("<debug> type3 char at %f/%f", x, y);
1341 Unicode u = uLen?(_u[0]):0;
1342 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);
1344 gfxmatrix_t m = this->current_font_matrix;
1345 this->transformXY(state, x, y, &m.tx, &m.ty);
1347 if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1348 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1350 msg("<debug> Drawing glyph %d as shape", charid);
1352 msg("<notice> Some texts will be rendered as shape");
1355 gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1356 gfxline_t*tglyph = gfxline_clone(glyph);
1357 gfxline_transform(tglyph, &m);
1358 if((render&3) != RENDER_INVISIBLE) {
1359 gfxline_t*add = gfxline_clone(tglyph);
1360 current_text_stroke = gfxline_append(current_text_stroke, add);
1362 if(render&RENDER_CLIP) {
1363 gfxline_t*add = gfxline_clone(tglyph);
1364 current_text_clip = gfxline_append(current_text_clip, add);
1365 if(!current_text_clip) {
1366 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1369 gfxline_free(tglyph);
1373 void GFXOutputDev::endString(GfxState *state)
1375 int render = state->getRender();
1376 msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1378 if(current_text_stroke) {
1379 /* fillstroke and stroke text rendering objects we can process right
1380 now (as there may be texts of other rendering modes in this
1381 text object)- clipping objects have to wait until endTextObject,
1383 device->setparameter(device, "mark","TXT");
1384 if((render&3) == RENDER_FILL) {
1385 fillGfxLine(state, current_text_stroke);
1386 gfxline_free(current_text_stroke);
1387 current_text_stroke = 0;
1388 } else if((render&3) == RENDER_FILLSTROKE) {
1389 fillGfxLine(state, current_text_stroke);
1390 strokeGfxline(state, current_text_stroke,0);
1391 gfxline_free(current_text_stroke);
1392 current_text_stroke = 0;
1393 } else if((render&3) == RENDER_STROKE) {
1394 strokeGfxline(state, current_text_stroke,0);
1395 gfxline_free(current_text_stroke);
1396 current_text_stroke = 0;
1398 device->setparameter(device, "mark","");
1402 void GFXOutputDev::endTextObject(GfxState *state)
1404 int render = state->getRender();
1405 msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1407 if(current_text_clip) {
1408 device->setparameter(device, "mark","TXT");
1409 clipToGfxLine(state, current_text_clip);
1410 device->setparameter(device, "mark","");
1411 gfxline_free(current_text_clip);
1412 current_text_clip = 0;
1416 /* the logic seems to be as following:
1417 first, beginType3Char is called, with the charcode and the coordinates.
1418 if this function returns true, it already knew about the char and has now drawn it.
1419 if the function returns false, it's a new char, and type3D0 and/or type3D1 might be
1420 called with some parameters.
1421 Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1422 at the position first passed to beginType3Char). the char ends with endType3Char.
1424 The drawing operations between beginType3Char and endType3Char are somewhat different to
1425 the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1426 color determines the color of a font)
1429 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1431 msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1434 if(config_extrafontdata && current_fontinfo) {
1436 gfxmatrix_t m = this->current_font_matrix;
1437 this->transformXY(state, 0, 0, &m.tx, &m.ty);
1438 m.m00*=INTERNAL_FONT_SIZE;
1439 m.m01*=INTERNAL_FONT_SIZE;
1440 m.m10*=INTERNAL_FONT_SIZE;
1441 m.m11*=INTERNAL_FONT_SIZE;
1443 if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1444 msg("<error> Invalid charid %d for font", charid);
1447 gfxcolor_t col={0,0,0,0};
1448 CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1449 device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1453 /* the character itself is going to be passed using the draw functions */
1454 return gFalse; /* gTrue= is_in_cache? */
1457 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1459 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1462 void GFXOutputDev::endType3Char(GfxState *state)
1465 msg("<debug> endType3Char");
1468 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
1470 this->currentpage = pageNum;
1472 int rot = doc->getPageRotate(1);
1473 gfxcolor_t white = {255,255,255,255};
1474 gfxcolor_t black = {255,0,0,0};
1476 gfxline_t clippath[5];
1478 /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1479 state->transform(state->getX2(),state->getY2(),&x2,&y2);
1480 Use CropBox, not MediaBox, as page size
1487 state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1488 state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1490 if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1491 if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1493 this->clipmovex = -(int)x1;
1494 this->clipmovey = -(int)y1;
1496 /* apply user clip box */
1497 if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1498 /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1499 /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1500 /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1501 /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1502 msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1505 //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1507 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);
1509 msg("<verbose> page is rotated %d degrees", rot);
1511 clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1512 clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1513 clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1514 clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1515 clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1516 device->startclip(device, clippath); outer_clip_box = 1;
1517 if(!config_transparent) {
1518 device->fill(device, clippath, &white);
1520 states[statepos].clipbbox.xmin = x1;
1521 states[statepos].clipbbox.ymin = x1;
1522 states[statepos].clipbbox.xmax = x2;
1523 states[statepos].clipbbox.ymax = y2;
1527 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1529 double x1, y1, x2, y2;
1530 gfxline_t points[5];
1533 msg("<debug> drawlink");
1535 link->getRect(&x1, &y1, &x2, &y2);
1536 cvtUserToDev(x1, y1, &x, &y);
1537 points[0].type = gfx_moveTo;
1538 points[0].x = points[4].x = x + user_movex + clipmovex;
1539 points[0].y = points[4].y = y + user_movey + clipmovey;
1540 points[0].next = &points[1];
1541 cvtUserToDev(x2, y1, &x, &y);
1542 points[1].type = gfx_lineTo;
1543 points[1].x = x + user_movex + clipmovex;
1544 points[1].y = y + user_movey + clipmovey;
1545 points[1].next = &points[2];
1546 cvtUserToDev(x2, y2, &x, &y);
1547 points[2].type = gfx_lineTo;
1548 points[2].x = x + user_movex + clipmovex;
1549 points[2].y = y + user_movey + clipmovey;
1550 points[2].next = &points[3];
1551 cvtUserToDev(x1, y2, &x, &y);
1552 points[3].type = gfx_lineTo;
1553 points[3].x = x + user_movex + clipmovex;
1554 points[3].y = y + user_movey + clipmovey;
1555 points[3].next = &points[4];
1556 cvtUserToDev(x1, y1, &x, &y);
1557 points[4].type = gfx_lineTo;
1558 points[4].x = x + user_movex + clipmovex;
1559 points[4].y = y + user_movey + clipmovey;
1562 msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1563 points[0].x, points[0].y,
1564 points[1].x, points[1].y,
1565 points[2].x, points[2].y,
1566 points[3].x, points[3].y);
1568 if(getLogLevel() >= LOGLEVEL_TRACE) {
1569 dump_outline(points);
1572 LinkAction*action=link->getAction();
1575 const char*type = "-?-";
1578 msg("<trace> drawlink action=%d", action->getKind());
1579 switch(action->getKind())
1583 LinkGoTo *ha=(LinkGoTo *)link->getAction();
1584 LinkDest *dest=NULL;
1585 if (ha->getDest()==NULL)
1586 dest=catalog->findDest(ha->getNamedDest());
1587 else dest=ha->getDest();
1589 if (dest->isPageRef()){
1590 Ref pageref=dest->getPageRef();
1591 page=catalog->findPage(pageref.num,pageref.gen);
1593 else page=dest->getPageNum();
1594 sprintf(buf, "%d", page);
1601 LinkGoToR*l = (LinkGoToR*)action;
1602 GString*g = l->getFileName();
1604 s = strdup(g->getCString());
1606 /* if the GoToR link has no filename, then
1607 try to find a refernce in the *local*
1609 GString*g = l->getNamedDest();
1611 s = strdup(g->getCString());
1617 LinkNamed*l = (LinkNamed*)action;
1618 GString*name = l->getName();
1620 s = strdup(name->lowerCase()->getCString());
1621 named = name->getCString();
1624 if(strstr(s, "next") || strstr(s, "forward"))
1626 page = currentpage + 1;
1628 else if(strstr(s, "prev") || strstr(s, "back"))
1630 page = currentpage - 1;
1632 else if(strstr(s, "last") || strstr(s, "end"))
1634 if(pages && pagepos>0)
1635 page = pages[pagepos-1];
1637 else if(strstr(s, "first") || strstr(s, "top"))
1645 case actionLaunch: {
1647 LinkLaunch*l = (LinkLaunch*)action;
1648 GString * str = new GString(l->getFileName());
1649 GString * params = l->getParams();
1651 str->append(params);
1652 s = strdup(str->getCString());
1659 LinkURI*l = (LinkURI*)action;
1660 GString*g = l->getURI();
1662 url = g->getCString();
1667 case actionUnknown: {
1669 LinkUnknown*l = (LinkUnknown*)action;
1674 msg("<error> Unknown link type!");
1679 if(!s) s = strdup("-?-");
1681 msg("<trace> drawlink s=%s", s);
1683 if(!linkinfo && (page || s))
1685 msg("<notice> File contains links");
1693 for(t=1;t<=pagepos;t++) {
1694 if(pages[t]==page) {
1703 sprintf(buf, "page%d", lpage);
1704 device->drawlink(device, points, buf);
1708 device->drawlink(device, points, s);
1711 msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1715 void GFXOutputDev::saveState(GfxState *state) {
1716 dbg("saveState");dbgindent+=2;
1718 msg("<trace> saveState");
1721 msg("<error> Too many nested states in pdf.");
1725 states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1726 states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1727 states[statepos].clipping = 0;
1728 states[statepos].clipbbox = states[statepos-1].clipbbox;
1731 void GFXOutputDev::restoreState(GfxState *state) {
1732 dbgindent-=2; dbg("restoreState");
1735 msg("<error> Invalid restoreState");
1738 msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1739 states[statepos].clipping?" (end clipping)":"");
1740 if(states[statepos].softmask) {
1741 clearSoftMask(state);
1744 while(states[statepos].clipping) {
1745 device->endclip(device);
1746 states[statepos].clipping--;
1751 void GFXOutputDev::updateLineWidth(GfxState *state)
1753 double width = state->getTransformedLineWidth();
1754 //swfoutput_setlinewidth(&device, width);
1757 void GFXOutputDev::updateLineCap(GfxState *state)
1759 int c = state->getLineCap();
1762 void GFXOutputDev::updateLineJoin(GfxState *state)
1764 int j = state->getLineJoin();
1767 void GFXOutputDev::updateFillColor(GfxState *state)
1770 double opaq = state->getFillOpacity();
1771 state->getFillRGB(&rgb);
1773 void GFXOutputDev::updateFillOpacity(GfxState *state)
1776 double opaq = state->getFillOpacity();
1777 state->getFillRGB(&rgb);
1778 dbg("update fillopaq %f", opaq);
1780 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1782 double opaq = state->getFillOpacity();
1783 dbg("update strokeopaq %f", opaq);
1785 void GFXOutputDev::updateFillOverprint(GfxState *state)
1787 double opaq = state->getFillOverprint();
1788 dbg("update filloverprint %f", opaq);
1790 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1792 double opaq = state->getStrokeOverprint();
1793 dbg("update strokeoverprint %f", opaq);
1795 void GFXOutputDev::updateTransfer(GfxState *state)
1797 dbg("update transfer");
1801 void GFXOutputDev::updateStrokeColor(GfxState *state)
1804 double opaq = state->getStrokeOpacity();
1805 state->getStrokeRGB(&rgb);
1809 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1811 gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1812 memset(font, 0, sizeof(gfxfont_t));
1814 font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1815 memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1816 font->id = strdup(getFontID(xpdffont));
1819 double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1821 //printf("%d glyphs\n", font->num_glyphs);
1822 font->num_glyphs = 0;
1823 for(t=0;t<src->num_glyphs;t++) {
1824 if(src->glyphs[t]) {
1825 SplashPath*path = src->glyphs[t]->path;
1826 int len = path?path->getLength():0;
1827 //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1828 gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1829 src->glyphs[t]->glyphid = font->num_glyphs;
1830 glyph->unicode = src->glyphs[t]->unicode;
1831 if(glyph->unicode >= font->max_unicode)
1832 font->max_unicode = glyph->unicode+1;
1834 gfxdrawer_target_gfxline(&drawer);
1838 for(s=0;s<len;s++) {
1841 path->getPoint(s, &x, &y, &f);
1844 if(f&splashPathFirst) {
1845 drawer.moveTo(&drawer, x*scale, y*scale);
1847 if(f&splashPathCurve) {
1849 path->getPoint(++s, &x2, &y2, &f);
1850 if(f&splashPathCurve) {
1852 path->getPoint(++s, &x3, &y3, &f);
1853 gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1855 drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1858 drawer.lineTo(&drawer, x*scale, y*scale);
1860 // printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1861 // (f&splashPathFirst)?"first":"",
1862 // (f&splashPathLast)?"last":"");
1864 glyph->line = (gfxline_t*)drawer.result(&drawer);
1865 glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1869 font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1870 memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1871 for(t=0;t<font->num_glyphs;t++) {
1872 if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1873 font->unicode2glyph[font->glyphs[t].unicode] = t;
1877 msg("<trace> %d glyphs.", t, font->num_glyphs);
1881 void GFXOutputDev::updateFont(GfxState *state)
1883 GfxFont* gfxFont = state->getFont();
1887 char*id = getFontID(gfxFont);
1888 msg("<verbose> Updating font to %s", id);
1889 if(gfxFont->getType() == fontType3) {
1890 infofeature("Type3 fonts");
1891 if(!config_extrafontdata) {
1896 msg("<error> Internal Error: FontID is null");
1900 this->current_fontinfo = this->info->getFont(id);
1901 if(!this->current_fontinfo) {
1902 msg("<error> Internal Error: no fontinfo for font %s", id);
1905 if(!this->current_fontinfo->seen) {
1906 dumpFontInfo("<verbose>", gfxFont);
1909 gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1911 font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1912 font->id = strdup(id);
1913 this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1915 device->addfont(device, font);
1917 current_gfxfont = font;
1920 updateFontMatrix(state);
1923 #define SQR(x) ((x)*(x))
1925 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1927 if((newwidth<2 || newheight<2) ||
1928 (width<=newwidth || height<=newheight))
1930 unsigned char*newdata;
1932 newdata= (unsigned char*)malloc(newwidth*newheight);
1933 double fx = (double)(width)/newwidth;
1934 double fy = (double)(height)/newheight;
1936 int blocksize = (int)(8192/(fx*fy));
1937 int r = 8192*256/palettesize;
1938 for(x=0;x<newwidth;x++) {
1939 double ex = px + fx;
1940 int fromx = (int)px;
1942 int xweight1 = (int)(((fromx+1)-px)*256);
1943 int xweight2 = (int)((ex-tox)*256);
1945 for(y=0;y<newheight;y++) {
1946 double ey = py + fy;
1947 int fromy = (int)py;
1949 int yweight1 = (int)(((fromy+1)-py)*256);
1950 int yweight2 = (int)((ey-toy)*256);
1953 for(xx=fromx;xx<=tox;xx++)
1954 for(yy=fromy;yy<=toy;yy++) {
1955 int b = 1-data[width*yy+xx];
1957 if(xx==fromx) weight = (weight*xweight1)/256;
1958 if(xx==tox) weight = (weight*xweight2)/256;
1959 if(yy==fromy) weight = (weight*yweight1)/256;
1960 if(yy==toy) weight = (weight*yweight2)/256;
1963 //if(a) a=(palettesize-1)*r/blocksize;
1964 newdata[y*newwidth+x] = (a*blocksize)/r;
1972 #define IMAGE_TYPE_JPEG 0
1973 #define IMAGE_TYPE_LOSSLESS 1
1975 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
1976 double x1,double y1,
1977 double x2,double y2,
1978 double x3,double y3,
1979 double x4,double y4, int type)
1981 gfxcolor_t*newpic=0;
1983 double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1984 double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1986 gfxline_t p1,p2,p3,p4,p5;
1987 p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1988 p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1989 p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1990 p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1991 p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1993 {p1.x = (int)(p1.x*20)/20.0;
1994 p1.y = (int)(p1.y*20)/20.0;
1995 p2.x = (int)(p2.x*20)/20.0;
1996 p2.y = (int)(p2.y*20)/20.0;
1997 p3.x = (int)(p3.x*20)/20.0;
1998 p3.y = (int)(p3.y*20)/20.0;
1999 p4.x = (int)(p4.x*20)/20.0;
2000 p4.y = (int)(p4.y*20)/20.0;
2001 p5.x = (int)(p5.x*20)/20.0;
2002 p5.y = (int)(p5.y*20)/20.0;
2006 m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
2007 m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
2012 img.data = (gfxcolor_t*)data;
2016 if(type == IMAGE_TYPE_JPEG)
2017 /* TODO: pass image_dpi to device instead */
2018 dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
2020 dev->fillbitmap(dev, &p1, &img, &m, 0);
2023 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2024 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2026 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
2029 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
2030 double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
2032 drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
2036 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
2037 int width, int height, GfxImageColorMap*colorMap, GBool invert,
2038 GBool inlineImg, int mask, int*maskColors,
2039 Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
2041 double x1,y1,x2,y2,x3,y3,x4,y4;
2042 ImageStream *imgStr;
2047 unsigned char* maskbitmap = 0;
2050 ncomps = colorMap->getNumPixelComps();
2051 bits = colorMap->getBits();
2056 unsigned char buf[8];
2057 maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
2059 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
2060 imgMaskStr->reset();
2061 unsigned char pal[256];
2062 int n = 1 << colorMap->getBits();
2067 maskColorMap->getGray(pixBuf, &gray);
2068 pal[t] = colToByte(gray);
2070 for (y = 0; y < maskHeight; y++) {
2071 for (x = 0; x < maskWidth; x++) {
2072 imgMaskStr->getPixel(buf);
2073 maskbitmap[y*maskWidth+x] = pal[buf[0]];
2078 ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
2079 imgMaskStr->reset();
2080 for (y = 0; y < maskHeight; y++) {
2081 for (x = 0; x < maskWidth; x++) {
2082 imgMaskStr->getPixel(buf);
2084 maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
2092 imgStr = new ImageStream(str, width, ncomps,bits);
2095 if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
2097 msg("<verbose> Ignoring %d by %d image", width, height);
2098 unsigned char buf[8];
2100 for (y = 0; y < height; ++y)
2101 for (x = 0; x < width; ++x) {
2102 imgStr->getPixel(buf);
2110 this->transformXY(state, 0, 1, &x1, &y1);
2111 this->transformXY(state, 0, 0, &x2, &y2);
2112 this->transformXY(state, 1, 0, &x3, &y3);
2113 this->transformXY(state, 1, 1, &x4, &y4);
2115 if(!pbminfo && !(str->getKind()==strDCT)) {
2117 msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
2121 msg("<verbose> drawing %d by %d masked picture", width, height);
2123 if(!jpeginfo && (str->getKind()==strDCT)) {
2124 msg("<notice> file contains jpeg pictures");
2129 unsigned char buf[8];
2131 unsigned char*pic = new unsigned char[width*height];
2132 gfxcolor_t pal[256];
2134 state->getFillRGB(&rgb);
2136 memset(pal,255,sizeof(pal));
2137 pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
2138 pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
2139 pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
2140 pal[0].a = 255; pal[1].a = 0;
2143 int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
2144 int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
2145 for (y = 0; y < height; ++y)
2146 for (x = 0; x < width; ++x)
2148 imgStr->getPixel(buf);
2151 pic[width*y+x] = buf[0];
2154 /* the size of the drawn image is added to the identifier
2155 as the same image may require different bitmaps if displayed
2156 at different sizes (due to antialiasing): */
2159 unsigned char*pic2 = 0;
2162 pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
2171 height = realheight;
2175 /* make a black/white palette */
2177 float r = 255./(float)(numpalette-1);
2179 for(t=0;t<numpalette;t++) {
2180 pal[t].r = colToByte(rgb.r);
2181 pal[t].g = colToByte(rgb.g);
2182 pal[t].b = colToByte(rgb.b);
2183 pal[t].a = (unsigned char)(t*r);
2187 gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2188 for (y = 0; y < height; ++y) {
2189 for (x = 0; x < width; ++x) {
2190 pic2[width*y+x] = pal[pic[y*width+x]];
2193 drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2197 if(maskbitmap) free(maskbitmap);
2203 if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2204 gfxcolor_t*pic=new gfxcolor_t[width*height];
2205 for (y = 0; y < height; ++y) {
2206 for (x = 0; x < width; ++x) {
2207 imgStr->getPixel(pixBuf);
2208 colorMap->getRGB(pixBuf, &rgb);
2209 pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2210 pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2211 pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2212 pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2214 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2218 if(str->getKind()==strDCT)
2219 drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2221 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2224 if(maskbitmap) free(maskbitmap);
2227 gfxcolor_t*pic=new gfxcolor_t[width*height];
2228 gfxcolor_t pal[256];
2229 int n = 1 << colorMap->getBits();
2231 for(t=0;t<256;t++) {
2233 colorMap->getRGB(pixBuf, &rgb);
2235 {/*if(maskColors && *maskColors==t) {
2236 msg("<notice> Color %d is transparent", t);
2237 if (imgData->maskColors) {
2239 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2240 if (pix[i] < imgData->maskColors[2*i] ||
2241 pix[i] > imgData->maskColors[2*i+1]) {
2256 pal[t].r = (unsigned char)(colToByte(rgb.r));
2257 pal[t].g = (unsigned char)(colToByte(rgb.g));
2258 pal[t].b = (unsigned char)(colToByte(rgb.b));
2259 pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2262 for (y = 0; y < height; ++y) {
2263 for (x = 0; x < width; ++x) {
2264 imgStr->getPixel(pixBuf);
2265 pic[width*y+x] = pal[pixBuf[0]];
2269 if(maskWidth < width && maskHeight < height) {
2270 for(y = 0; y < height; y++) {
2271 for (x = 0; x < width; x++) {
2272 pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2276 msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2277 gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2278 double dx = width / maskWidth;
2279 double dy = height / maskHeight;
2281 for(y = 0; y < maskHeight; y++) {
2283 for (x = 0; x < maskWidth; x++) {
2284 newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2285 newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2293 height = maskHeight;
2296 drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2300 if(maskbitmap) free(maskbitmap);
2305 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2306 int width, int height, GBool invert,
2309 dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2310 msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2311 drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2314 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2315 int width, int height, GfxImageColorMap *colorMap,
2316 int *maskColors, GBool inlineImg)
2318 dbg("drawImage %dx%d, %s, %s, inline=%d", width, height,
2319 colorMap?"colorMap":"no colorMap",
2320 maskColors?"maskColors":"no maskColors",
2322 msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
2323 colorMap?"colorMap":"no colorMap",
2324 maskColors?"maskColors":"no maskColors",
2327 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2328 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2329 drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2332 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2333 int width, int height,
2334 GfxImageColorMap *colorMap,
2335 Stream *maskStr, int maskWidth, int maskHeight,
2338 dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2339 colorMap?"colorMap":"no colorMap",
2340 maskWidth, maskHeight);
2341 msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
2342 colorMap?"colorMap":"no colorMap",
2343 maskWidth, maskHeight);
2345 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2346 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2347 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2350 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2351 int width, int height,
2352 GfxImageColorMap *colorMap,
2354 int maskWidth, int maskHeight,
2355 GfxImageColorMap *maskColorMap)
2357 dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2358 colorMap?"colorMap":"no colorMap",
2359 maskWidth, maskHeight);
2360 msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
2361 colorMap?"colorMap":"no colorMap",
2362 maskWidth, maskHeight);
2364 msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2365 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2366 drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2369 void GFXOutputDev::stroke(GfxState *state)
2373 GfxPath * path = state->getPath();
2374 gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2375 strokeGfxline(state, line, 0);
2379 void GFXOutputDev::fill(GfxState *state)
2381 gfxcolor_t col = getFillColor(state);
2382 dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2384 GfxPath * path = state->getPath();
2385 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2386 if(config_optimize_polygons) {
2387 gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2391 fillGfxLine(state, line);
2395 void GFXOutputDev::eoFill(GfxState *state)
2397 gfxcolor_t col = getFillColor(state);
2398 dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2400 GfxPath * path = state->getPath();
2401 gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2402 fillGfxLine(state, line);
2407 static const char* dirseparator()
2416 void addGlobalFont(const char*filename)
2418 fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2419 memset(f, 0, sizeof(fontfile_t));
2420 f->filename = filename;
2421 int len = strlen(filename);
2422 char*r1 = strrchr(filename, '/');
2423 char*r2 = strrchr(filename, '\\');
2431 msg("<notice> Adding font \"%s\".", filename);
2432 if(global_fonts_next) {
2433 global_fonts_next->next = f;
2434 global_fonts_next = global_fonts_next->next;
2436 global_fonts_next = global_fonts = f;
2440 void addGlobalLanguageDir(const char*dir)
2442 msg("<notice> Adding %s to language pack directories", dir);
2445 char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2446 strcpy(config_file, dir);
2447 strcat(config_file, dirseparator());
2448 strcat(config_file, "add-to-xpdfrc");
2450 fi = fopen(config_file, "rb");
2452 msg("<error> Could not open %s", config_file);
2455 globalParams->parseFile(new GString(config_file), fi);
2459 void addGlobalFontDir(const char*dirname)
2461 #ifdef HAVE_DIRENT_H
2462 msg("<notice> Adding %s to font directories", dirname);
2463 lastfontdir = strdup(dirname);
2464 DIR*dir = opendir(dirname);
2466 msg("<warning> Couldn't open directory %s", dirname);
2471 ent = readdir (dir);
2475 char*name = ent->d_name;
2481 if(!strncasecmp(&name[l-4], ".pfa", 4))
2483 if(!strncasecmp(&name[l-4], ".pfb", 4))
2485 if(!strncasecmp(&name[l-4], ".ttf", 4))
2488 char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2489 strcpy(fontname, dirname);
2490 strcat(fontname, dirseparator());
2491 strcat(fontname, name);
2492 addGlobalFont(fontname);
2497 msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2501 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2507 this->pagebuflen = 1024;
2508 this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2509 memset(this->pages, -1, this->pagebuflen*sizeof(int));
2511 while(pdfpage >= this->pagebuflen)
2513 int oldlen = this->pagebuflen;
2514 this->pagebuflen+=1024;
2515 this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2516 memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2519 this->pages[pdfpage] = outputpage;
2520 if(pdfpage>this->pagepos)
2521 this->pagepos = pdfpage;
2524 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2525 GfxColorSpace *blendingColorSpace,
2526 GBool isolated, GBool knockout,
2529 const char*colormodename = "";
2531 if(blendingColorSpace) {
2532 colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2534 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);
2535 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);
2537 //states[statepos].createsoftmask |= forSoftMask;
2538 states[statepos].createsoftmask = forSoftMask;
2539 states[statepos].transparencygroup = !forSoftMask;
2540 states[statepos].isolated = isolated;
2542 states[statepos].olddevice = this->device;
2543 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2545 gfxdevice_record_init(this->device);
2547 /*if(!forSoftMask) { ////???
2548 state->setFillOpacity(0.0);
2553 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2556 dbg("endTransparencyGroup");
2557 msg("<verbose> endTransparencyGroup");
2559 gfxdevice_t*r = this->device;
2561 this->device = states[statepos].olddevice;
2563 gfxresult_t*recording = r->finish(r);
2564 if(states[statepos].createsoftmask) {
2565 states[statepos-1].softmaskrecording = recording;
2567 states[statepos-1].grouprecording = recording;
2570 states[statepos].createsoftmask = 0;
2571 states[statepos].transparencygroup = 0;
2575 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2577 const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2578 "colordodge","colorburn","hardlight","softlight","difference",
2579 "exclusion","hue","saturation","color","luminosity"};
2581 dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2582 msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2584 if(state->getBlendMode() == gfxBlendNormal)
2585 infofeature("transparency groups");
2588 sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2589 warnfeature(buffer, 0);
2592 gfxresult_t*grouprecording = states[statepos].grouprecording;
2594 int blendmode = state->getBlendMode();
2595 if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2596 int alpha = (int)(state->getFillOpacity()*255);
2597 if(blendmode == gfxBlendMultiply && alpha>200)
2600 gfxdevice_ops_init(&ops, this->device, alpha);
2601 gfxresult_record_replay(grouprecording, &ops);
2604 grouprecording->destroy(grouprecording);
2606 states[statepos].grouprecording = 0;
2609 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2611 /* alpha = 1: retrieve mask values from alpha layer
2612 alpha = 0: retrieve mask values from luminance */
2613 dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2614 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2615 msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2616 bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2618 infofeature("soft masks");
2620 warnfeature("soft masks from alpha channel",0);
2622 states[statepos].olddevice = this->device;
2623 this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2624 gfxdevice_record_init(this->device);
2626 dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2628 states[statepos].softmask = 1;
2629 states[statepos].softmask_alpha = alpha;
2632 static inline Guchar div255(int x) {
2633 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2636 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2638 if(c < min) c = min;
2639 if(c > max) c = max;
2643 void GFXOutputDev::clearSoftMask(GfxState *state)
2645 if(!states[statepos].softmask)
2647 states[statepos].softmask = 0;
2648 dbg("clearSoftMask statepos=%d", statepos);
2649 msg("<verbose> clearSoftMask");
2651 if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2652 msg("<error> Error in softmask/tgroup ordering");
2656 gfxresult_t*mask = states[statepos].softmaskrecording;
2657 gfxresult_t*below = this->device->finish(this->device);
2658 this->device = states[statepos].olddevice;
2660 /* get outline of all objects below the soft mask */
2661 gfxdevice_t uniondev;
2662 gfxdevice_union_init(&uniondev, 0);
2663 gfxresult_record_replay(below, &uniondev);
2664 gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2665 uniondev.finish(&uniondev);
2667 gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2669 this->device->startclip(this->device, belowoutline);
2670 gfxresult_record_replay(below, this->device);
2671 gfxresult_record_replay(mask, this->device);
2672 this->device->endclip(this->device);
2673 gfxline_free(belowoutline);
2676 int width = (int)bbox.xmax,height = (int)bbox.ymax;
2677 if(width<=0 || height<=0)
2680 gfxdevice_t belowrender;
2681 gfxdevice_render_init(&belowrender);
2682 if(states[statepos+1].isolated) {
2683 belowrender.setparameter(&belowrender, "fillwhite", "1");
2685 belowrender.setparameter(&belowrender, "antialize", "2");
2686 belowrender.startpage(&belowrender, width, height);
2687 gfxresult_record_replay(below, &belowrender);
2688 belowrender.endpage(&belowrender);
2689 gfxresult_t* belowresult = belowrender.finish(&belowrender);
2690 gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2691 //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2693 gfxdevice_t maskrender;
2694 gfxdevice_render_init(&maskrender);
2695 maskrender.startpage(&maskrender, width, height);
2696 gfxresult_record_replay(mask, &maskrender);
2697 maskrender.endpage(&maskrender);
2698 gfxresult_t* maskresult = maskrender.finish(&maskrender);
2699 gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2701 if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2702 msg("<fatal> Internal error in mask drawing");
2707 for(y=0;y<height;y++) {
2708 gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2709 gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2710 for(x=0;x<width;x++) {
2712 if(states[statepos].softmask_alpha) {
2715 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2718 l2->a = div255(alpha*l2->a);
2720 /* DON'T premultiply alpha- this is done by fillbitmap,
2721 depending on the output device */
2722 //l2->r = div255(alpha*l2->r);
2723 //l2->g = div255(alpha*l2->g);
2724 //l2->b = div255(alpha*l2->b);
2730 gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2733 matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2734 matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2736 this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2738 mask->destroy(mask);
2739 below->destroy(below);
2740 maskresult->destroy(maskresult);
2741 belowresult->destroy(belowresult);
2742 states[statepos].softmaskrecording = 0;
2747 // public: ~MemCheck()
2749 // delete globalParams;globalParams=0;
2750 // Object::memCheck(stderr);
2751 // gMemReport(stderr);