render multiply blended transparency groups with 50% transparency
[swftools.git] / lib / pdf / GFXOutputDev.cc
1 /* GFXOutputDev.cc
2    implements a pdf output device (OutputDev).
3
4    This file is part of swftools.
5
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.
10
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.
15
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 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <stddef.h>
24 #include <string.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #include "../../config.h"
29 #include "../os.h"
30 #ifdef HAVE_DIRENT_H
31 #include <dirent.h>
32 #endif
33 #ifdef HAVE_SYS_STAT_H
34 #include <sys/stat.h>
35 #endif
36 //xpdf header files
37 #include "config.h"
38 #include "gfile.h"
39 #include "GString.h"
40 #include "gmem.h"
41 #include "Object.h"
42 #include "Stream.h"
43 #include "Array.h"
44 #include "Dict.h"
45 #include "XRef.h"
46 #include "Catalog.h"
47 #include "Page.h"
48 #include "PDFDoc.h"
49 #include "Error.h"
50 #include "Link.h"
51 #include "OutputDev.h"
52 #include "GfxFont.h"
53 #include "GfxState.h"
54 #include "CharCodeToUnicode.h"
55 #include "NameToUnicodeTable.h"
56 #include "GlobalParams.h"
57 #include "FoFiType1C.h"
58 #include "FoFiTrueType.h"
59 #include "GHash.h"
60 #include "GFXOutputDev.h"
61
62 //  swftools header files
63 #include "../log.h"
64 #include "../gfxdevice.h"
65 #include "../gfxtools.h"
66 #include "../gfxfont.h"
67 #include "../gfxpoly.h"
68 #include "../devices/record.h"
69 #include "../devices/ops.h"
70 #include "../devices/polyops.h"
71 #include "../devices/render.h"
72
73 #include "../art/libart.h"
74
75 #include "../png.h"
76 #include "fonts.h"
77
78 #include <math.h>
79
80 typedef struct _fontfile
81 {
82     const char*filename;
83     int len; // basename length
84     int used;
85     struct _fontfile*next;
86 } fontfile_t;
87
88 // for pdfswf_addfont
89
90 static fontfile_t* global_fonts = 0;
91 static fontfile_t* global_fonts_next = 0;
92
93 static int fontnum = 0;
94
95 /* config */
96
97 static char* lastfontdir = 0;
98
99 struct fontentry {
100     const char*pdffont;
101     const char*filename;
102     char*afm;
103     int afmlen;
104     char*pfb;
105     int pfblen;
106     char*fullfilename;
107 } pdf2t1map[] ={
108 {"Times-Roman",           "n021003l", n021003l_afm, n021003l_afm_len, n021003l_pfb, n021003l_pfb_len},
109 {"Times-Italic",          "n021023l", n021023l_afm, n021023l_afm_len, n021023l_pfb, n021023l_pfb_len},
110 {"Times-Bold",            "n021004l", n021004l_afm, n021004l_afm_len, n021004l_pfb, n021004l_pfb_len},
111 {"Times-BoldItalic",      "n021024l", n021024l_afm, n021024l_afm_len, n021024l_pfb, n021024l_pfb_len},
112 {"Helvetica",             "n019003l", n019003l_afm, n019003l_afm_len, n019003l_pfb, n019003l_pfb_len},
113 {"Helvetica-Oblique",     "n019023l", n019023l_afm, n019023l_afm_len, n019023l_pfb, n019023l_pfb_len},
114 {"Helvetica-Bold",        "n019004l", n019004l_afm, n019004l_afm_len, n019004l_pfb, n019004l_pfb_len},
115 {"Helvetica-BoldOblique", "n019024l", n019024l_afm, n019024l_afm_len, n019024l_pfb, n019024l_pfb_len},
116 {"Courier",               "n022003l", n022003l_afm, n022003l_afm_len, n022003l_pfb, n022003l_pfb_len},
117 {"Courier-Oblique",       "n022023l", n022023l_afm, n022023l_afm_len, n022023l_pfb, n022023l_pfb_len},
118 {"Courier-Bold",          "n022004l", n022004l_afm, n022004l_afm_len, n022004l_pfb, n022004l_pfb_len},
119 {"Courier-BoldOblique",   "n022024l", n022024l_afm, n022024l_afm_len, n022024l_pfb, n022024l_pfb_len},
120 {"Symbol",                "s050000l", s050000l_afm, s050000l_afm_len, s050000l_pfb, s050000l_pfb_len},
121 {"ZapfDingbats",          "d050000l", d050000l_afm, d050000l_afm_len, d050000l_pfb, d050000l_pfb_len}};
122
123
124 static int verbose = 0;
125 static int dbgindent = 0;
126 static void dbg(const char*format, ...)
127 {
128     char buf[1024];
129     int l;
130     va_list arglist;
131     if(!verbose)
132         return;
133     va_start(arglist, format);
134     vsprintf(buf, format, arglist);
135     va_end(arglist);
136     l = strlen(buf);
137     while(l && buf[l-1]=='\n') {
138         buf[l-1] = 0;
139         l--;
140     }
141     printf("(pdf) ");
142     int indent = dbgindent;
143     while(indent) {
144         printf(" ");
145         indent--;
146     }
147     printf("%s\n", buf);
148     fflush(stdout);
149 }
150
151
152 typedef struct _feature
153 {
154     char*string;
155     struct _feature*next;
156 } feature_t;
157 feature_t*featurewarnings = 0;
158
159 void GFXOutputDev::showfeature(const char*feature, char fully, char warn)
160 {
161     feature_t*f = featurewarnings;
162     while(f) {
163         if(!strcmp(feature, f->string))
164             return;
165         f = f->next;
166     }
167     f = (feature_t*)malloc(sizeof(feature_t));
168     f->string = strdup(feature);
169     f->next = featurewarnings;
170     featurewarnings = f;
171     if(warn) {
172         msg("<warning> %s not yet %ssupported!",feature,fully?"fully ":"");
173         if(this->config_break_on_warning) {
174             msg("<fatal> Aborting conversion due to unsupported feature");
175             exit(1);
176         }
177     } else {
178         msg("<notice> File contains %s",feature);
179     }
180 }
181 void GFXOutputDev::warnfeature(const char*feature,char fully)
182 {
183     showfeature(feature,fully,1);
184 }
185 void GFXOutputDev::infofeature(const char*feature)
186 {
187     showfeature(feature,0,0);
188 }
189
190 GFXOutputState::GFXOutputState() {
191     this->clipping = 0;
192     this->createsoftmask = 0;
193     this->transparencygroup = 0;
194     this->softmask = 0;
195     this->grouprecording = 0;
196     this->isolated = 0;
197 }
198
199 GBool GFXOutputDev::interpretType3Chars() 
200 {
201     return gTrue;
202 }
203
204 typedef struct _drawnchar
205 {
206     gfxcoord_t x,y;
207     int charid;
208     gfxcolor_t color;
209 } drawnchar_t;
210
211 class CharBuffer
212 {
213     drawnchar_t * chars;
214     int buf_size;
215     int num_chars;
216
217 public:
218
219     CharBuffer()
220     {
221         buf_size = 32;
222         chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
223         memset(chars, 0, sizeof(drawnchar_t)*buf_size);
224         num_chars = 0;
225     }
226     ~CharBuffer()
227     {
228         free(chars);chars = 0;
229     }
230
231     void grow(int size)
232     {
233         if(size>=buf_size) {
234             buf_size += 32;
235             chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
236         }
237     }
238
239     void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
240     {
241         grow(num_chars);
242         chars[num_chars].x = x;
243         chars[num_chars].y = y;
244         chars[num_chars].color = color;
245         chars[num_chars].charid = charid;
246     }
247 };
248     
249 char* writeOutStdFont(fontentry* f)
250 {
251     FILE*fi;
252     char namebuf1[512];
253     char namebuf2[512];
254     char* tmpFileName = mktmpname(namebuf1);
255
256     sprintf(namebuf2, "%s.afm", tmpFileName);
257     fi = fopen(namebuf2, "wb");
258     if(!fi)
259         return 0;
260     fwrite(f->afm, 1, f->afmlen, fi);
261     fclose(fi);
262
263     sprintf(namebuf2, "%s.pfb", tmpFileName);
264     fi = fopen(namebuf2, "wb");
265     if(!fi)
266         return 0;
267     fwrite(f->pfb, 1, f->pfblen, fi);
268     fclose(fi);
269     return strdup(namebuf2);
270 }
271 void unlinkfont(char* filename)
272 {
273     int l;
274     if(!filename)
275         return;
276     msg("<verbose> Removing temporary font file %s", filename);
277     l=strlen(filename);
278     unlink(filename);
279     if(!strncmp(&filename[l-4],".afm",4)) {
280         memcpy(&filename[l-4],".pfb",4); unlink(filename);
281         memcpy(&filename[l-4],".pfa",4); unlink(filename);
282         memcpy(&filename[l-4],".afm",4);
283         return;
284     } else 
285     if(!strncmp(&filename[l-4],".pfa",4)) {
286         memcpy(&filename[l-4],".afm",4); unlink(filename);
287         memcpy(&filename[l-4],".pfa",4);
288         return;
289     } else 
290     if(!strncmp(&filename[l-4],".pfb",4)) {
291         memcpy(&filename[l-4],".afm",4); unlink(filename);
292         memcpy(&filename[l-4],".pfb",4);
293         return;
294     }
295 }
296
297
298 GFXGlobalParams::GFXGlobalParams()
299 : GlobalParams("")
300 {
301     //setupBaseFonts(char *dir); //not tested yet
302 }
303 GFXGlobalParams::~GFXGlobalParams()
304 {
305     msg("<verbose> Performing cleanups");
306     int t;
307     for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
308         if(pdf2t1map[t].fullfilename) {
309             unlinkfont(pdf2t1map[t].fullfilename);
310         }
311     }
312 }
313 DisplayFontParam *GFXGlobalParams::getDisplayFont(GString *fontName)
314 {
315     msg("<verbose> looking for font %s in global params", fontName->getCString());
316
317     char*name = fontName->getCString();
318     /* see if it is a pdf standard font */
319     int t;
320     for(t=0;t<sizeof(pdf2t1map)/sizeof(fontentry);t++) {
321         if(!strcmp(name, pdf2t1map[t].pdffont)) {
322             if(!pdf2t1map[t].fullfilename) {
323                 pdf2t1map[t].fullfilename = writeOutStdFont(&pdf2t1map[t]);
324                 if(!pdf2t1map[t].fullfilename) {
325                     msg("<error> Couldn't save default font- is the Temp Directory writable?");
326                 } else {
327                     msg("<verbose> Storing standard PDF font %s at %s", name, pdf2t1map[t].fullfilename);
328                 }
329             }
330             DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
331             dfp->t1.fileName = new GString(pdf2t1map[t].fullfilename);
332             return dfp;
333         }
334     }
335     
336     int bestlen = 0x7fffffff;
337     const char*bestfilename = 0;
338     
339     fontfile_t*f = 0;
340     while(f) {
341         if(strstr(f->filename, name)) {
342             if(f->len < bestlen) {
343                 bestlen = f->len;
344                 bestfilename = f->filename;
345             }
346         }
347         f = f->next;
348     }
349     if(bestfilename) {
350         DisplayFontParam *dfp = new DisplayFontParam(new GString(fontName), displayFontT1);
351         dfp->t1.fileName = new GString(bestfilename);
352         return dfp;
353     }
354     return GlobalParams::getDisplayFont(fontName);
355 }
356
357 GFXOutputDev::GFXOutputDev(InfoOutputDev*info, PDFDoc*doc)
358 {
359     this->info = info;
360     this->doc = doc;
361     this->xref = doc->getXRef();
362
363     this->jpeginfo = 0;
364     this->textmodeinfo = 0;
365     this->linkinfo = 0;
366     this->pbminfo = 0;
367     this->type3active = 0;
368     this->statepos = 0;
369     this->xref = 0;
370     this->substitutepos = 0;
371     this->type3Warning = 0;
372     this->user_movex = 0;
373     this->user_movey = 0;
374     this->clipmovex = 0;
375     this->clipmovey = 0;
376     this->user_clipx1 = 0;
377     this->user_clipy1 = 0;
378     this->user_clipx2 = 0;
379     this->user_clipy2 = 0;
380     this->current_text_stroke = 0;
381     this->current_text_clip = 0;
382     this->outer_clip_box = 0;
383     this->pages = 0;
384     this->pagebuflen = 0;
385     this->pagepos = 0;
386     this->config_convertgradients=0;
387     this->config_break_on_warning=0;
388     this->config_remapunicode=0;
389     this->config_transparent=0;
390     this->config_extrafontdata = 0;
391     this->config_fontquality = 10;
392     this->config_optimize_polygons = 0;
393
394     this->gfxfontlist = gfxfontlist_create();
395   
396     memset(states, 0, sizeof(states));
397 };
398
399 void GFXOutputDev::setParameter(const char*key, const char*value)
400 {
401     if(!strcmp(key,"breakonwarning")) {
402         this->config_break_on_warning = atoi(value);
403     } else if(!strcmp(key,"remapunicode")) {
404         this->config_remapunicode = atoi(value);
405     } else if(!strcmp(key,"transparent")) {
406         this->config_transparent = atoi(value);
407     } else if(!strcmp(key,"extrafontdata")) {
408         this->config_extrafontdata = atoi(value);
409     } else if(!strcmp(key,"convertgradients")) {
410         this->config_convertgradients = atoi(value);
411     } else if(!strcmp(key,"optimize_polygons")) {
412         this->config_optimize_polygons = atoi(value);
413     } else if(!strcmp(key,"fontquality")) {
414         this->config_fontquality = atof(value);
415         if(this->config_fontquality<=1)
416             this->config_fontquality=1;
417     } else if(!strcmp(key,"help")) {
418         printf("\nPDF layer options:\n");
419         printf("breakonwarning=0/1  Abort conversion if graphic objects are found which\n");
420         printf("                    are not 100%% supported\n");
421         printf("transparent=0/1     Make PDF transparent (alpha background)\n");
422         printf("extrafontdata=0/1   Store Type3 characters and capture characters\n");
423         printf("fontquality=1..100  Curve approximation quality of the fonts\n");
424     }
425     
426 }
427   
428 void GFXOutputDev::setDevice(gfxdevice_t*dev)
429 {
430     this->device = dev;
431 }
432   
433 void GFXOutputDev::setMove(int x,int y)
434 {
435     this->user_movex = x;
436     this->user_movey = y;
437 }
438
439 void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
440 {
441     if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
442     if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
443
444     this->user_clipx1 = x1;
445     this->user_clipy1 = y1;
446     this->user_clipx2 = x2;
447     this->user_clipy2 = y2;
448 }
449
450 static char*getFontName(GfxFont*font)
451 {
452     char*fontid;
453     GString*gstr = font->getName();
454     char* fname = gstr==0?0:gstr->getCString();
455     if(fname==0) {
456         char buf[32];
457         Ref*r=font->getID();
458         sprintf(buf, "UFONT%d", r->num);
459         fontid = strdup(buf);
460     } else {
461         fontid = strdup(fname);
462     }
463
464     char*fontname= 0;
465     char* plus = strchr(fontid, '+');
466     if(plus && plus < &fontid[strlen(fontid)-1]) {
467         fontname = strdup(plus+1);
468     } else {
469         fontname = strdup(fontid);
470     }
471     free(fontid);
472     return fontname;
473 }
474
475 static void dumpFontInfo(const char*loglevel, GfxFont*font);
476 static int lastdumps[1024];
477 static int lastdumppos = 0;
478 /* nr = 0  unknown
479    nr = 1  substituting
480    nr = 2  type 3
481  */
482 static void showFontError(GfxFont*font, int nr) 
483 {  
484     Ref*r=font->getID();
485     int t;
486     for(t=0;t<lastdumppos;t++)
487         if(lastdumps[t] == r->num)
488             break;
489     if(t < lastdumppos)
490       return;
491     if(lastdumppos<sizeof(lastdumps)/sizeof(int))
492     lastdumps[lastdumppos++] = r->num;
493     if(nr == 0)
494       msg("<warning> The following font caused problems:");
495     else if(nr == 1)
496       msg("<warning> The following font caused problems (substituting):");
497     else if(nr == 2)
498       msg("<warning> The following Type 3 Font will be rendered as graphics:");
499     dumpFontInfo("<warning>", font);
500 }
501
502 static void dumpFontInfo(const char*loglevel, GfxFont*font)
503 {
504   char* id = getFontID(font);
505   char* name = getFontName(font);
506   Ref* r=font->getID();
507   msg("%s=========== %s (ID:%d,%d) ==========", loglevel, name, r->num,r->gen);
508
509   GString*gstr  = font->getTag();
510    
511   msg("%s| Tag: %s", loglevel, id);
512   
513   if(font->isCIDFont()) msg("%s| is CID font", loglevel);
514
515   GfxFontType type=font->getType();
516   switch(type) {
517     case fontUnknownType:
518      msg("%s| Type: unknown",loglevel);
519     break;
520     case fontType1:
521      msg("%s| Type: 1",loglevel);
522     break;
523     case fontType1C:
524      msg("%s| Type: 1C",loglevel);
525     break;
526     case fontType3:
527      msg("%s| Type: 3",loglevel);
528     break;
529     case fontTrueType:
530      msg("%s| Type: TrueType",loglevel);
531     break;
532     case fontCIDType0:
533      msg("%s| Type: CIDType0",loglevel);
534     break;
535     case fontCIDType0C:
536      msg("%s| Type: CIDType0C",loglevel);
537     break;
538     case fontCIDType2:
539      msg("%s| Type: CIDType2",loglevel);
540     break;
541   }
542   
543   Ref embRef;
544   GBool embedded = font->getEmbeddedFontID(&embRef);
545   char*embeddedName=0;
546   if(font->getEmbeddedFontName()) {
547     embeddedName = font->getEmbeddedFontName()->getCString();
548   }
549   if(embedded)
550    msg("%s| Embedded id: %s id: %d",loglevel, FIXNULL(embeddedName), embRef.num);
551
552   gstr = font->getExtFontFile();
553   if(gstr)
554    msg("%s| External Font file: %s", loglevel, FIXNULL(gstr->getCString()));
555
556   // Get font descriptor flags.
557   if(font->isFixedWidth()) msg("%s| is fixed width", loglevel);
558   if(font->isSerif()) msg("%s| is serif", loglevel);
559   if(font->isSymbolic()) msg("%s| is symbolic", loglevel);
560   if(font->isItalic()) msg("%s| is italic", loglevel);
561   if(font->isBold()) msg("%s| is bold", loglevel);
562
563   free(id);
564   free(name);
565 }
566
567 //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");}
568 //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");}
569
570 void dump_outline(gfxline_t*line)
571 {
572     while(line) {
573         if(line->type == gfx_moveTo) {
574             msg("<debug> |     moveTo %.2f %.2f", line->x,line->y);
575         } else if(line->type == gfx_lineTo) {
576             msg("<debug> |     lineTo %.2f %.2f", line->x,line->y);
577         } else if(line->type == gfx_splineTo) {
578             msg("<debug> |     splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
579         }
580         line = line->next;
581     }
582 }
583
584 gfxline_t* GFXOutputDev::gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
585 {
586     int num = path->getNumSubpaths();
587     int s,t;
588     int cpos = 0;
589     double lastx=0,lasty=0,posx=0,posy=0;
590     int needsfix=0;
591     if(!num) {
592         msg("<warning> empty path");
593         return 0;
594     }
595     gfxdrawer_t draw;
596     gfxdrawer_target_gfxline(&draw);
597
598     for(t = 0; t < num; t++) {
599         GfxSubpath *subpath = path->getSubpath(t);
600         int subnum = subpath->getNumPoints();
601         double bx=0,by=0,cx=0,cy=0;
602
603         for(s=0;s<subnum;s++) {
604            double x,y;
605            
606            this->transformXY(state, subpath->getX(s),subpath->getY(s),&x,&y);
607
608            if(s==0) {
609                 if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
610                     draw.lineTo(&draw, lastx, lasty);
611                 }
612                 draw.moveTo(&draw, x,y);
613                 posx = lastx = x; 
614                 posy = lasty = y;
615                 cpos = 0;
616                 needsfix = 0;
617            } else if(subpath->getCurve(s) && cpos==0) {
618                 bx = x;
619                 by = y;
620                 cpos = 1;
621            } else if(subpath->getCurve(s) && cpos==1) {
622                 cx = x;
623                 cy = y;
624                 cpos = 2;
625            } else {
626                 posx = x;
627                 posy = y;
628                 if(cpos==0) {
629                     draw.lineTo(&draw, x,y);
630                 } else {
631                     gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
632                 }
633                 needsfix = 1;
634                 cpos = 0;
635            }
636         }
637     }
638     /* fix non-closed lines */
639     if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
640         draw.lineTo(&draw, lastx, lasty);
641     }
642     gfxline_t*result = (gfxline_t*)draw.result(&draw);
643
644     gfxline_optimize(result);
645
646     return result;
647 }
648
649 GBool GFXOutputDev::useTilingPatternFill()
650 {
651     infofeature("tiled patterns");
652 //    if(config_convertgradients)
653 //      return gTrue;
654     return gFalse;
655 }
656 GBool GFXOutputDev::useShadedFills()
657 {
658     infofeature("shaded fills");
659     if(config_convertgradients)
660         return gTrue;
661     return gFalse;
662 }
663
664 void GFXOutputDev::transformXY(GfxState*state, double x, double y, double*nx, double*ny)
665 {
666     state->transform(x,y,nx,ny);
667     *nx += user_movex + clipmovex;
668     *ny += user_movey + clipmovey;
669 }
670
671
672 #if (xpdfMajorVersion*10000 + xpdfMinorVersion*100 + xpdfUpdateVersion) < 30207
673 void GFXOutputDev::tilingPatternFill(GfxState *state, Object *str,
674                                int paintType, Dict *resDict,
675                                double *mat, double *bbox,
676                                int x0, int y0, int x1, int y1,
677                                double xStep, double yStep)
678 #else
679 void GFXBitmapOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *str,
680                                int paintType, Dict *resDict,
681                                double *mat, double *bbox,
682                                int x0, int y0, int x1, int y1,
683                                double xStep, double yStep) 
684 #endif
685 {
686     msg("<debug> tilingPatternFill");
687     infofeature("tiling pattern fills");
688 }
689
690 GBool GFXOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) 
691 {
692     msg("<error> functionShadedFill not supported yet");
693     infofeature("function shaded fills");
694     return gFalse;
695 }
696 static gfxcolor_t col2col(GfxColorSpace*colspace, GfxColor* col)
697 {
698     gfxcolor_t c;
699     GfxRGB rgb;
700     colspace->getRGB(col, &rgb);
701     c.r = colToByte(rgb.r);
702     c.g = colToByte(rgb.g);
703     c.b = colToByte(rgb.b);
704     c.a = 255;
705     return c;
706 }
707
708 GBool GFXOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading)
709 {
710     double x0,y0,r0,x1,y1,x2,y2,x9,y9,r1;
711     shading->getCoords(&x0,&y0,&r0,&x9,&y9,&r1);
712     x1=x0+r1;y1=y0;
713     x2=x0;   y2=y0+r1;
714     this->transformXY(state, x0,y0, &x0,&y0);
715     this->transformXY(state, x1,y1, &x1,&y1);
716     this->transformXY(state, x2,y2, &x2,&y2);
717     
718     GfxColor color0;
719     GfxColor color1;
720     GfxColor color2;
721     shading->getColor(0.0, &color0);
722     shading->getColor(0.5, &color1);
723     shading->getColor(1.0, &color2);
724   
725     GfxColorSpace* colspace = shading->getColorSpace();
726     
727     msg("<verbose> radialShadedFill %f %f %f %f %f %f %02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1, x2, y2,
728             colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]), 
729             colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
730             colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2]));
731     infofeature("radial shaded fills");
732
733     gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
734     g[0].next = &g[1];
735     g[1].next = &g[2];
736     g[2].next = 0;
737     g[0].color = col2col(colspace, &color0);
738     g[1].color = col2col(colspace, &color1);
739     g[2].color = col2col(colspace, &color2);
740     g[0].pos = 0.0;
741     g[1].pos = 0.5;
742     g[2].pos = 1.0;
743
744     gfxbbox_t b = states[statepos].clipbbox;
745     gfxline_t p1,p2,p3,p4,p5;
746     p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
747     p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
748     p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
749     p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
750     p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
751     
752     gfxmatrix_t m;
753     //m.m00 = (x3-x0); m.m10 = (x1-x0);
754     //m.m01 = (y3-y0); m.m11 = (y1-y0);
755     //x3/y3 specifies another (the ending) circle, not the second radius of an ellipse
756     m.m00 = (x1-x0); m.m10 = (x2-x0);
757     m.m01 = (y1-y0); m.m11 = (y2-y0);
758     m.tx = x0 - 0.5;
759     m.ty = y0 - 0.5;
760
761     device->fillgradient(device, &p1, g, gfxgradient_radial, &m);
762     return gTrue;
763 }
764
765 GBool GFXOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading)
766 {
767     double x0,y0,x1,y1;
768     shading->getCoords(&x0,&y0,&x1,&y1);
769     this->transformXY(state, x0,y0,&x0,&y0);
770     this->transformXY(state, x1,y1,&x1,&y1);
771
772     GfxColor color0;
773     GfxColor color1;
774     GfxColor color2;
775     shading->getColor(0.0, &color0);
776     shading->getColor(0.5, &color1);
777     shading->getColor(1.0, &color2);
778     
779     GfxColorSpace* colspace = shading->getColorSpace();
780     
781     msg("<verbose> axialShadedFill %f %f %f %f %02x%02x%02x->%02x%02x%02x->%02x%02x%02x", x0, y0, x1, y1,
782             colToByte(color0.c[0]), colToByte(color0.c[1]), colToByte(color0.c[2]), 
783             colToByte(color1.c[0]), colToByte(color1.c[1]), colToByte(color1.c[2]),
784             colToByte(color2.c[0]), colToByte(color2.c[1]), colToByte(color2.c[2])
785             );
786     infofeature("axial shaded fills");
787
788     gfxgradient_t*g = (gfxgradient_t*)malloc(sizeof(gfxgradient_t)*3);
789     g[0].next = &g[1];
790     g[1].next = &g[2];
791     g[2].next = 0;
792     g[0].color = col2col(colspace, &color0);
793     g[1].color = col2col(colspace, &color1);
794     g[2].color = col2col(colspace, &color2);
795     g[0].pos = 0.0;
796     g[1].pos = 0.5;
797     g[2].pos = 1.0;
798  
799     double xMin,yMin,xMax,yMax;
800     state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
801     this->transformXY(state, xMin, yMin, &xMin, &yMin);
802     msg("<verbose> userClipBox %f %f %f %f", xMin, yMin, xMax, yMax);
803
804     xMin = 0; yMin = 0;
805     xMin = 1024; yMin = 1024;
806
807     gfxbbox_t b = states[statepos].clipbbox;
808     gfxline_t p1,p2,p3,p4,p5;
809     p1.type=gfx_moveTo;p1.x=b.xmin; p1.y=b.ymin; p1.next=&p2;
810     p2.type=gfx_lineTo;p2.x=b.xmin; p2.y=b.ymax; p2.next=&p3;
811     p3.type=gfx_lineTo;p3.x=b.xmax; p3.y=b.ymax; p3.next=&p4;
812     p4.type=gfx_lineTo;p4.x=b.xmax; p4.y=b.ymin; p4.next=&p5;
813     p5.type=gfx_lineTo;p5.x=b.xmin; p5.y=b.ymin; p5.next=0;
814    
815     /* the gradient starts at (-1.0,0.0), so move (0,0) to
816        the middle of the two control points */
817     gfxmatrix_t m;
818     m.m00 = (x1-x0)/2; m.m10 = -(y1-y0)/2;
819     m.m01 = (y1-y0)/2; m.m11 =  (x1-x0)/2;
820     m.tx = (x0 + x1)/2 - 0.5;
821     m.ty = (y0 + y1)/2 - 0.5;
822
823     device->fillgradient(device, &p1, g, gfxgradient_linear, &m);
824     return gTrue;
825 }
826   
827 GBool GFXOutputDev::useDrawForm() 
828
829     infofeature("forms");
830     return gFalse; 
831 }
832 void GFXOutputDev::drawForm(Ref id) 
833 {
834     msg("<error> drawForm not implemented");
835 }
836 GBool GFXOutputDev::needNonText() 
837
838     return gTrue; 
839 }
840 void GFXOutputDev::endPage() 
841 {
842     msg("<verbose> endPage (GfxOutputDev)");
843     if(outer_clip_box) {
844         device->endclip(device);
845         outer_clip_box = 0;
846     }
847 }
848
849 #define STROKE_FILL 1
850 #define STROKE_CLIP 2
851 void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line, int flags)
852 {
853     int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
854     int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
855     double miterLimit = state->getMiterLimit();
856     double width = state->getTransformedLineWidth();
857
858     GfxRGB rgb;
859     double opaq = state->getStrokeOpacity();
860     if(type3active)
861         state->getFillRGB(&rgb);
862     else
863         state->getStrokeRGB(&rgb);
864     gfxcolor_t col;
865     col.r = colToByte(rgb.r);
866     col.g = colToByte(rgb.g);
867     col.b = colToByte(rgb.b);
868     col.a = (unsigned char)(opaq*255);
869    
870     gfx_capType capType = gfx_capRound;
871     if(lineCap == 0) capType = gfx_capButt;
872     else if(lineCap == 1) capType = gfx_capRound;
873     else if(lineCap == 2) capType = gfx_capSquare;
874
875     gfx_joinType joinType = gfx_joinRound;
876     if(lineJoin == 0) joinType = gfx_joinMiter;
877     else if(lineJoin == 1) joinType = gfx_joinRound;
878     else if(lineJoin == 2) joinType = gfx_joinBevel;
879
880     int dashnum = 0;
881     double dashphase = 0;
882     double * ldash = 0;
883     state->getLineDash(&ldash, &dashnum, &dashphase);
884
885     gfxline_t*line2 = 0;
886
887     if(dashnum && ldash) {
888         float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
889         int t;
890         msg("<trace> %d dashes", dashnum);
891         msg("<trace> |  phase: %f", dashphase);
892         for(t=0;t<dashnum;t++) {
893             dash[t] = (float)ldash[t];
894             msg("<trace> |  d%-3d: %f", t, ldash[t]);
895         }
896         dash[dashnum] = -1;
897         if(getLogLevel() >= LOGLEVEL_TRACE) {
898             dump_outline(line);
899         }
900
901         line2 = gfxtool_dash_line(line, dash, (float)dashphase);
902         line = line2;
903         free(dash);
904         msg("<trace> After dashing:");
905     }
906     
907     if(getLogLevel() >= LOGLEVEL_TRACE)  {
908         msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x",
909                 width,
910                 lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
911                 lineCap==0?"butt": (lineJoin==1?"round":"square"),
912                 dashnum,
913                 col.r,col.g,col.b,col.a
914                 );
915         dump_outline(line);
916     }
917
918     if(flags&STROKE_FILL) {
919         gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, capType, joinType, miterLimit);
920         gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
921         if(getLogLevel() >= LOGLEVEL_TRACE)  {
922             dump_outline(gfxline);
923         }
924         if(!gfxline) {
925             msg("<warning> Empty polygon (resulting from stroked line)");
926         }
927         if(flags&STROKE_CLIP) {
928             device->startclip(device, gfxline);
929             states[statepos].clipping++;
930         } else {
931             device->fill(device, gfxline, &col);
932         }
933         free(gfxline);
934         gfxpoly_free(poly);
935     } else {
936         if(flags&STROKE_CLIP) 
937             msg("<error> Stroke&clip not supported at the same time");
938         device->stroke(device, line, width, &col, capType, joinType, miterLimit);
939     }
940     
941     if(line2)
942         gfxline_free(line2);
943 }
944
945 gfxcolor_t getFillColor(GfxState * state)
946 {
947     GfxRGB rgb;
948     double opaq = state->getFillOpacity();
949     state->getFillRGB(&rgb);
950     gfxcolor_t col;
951     col.r = colToByte(rgb.r);
952     col.g = colToByte(rgb.g);
953     col.b = colToByte(rgb.b);
954     col.a = (unsigned char)(opaq*255);
955     return col;
956 }
957
958 void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line) 
959 {
960     gfxcolor_t col = getFillColor(state);
961
962     if(getLogLevel() >= LOGLEVEL_TRACE)  {
963         msg("<trace> fill %02x%02x%02x%02x", col.r, col.g, col.b, col.a);
964         dump_outline(line);
965     }
966     device->fill(device, line, &col);
967 }
968
969 void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line) 
970 {
971     if(getLogLevel() >= LOGLEVEL_TRACE)  {
972         dump_outline(line);
973     }
974     gfxbbox_t bbox = gfxline_getbbox(line);
975     gfxbbox_intersect(&states[statepos].clipbbox, &bbox);
976
977     device->startclip(device, line);
978     states[statepos].clipping++;
979 }
980
981 void GFXOutputDev::clip(GfxState *state) 
982 {
983     GfxPath * path = state->getPath();
984     msg("<trace> clip");
985     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
986     if(config_optimize_polygons) {
987         gfxline_t*line2 = gfxline_circularToEvenOdd(line);
988         gfxline_free(line);
989         line = line2;
990     }
991     clipToGfxLine(state, line);
992     gfxline_free(line);
993 }
994
995 void GFXOutputDev::eoClip(GfxState *state) 
996 {
997     GfxPath * path = state->getPath();
998     gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
999     clipToGfxLine(state, line);
1000     gfxline_free(line);
1001 }
1002 void GFXOutputDev::clipToStrokePath(GfxState *state)
1003 {
1004     GfxPath * path = state->getPath();
1005     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1006
1007     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1008         msg("<trace> cliptostrokepath");
1009         dump_outline(line);
1010     }
1011
1012     strokeGfxline(state, line, STROKE_FILL|STROKE_CLIP);
1013     gfxline_free(line);
1014 }
1015
1016 void GFXOutputDev::finish()
1017 {
1018     if(outer_clip_box) {
1019         if(device) {
1020             device->endclip(device);
1021         }
1022         outer_clip_box = 0;
1023     }
1024 }
1025
1026 GFXOutputDev::~GFXOutputDev() 
1027 {
1028     finish();
1029
1030     if(this->pages) {
1031         free(this->pages); this->pages = 0;
1032     }
1033
1034     gfxfontlist_free(this->gfxfontlist, 1);this->gfxfontlist = 0;
1035 };
1036 GBool GFXOutputDev::upsideDown() 
1037 {
1038     return gTrue;
1039 };
1040 GBool GFXOutputDev::useDrawChar() 
1041 {
1042     return gTrue;
1043 }
1044
1045 const char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
1046                       "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
1047
1048 static char tmp_printstr[4096];
1049 char* makeStringPrintable(char*str)
1050 {
1051     int len = strlen(str);
1052     int dots = 0;
1053     if(len>=80) {
1054         len = 80;
1055         dots = 1;
1056     }
1057     int t;
1058     for(t=0;t<len;t++) {
1059         char c = str[t];
1060         if(c<32 || c>124) {
1061             c = '.';
1062         }
1063         tmp_printstr[t] = c;
1064     }
1065     if(dots) {
1066         tmp_printstr[len++] = '.';
1067         tmp_printstr[len++] = '.';
1068         tmp_printstr[len++] = '.';
1069     }
1070     tmp_printstr[len] = 0;
1071     return tmp_printstr;
1072 }
1073 #define INTERNAL_FONT_SIZE 1024.0
1074 void GFXOutputDev::updateFontMatrix(GfxState*state)
1075 {
1076     double* ctm = state->getCTM();
1077     double fontSize = state->getFontSize();
1078     double*textMat = state->getTextMat();
1079
1080     /*  taking the absolute value of horizScaling seems to be required for
1081         some italic fonts. FIXME: SplashOutputDev doesn't need this- why? */
1082     double hscale = fabs(state->getHorizScaling());
1083    
1084     // from xpdf-3.02/SplashOutputDev:updateFont
1085     double mm11 = textMat[0] * fontSize * hscale;
1086     double mm12 = textMat[1] * fontSize * hscale;
1087     double mm21 = textMat[2] * fontSize;
1088     double mm22 = textMat[3] * fontSize;
1089
1090     // multiply with ctm, like state->getFontTransMat() does
1091     this->current_font_matrix.m00 = (ctm[0]*mm11 + ctm[2]*mm12) / INTERNAL_FONT_SIZE;
1092     this->current_font_matrix.m01 = (ctm[1]*mm11 + ctm[3]*mm12) / INTERNAL_FONT_SIZE;
1093     this->current_font_matrix.m10 = (ctm[0]*mm21 + ctm[2]*mm22) / INTERNAL_FONT_SIZE;
1094     this->current_font_matrix.m11 = (ctm[1]*mm21 + ctm[3]*mm22) / INTERNAL_FONT_SIZE;
1095     this->current_font_matrix.tx = 0;
1096     this->current_font_matrix.ty = 0;
1097 }
1098
1099 void GFXOutputDev::beginString(GfxState *state, GString *s) 
1100
1101     int render = state->getRender();
1102     if(current_text_stroke) {
1103         msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
1104     }
1105
1106     msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
1107 }
1108
1109 static gfxline_t* mkEmptyGfxShape(double x, double y)
1110 {
1111     gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
1112     line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
1113     return line;
1114 }
1115
1116 static char isValidUnicode(int c)
1117 {
1118     if(c>=32 && c<0x2fffe)
1119         return 1;
1120     return 0;
1121 }
1122
1123 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
1124                         double dx, double dy,
1125                         double originX, double originY,
1126                         CharCode charid, int nBytes, Unicode *_u, int uLen)
1127 {
1128     if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1129         msg("<error> Invalid charid %d for font (%d characters)", charid, current_fontinfo?current_fontinfo->num_glyphs:0);
1130         return;
1131     }
1132   
1133     CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1134
1135     int render = state->getRender();
1136     gfxcolor_t col = getFillColor(state);
1137
1138     // check for invisible text -- this is used by Acrobat Capture
1139     if (render == RENDER_INVISIBLE) {
1140         col.a = 0;
1141         if(!config_extrafontdata)
1142             return;
1143     }
1144
1145     GfxFont*font = state->getFont();
1146
1147     if(font->getType() == fontType3) {
1148         /* type 3 chars are passed as graphics */
1149         msg("<debug> type3 char at %f/%f", x, y);
1150         return;
1151     }
1152
1153     Unicode u = uLen?(_u[0]):0;
1154     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);
1155
1156     gfxmatrix_t m = this->current_font_matrix;
1157     this->transformXY(state, x, y, &m.tx, &m.ty);
1158
1159     if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
1160         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1161     } else {
1162         msg("<debug> Drawing glyph %d as shape", charid);
1163         if(!textmodeinfo) {
1164             msg("<notice> Some texts will be rendered as shape");
1165             textmodeinfo = 1;
1166         }
1167         gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
1168         gfxline_t*tglyph = gfxline_clone(glyph);
1169         gfxline_transform(tglyph, &m);
1170         if((render&3) != RENDER_INVISIBLE) {
1171             gfxline_t*add = gfxline_clone(tglyph);
1172             current_text_stroke = gfxline_append(current_text_stroke, add);
1173         }
1174         if(render&RENDER_CLIP) {
1175             gfxline_t*add = gfxline_clone(tglyph);
1176             current_text_clip = gfxline_append(current_text_clip, add);
1177             if(!current_text_clip) {
1178                 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
1179             }
1180         }
1181         gfxline_free(tglyph);
1182     }
1183 }
1184
1185 void GFXOutputDev::endString(GfxState *state) 
1186
1187     int render = state->getRender();
1188     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
1189     
1190     if(current_text_stroke) {
1191         /* fillstroke and stroke text rendering objects we can process right
1192            now (as there may be texts of other rendering modes in this
1193            text object)- clipping objects have to wait until endTextObject,
1194            however */
1195         device->setparameter(device, "mark","TXT");
1196         if((render&3) == RENDER_FILL) {
1197             fillGfxLine(state, current_text_stroke);
1198             gfxline_free(current_text_stroke);
1199             current_text_stroke = 0;
1200         } else if((render&3) == RENDER_FILLSTROKE) {
1201             fillGfxLine(state, current_text_stroke);
1202             strokeGfxline(state, current_text_stroke,0);
1203             gfxline_free(current_text_stroke);
1204             current_text_stroke = 0;
1205         } else if((render&3) == RENDER_STROKE) {
1206             strokeGfxline(state, current_text_stroke,0);
1207             gfxline_free(current_text_stroke);
1208             current_text_stroke = 0;
1209         }
1210         device->setparameter(device, "mark","");
1211     }
1212 }    
1213
1214 void GFXOutputDev::endTextObject(GfxState *state)
1215 {
1216     int render = state->getRender();
1217     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1218     
1219     if(current_text_clip) {
1220         device->setparameter(device, "mark","TXT");
1221         clipToGfxLine(state, current_text_clip);
1222         device->setparameter(device, "mark","");
1223         gfxline_free(current_text_clip);
1224         current_text_clip = 0;
1225     }
1226 }
1227
1228 /* the logic seems to be as following:
1229    first, beginType3Char is called, with the charcode and the coordinates.
1230    if this function returns true, it already knew about the char and has now drawn it.
1231    if the function returns false, it's a new char, and type3D0 and/or type3D1 might be 
1232    called with some parameters.
1233    Afterwards, all draw operations until endType3Char are part of the char (which in this moment is
1234    at the position first passed to beginType3Char). the char ends with endType3Char.
1235
1236    The drawing operations between beginType3Char and endType3Char are somewhat different to
1237    the normal ones. For example, the fillcolor equals the stroke color. (Because the stroke
1238    color determines the color of a font)
1239 */
1240
1241 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode charid, Unicode *u, int uLen)
1242 {
1243     msg("<debug> beginType3Char %d u=%d", charid, uLen?u[0]:0);
1244     type3active = 1;
1245     
1246     if(config_extrafontdata && current_fontinfo) {
1247
1248         gfxmatrix_t m = this->current_font_matrix;
1249         this->transformXY(state, 0, 0, &m.tx, &m.ty);
1250         m.m00*=INTERNAL_FONT_SIZE;
1251         m.m01*=INTERNAL_FONT_SIZE;
1252         m.m10*=INTERNAL_FONT_SIZE;
1253         m.m11*=INTERNAL_FONT_SIZE;
1254
1255         if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
1256             msg("<error> Invalid charid %d for font", charid);
1257             return gFalse;
1258         }
1259         gfxcolor_t col={0,0,0,0};
1260         CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
1261         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
1262     }
1263
1264
1265     /* the character itself is going to be passed using the draw functions */
1266     return gFalse; /* gTrue= is_in_cache? */
1267 }
1268
1269 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1270 }
1271 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1272 }
1273
1274 void GFXOutputDev::endType3Char(GfxState *state)
1275 {
1276     type3active = 0;
1277     msg("<debug> endType3Char");
1278 }
1279
1280 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
1281 {
1282     this->currentpage = pageNum;
1283     double x1,y1,x2,y2;
1284     int rot = doc->getPageRotate(1);
1285     gfxcolor_t white = {255,255,255,255};
1286     gfxcolor_t black = {255,0,0,0};
1287     laststate = state;
1288     gfxline_t clippath[5];
1289
1290     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1291     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1292     Use CropBox, not MediaBox, as page size
1293     */
1294     
1295     /*x1 = crop_x1;
1296     y1 = crop_y1;
1297     x2 = crop_x2;
1298     y2 = crop_y2;*/
1299     state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1300     state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1301
1302     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1303     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1304
1305     this->clipmovex = -(int)x1;
1306     this->clipmovey = -(int)y1;
1307     
1308     /* apply user clip box */
1309     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1310         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1311         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1312         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1313         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1314         msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1315     }
1316
1317     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1318     
1319     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);
1320     if(rot!=0)
1321         msg("<verbose> page is rotated %d degrees", rot);
1322
1323     clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1324     clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1325     clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1326     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1327     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1328     device->startclip(device, clippath); outer_clip_box = 1;
1329     if(!config_transparent) {
1330         device->fill(device, clippath, &white);
1331     }
1332     states[statepos].clipbbox.xmin = x1;
1333     states[statepos].clipbbox.ymin = x1;
1334     states[statepos].clipbbox.xmax = x2;
1335     states[statepos].clipbbox.ymax = y2;
1336 }
1337
1338
1339 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1340 {
1341     double x1, y1, x2, y2;
1342     gfxline_t points[5];
1343     int x, y;
1344     
1345     msg("<debug> drawlink");
1346
1347     link->getRect(&x1, &y1, &x2, &y2);
1348     cvtUserToDev(x1, y1, &x, &y);
1349     points[0].type = gfx_moveTo;
1350     points[0].x = points[4].x = x + user_movex + clipmovex;
1351     points[0].y = points[4].y = y + user_movey + clipmovey;
1352     points[0].next = &points[1];
1353     cvtUserToDev(x2, y1, &x, &y);
1354     points[1].type = gfx_lineTo;
1355     points[1].x = x + user_movex + clipmovex;
1356     points[1].y = y + user_movey + clipmovey;
1357     points[1].next = &points[2];
1358     cvtUserToDev(x2, y2, &x, &y);
1359     points[2].type = gfx_lineTo;
1360     points[2].x = x + user_movex + clipmovex;
1361     points[2].y = y + user_movey + clipmovey;
1362     points[2].next = &points[3];
1363     cvtUserToDev(x1, y2, &x, &y);
1364     points[3].type = gfx_lineTo;
1365     points[3].x = x + user_movex + clipmovex;
1366     points[3].y = y + user_movey + clipmovey;
1367     points[3].next = &points[4];
1368     cvtUserToDev(x1, y1, &x, &y);
1369     points[4].type = gfx_lineTo;
1370     points[4].x = x + user_movex + clipmovex;
1371     points[4].y = y + user_movey + clipmovey;
1372     points[4].next = 0;
1373     
1374     msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f",
1375             points[0].x, points[0].y,
1376             points[1].x, points[1].y,
1377             points[2].x, points[2].y,
1378             points[3].x, points[3].y); 
1379     
1380     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1381         dump_outline(points);
1382     }
1383
1384     LinkAction*action=link->getAction();
1385     char buf[128];
1386     char*s = 0;
1387     const char*type = "-?-";
1388     char*named = 0;
1389     int page = -1;
1390     msg("<trace> drawlink action=%d", action->getKind());
1391     switch(action->getKind())
1392     {
1393         case actionGoTo: {
1394             type = "GoTo";
1395             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1396             LinkDest *dest=NULL;
1397             if (ha->getDest()==NULL) 
1398                 dest=catalog->findDest(ha->getNamedDest());
1399             else dest=ha->getDest();
1400             if (dest){ 
1401               if (dest->isPageRef()){
1402                 Ref pageref=dest->getPageRef();
1403                 page=catalog->findPage(pageref.num,pageref.gen);
1404               }
1405               else  page=dest->getPageNum();
1406               sprintf(buf, "%d", page);
1407               s = strdup(buf);
1408             }
1409         }
1410         break;
1411         case actionGoToR: {
1412             type = "GoToR";
1413             LinkGoToR*l = (LinkGoToR*)action;
1414             GString*g = l->getFileName();
1415             if(g)
1416              s = strdup(g->getCString());
1417             if(!s) {
1418                 /* if the GoToR link has no filename, then
1419                    try to find a refernce in the *local*
1420                    file */
1421                 GString*g = l->getNamedDest();
1422                 if(g)
1423                  s = strdup(g->getCString());
1424             }
1425         }
1426         break;
1427         case actionNamed: {
1428             type = "Named";
1429             LinkNamed*l = (LinkNamed*)action;
1430             GString*name = l->getName();
1431             if(name) {
1432                 s = strdup(name->lowerCase()->getCString());
1433                 named = name->getCString();
1434                 if(!strchr(s,':')) 
1435                 {
1436                     if(strstr(s, "next") || strstr(s, "forward"))
1437                     {
1438                         page = currentpage + 1;
1439                     }
1440                     else if(strstr(s, "prev") || strstr(s, "back"))
1441                     {
1442                         page = currentpage - 1;
1443                     }
1444                     else if(strstr(s, "last") || strstr(s, "end"))
1445                     {
1446                         if(pages && pagepos>0)
1447                             page = pages[pagepos-1];
1448                     }
1449                     else if(strstr(s, "first") || strstr(s, "top"))
1450                     {
1451                         page = 1;
1452                     }
1453                 }
1454             }
1455         }
1456         break;
1457         case actionLaunch: {
1458             type = "Launch";
1459             LinkLaunch*l = (LinkLaunch*)action;
1460             GString * str = new GString(l->getFileName());
1461             GString * params = l->getParams();
1462             if(params)
1463                 str->append(params);
1464             s = strdup(str->getCString());
1465             delete str;
1466         }
1467         break;
1468         case actionURI: {
1469             char*url = 0;
1470             type = "URI";
1471             LinkURI*l = (LinkURI*)action;
1472             GString*g = l->getURI();
1473             if(g) {
1474              url = g->getCString();
1475              s = strdup(url);
1476             }
1477         }
1478         break;
1479         case actionUnknown: {
1480             type = "Unknown";
1481             LinkUnknown*l = (LinkUnknown*)action;
1482             s = strdup("");
1483         }
1484         break;
1485         default: {
1486             msg("<error> Unknown link type!");
1487             break;
1488         }
1489     }
1490
1491     if(!s) s = strdup("-?-");
1492     
1493     msg("<trace> drawlink s=%s", s);
1494
1495     if(!linkinfo && (page || s))
1496     {
1497         msg("<notice> File contains links");
1498         linkinfo = 1;
1499     }
1500     
1501     if(page>0)
1502     {
1503         int t;
1504         int lpage = -1;
1505         for(t=1;t<=pagepos;t++) {
1506             if(pages[t]==page) {
1507                 lpage = t;
1508                 break;
1509             }
1510         }
1511         if(lpage<0) {
1512             lpage = page;
1513         }
1514         char buf[80];
1515         sprintf(buf, "page%d", lpage);
1516         device->drawlink(device, points, buf);
1517     }
1518     else if(s)
1519     {
1520         device->drawlink(device, points, s);
1521     }
1522
1523     msg("<verbose> \"%s\" link to \"%s\" (%d)", type, FIXNULL(s), page);
1524     free(s);s=0;
1525 }
1526
1527 void GFXOutputDev::saveState(GfxState *state) {
1528     dbg("saveState");dbgindent+=2;
1529
1530     msg("<trace> saveState");
1531     updateAll(state);
1532     if(statepos>=64) {
1533       msg("<error> Too many nested states in pdf.");
1534       return;
1535     }
1536     statepos ++;
1537     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1538     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1539     states[statepos].clipping = 0;
1540     states[statepos].clipbbox = states[statepos-1].clipbbox;
1541 };
1542
1543 void GFXOutputDev::restoreState(GfxState *state) {
1544   dbgindent-=2; dbg("restoreState");
1545
1546   if(statepos==0) {
1547       msg("<error> Invalid restoreState");
1548       return;
1549   }
1550   msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1551                                   states[statepos].clipping?" (end clipping)":"");
1552   if(states[statepos].softmask) {
1553       clearSoftMask(state);
1554   }
1555   updateAll(state);
1556   while(states[statepos].clipping) {
1557       device->endclip(device);
1558       states[statepos].clipping--;
1559   }
1560   statepos--;
1561 }
1562
1563 void GFXOutputDev::updateLineWidth(GfxState *state)
1564 {
1565     double width = state->getTransformedLineWidth();
1566     //swfoutput_setlinewidth(&device, width);
1567 }
1568
1569 void GFXOutputDev::updateLineCap(GfxState *state)
1570 {
1571     int c = state->getLineCap();
1572 }
1573
1574 void GFXOutputDev::updateLineJoin(GfxState *state)
1575 {
1576     int j = state->getLineJoin();
1577 }
1578
1579 void GFXOutputDev::updateFillColor(GfxState *state) 
1580 {
1581     GfxRGB rgb;
1582     double opaq = state->getFillOpacity();
1583     state->getFillRGB(&rgb);
1584 }
1585 void GFXOutputDev::updateFillOpacity(GfxState *state)
1586 {
1587     GfxRGB rgb;
1588     double opaq = state->getFillOpacity();
1589     state->getFillRGB(&rgb);
1590     dbg("update fillopaq %f", opaq);
1591 }
1592 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1593 {
1594     double opaq = state->getFillOpacity();
1595     dbg("update strokeopaq %f", opaq);
1596 }
1597 void GFXOutputDev::updateFillOverprint(GfxState *state)
1598 {
1599     double opaq = state->getFillOverprint();
1600     dbg("update filloverprint %f", opaq);
1601 }
1602 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1603 {
1604     double opaq = state->getStrokeOverprint();
1605     dbg("update strokeoverprint %f", opaq);
1606 }
1607 void GFXOutputDev::updateTransfer(GfxState *state)
1608 {
1609     dbg("update transfer");
1610 }
1611
1612
1613 void GFXOutputDev::updateStrokeColor(GfxState *state) 
1614 {
1615     GfxRGB rgb;
1616     double opaq = state->getStrokeOpacity();
1617     state->getStrokeRGB(&rgb);
1618 }
1619
1620
1621 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1622 {
1623     gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1624     memset(font, 0, sizeof(gfxfont_t));
1625
1626     font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1627     memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1628     font->id = strdup(getFontID(xpdffont));
1629     int t;
1630     
1631     double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1632     double scale = 1;
1633     //printf("%d glyphs\n", font->num_glyphs);
1634     font->num_glyphs = 0;
1635     for(t=0;t<src->num_glyphs;t++) {
1636         if(src->glyphs[t]) {
1637             SplashPath*path = src->glyphs[t]->path;
1638             int len = path?path->getLength():0;
1639             //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1640             gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1641             src->glyphs[t]->glyphid = font->num_glyphs;
1642             glyph->unicode = src->glyphs[t]->unicode;
1643             if(glyph->unicode >= font->max_unicode)
1644                 font->max_unicode = glyph->unicode+1;
1645             gfxdrawer_t drawer;
1646             gfxdrawer_target_gfxline(&drawer);
1647             int s;
1648             int count = 0;
1649             double xmax = 0;
1650             for(s=0;s<len;s++) {
1651                 Guchar f;
1652                 double x, y;
1653                 path->getPoint(s, &x, &y, &f);
1654                 if(!s || x > xmax)
1655                     xmax = x;
1656                 if(f&splashPathFirst) {
1657                     drawer.moveTo(&drawer, x*scale, y*scale);
1658                 }
1659                 if(f&splashPathCurve) {
1660                     double x2,y2;
1661                     path->getPoint(++s, &x2, &y2, &f);
1662                     if(f&splashPathCurve) {
1663                         double x3,y3;
1664                         path->getPoint(++s, &x3, &y3, &f);
1665                         gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1666                     } else {
1667                         drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1668                     }
1669                 } else {
1670                     drawer.lineTo(&drawer, x*scale, y*scale);
1671                 }
1672              //   printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1673              //                           (f&splashPathFirst)?"first":"",
1674              //                           (f&splashPathLast)?"last":"");
1675             }
1676             glyph->line = (gfxline_t*)drawer.result(&drawer);
1677             glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1678             font->num_glyphs++;
1679         }
1680     }
1681     font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1682     memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1683     for(t=0;t<font->num_glyphs;t++) {
1684         if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1685             font->unicode2glyph[font->glyphs[t].unicode] = t;
1686         }
1687
1688     }
1689     msg("<trace> %d glyphs.", t, font->num_glyphs);
1690     return font;
1691 }
1692
1693 void GFXOutputDev::updateFont(GfxState *state) 
1694 {
1695     GfxFont* gfxFont = state->getFont();
1696     if (!gfxFont) {
1697         return; 
1698     }  
1699     char*id = getFontID(gfxFont);
1700     msg("<verbose> Updating font to %s", id);
1701     if(gfxFont->getType() == fontType3) {
1702         infofeature("Type3 fonts");
1703         if(!config_extrafontdata) {
1704             return;
1705         }
1706     }
1707     if(!id) {
1708         msg("<error> Internal Error: FontID is null");
1709         return; 
1710     }
1711
1712     this->current_fontinfo = this->info->getFont(id);
1713     if(!this->current_fontinfo) {
1714         msg("<error> Internal Error: no fontinfo for font %s", id);
1715         return;
1716     }
1717     if(!this->current_fontinfo->seen) {
1718         dumpFontInfo("<verbose>", gfxFont);
1719     }
1720     
1721     gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1722     if(!font) {
1723         font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1724         font->id = strdup(id);
1725         this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1726     }
1727     device->addfont(device, font);
1728
1729     current_gfxfont = font;
1730     free(id);
1731
1732     updateFontMatrix(state);
1733 }
1734
1735 #define SQR(x) ((x)*(x))
1736
1737 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1738 {
1739     if((newwidth<2 || newheight<2) ||
1740        (width<=newwidth || height<=newheight))
1741         return 0;
1742     unsigned char*newdata;
1743     int x,y;
1744     newdata= (unsigned char*)malloc(newwidth*newheight);
1745     double fx = (double)(width)/newwidth;
1746     double fy = (double)(height)/newheight;
1747     double px = 0;
1748     int blocksize = (int)(8192/(fx*fy));
1749     int r = 8192*256/palettesize;
1750     for(x=0;x<newwidth;x++) {
1751         double ex = px + fx;
1752         int fromx = (int)px;
1753         int tox = (int)ex;
1754         int xweight1 = (int)(((fromx+1)-px)*256);
1755         int xweight2 = (int)((ex-tox)*256);
1756         double py =0;
1757         for(y=0;y<newheight;y++) {
1758             double ey = py + fy;
1759             int fromy = (int)py;
1760             int toy = (int)ey;
1761             int yweight1 = (int)(((fromy+1)-py)*256);
1762             int yweight2 = (int)((ey-toy)*256);
1763             int a = 0;
1764             int xx,yy;
1765             for(xx=fromx;xx<=tox;xx++)
1766             for(yy=fromy;yy<=toy;yy++) {
1767                 int b = 1-data[width*yy+xx];
1768                 int weight=256;
1769                 if(xx==fromx) weight = (weight*xweight1)/256;
1770                 if(xx==tox) weight = (weight*xweight2)/256;
1771                 if(yy==fromy) weight = (weight*yweight1)/256;
1772                 if(yy==toy) weight = (weight*yweight2)/256;
1773                 a+=b*weight;
1774             }
1775             //if(a) a=(palettesize-1)*r/blocksize;
1776             newdata[y*newwidth+x] = (a*blocksize)/r;
1777             py = ey;
1778         }
1779         px = ex;
1780     }
1781     return newdata;
1782 }
1783
1784 #define IMAGE_TYPE_JPEG 0
1785 #define IMAGE_TYPE_LOSSLESS 1
1786
1787 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
1788         double x1,double y1,
1789         double x2,double y2,
1790         double x3,double y3,
1791         double x4,double y4, int type)
1792 {
1793     gfxcolor_t*newpic=0;
1794     
1795     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1796     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1797    
1798     gfxline_t p1,p2,p3,p4,p5;
1799     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1800     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1801     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1802     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1803     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1804
1805     {p1.x = (int)(p1.x*20)/20.0;
1806      p1.y = (int)(p1.y*20)/20.0;
1807      p2.x = (int)(p2.x*20)/20.0;
1808      p2.y = (int)(p2.y*20)/20.0;
1809      p3.x = (int)(p3.x*20)/20.0;
1810      p3.y = (int)(p3.y*20)/20.0;
1811      p4.x = (int)(p4.x*20)/20.0;
1812      p4.y = (int)(p4.y*20)/20.0;
1813      p5.x = (int)(p5.x*20)/20.0;
1814      p5.y = (int)(p5.y*20)/20.0;
1815     }
1816     
1817     gfxmatrix_t m;
1818     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1819     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1820     m.tx = p1.x - 0.5;
1821     m.ty = p1.y - 0.5;
1822
1823     gfximage_t img;
1824     img.data = (gfxcolor_t*)data;
1825     img.width = sizex;
1826     img.height = sizey;
1827   
1828     if(type == IMAGE_TYPE_JPEG)
1829         /* TODO: pass image_dpi to device instead */
1830         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1831
1832     dev->fillbitmap(dev, &p1, &img, &m, 0);
1833 }
1834
1835 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
1836         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1837 {
1838     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1839 }
1840
1841 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
1842         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1843 {
1844     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1845 }
1846
1847
1848 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1849                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
1850                                    GBool inlineImg, int mask, int*maskColors,
1851                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1852 {
1853   double x1,y1,x2,y2,x3,y3,x4,y4;
1854   ImageStream *imgStr;
1855   Guchar pixBuf[4];
1856   GfxRGB rgb;
1857   int ncomps = 1;
1858   int bits = 1;
1859   unsigned char* maskbitmap = 0;
1860                                  
1861   if(colorMap) {
1862     ncomps = colorMap->getNumPixelComps();
1863     bits = colorMap->getBits();
1864   }
1865       
1866   if(maskStr) {
1867       int x,y;
1868       unsigned char buf[8];
1869       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1870       if(maskColorMap) {
1871           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1872           imgMaskStr->reset();
1873           unsigned char pal[256];
1874           int n = 1 << colorMap->getBits();
1875           int t;
1876           for(t=0;t<n;t++) {
1877               GfxGray gray;
1878               pixBuf[0] = t;
1879               maskColorMap->getGray(pixBuf, &gray);
1880               pal[t] = colToByte(gray);
1881           }
1882           for (y = 0; y < maskHeight; y++) {
1883               for (x = 0; x < maskWidth; x++) {
1884                   imgMaskStr->getPixel(buf);
1885                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
1886               }
1887           }
1888           delete imgMaskStr;
1889       } else {
1890           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1891           imgMaskStr->reset();
1892           for (y = 0; y < maskHeight; y++) {
1893               for (x = 0; x < maskWidth; x++) {
1894                   imgMaskStr->getPixel(buf);
1895                   buf[0]^=maskInvert;
1896                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1897               }
1898           }
1899           delete imgMaskStr;
1900       }
1901       maskStr->close();
1902   }
1903   
1904   imgStr = new ImageStream(str, width, ncomps,bits);
1905   imgStr->reset();
1906
1907   if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
1908   {
1909       msg("<verbose> Ignoring %d by %d image", width, height);
1910       unsigned char buf[8];
1911       int x,y;
1912       for (y = 0; y < height; ++y)
1913       for (x = 0; x < width; ++x) {
1914           imgStr->getPixel(buf);
1915       }
1916       delete imgStr;
1917       if(maskbitmap)
1918           free(maskbitmap);
1919       return;
1920   }
1921
1922   this->transformXY(state, 0, 1, &x1, &y1);
1923   this->transformXY(state, 0, 0, &x2, &y2);
1924   this->transformXY(state, 1, 0, &x3, &y3);
1925   this->transformXY(state, 1, 1, &x4, &y4);
1926
1927   if(!pbminfo && !(str->getKind()==strDCT)) {
1928       if(!type3active) {
1929           msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1930           pbminfo = 1;
1931       }
1932       if(mask)
1933       msg("<verbose> drawing %d by %d masked picture", width, height);
1934   }
1935   if(!jpeginfo && (str->getKind()==strDCT)) {
1936       msg("<notice> file contains jpeg pictures");
1937       jpeginfo = 1;
1938   }
1939
1940   if(mask) {
1941       unsigned char buf[8];
1942       int x,y;
1943       unsigned char*pic = new unsigned char[width*height];
1944       gfxcolor_t pal[256];
1945       GfxRGB rgb;
1946       state->getFillRGB(&rgb);
1947
1948       memset(pal,255,sizeof(pal));
1949       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1950       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1951       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1952       pal[0].a = 255;              pal[1].a = 0;
1953     
1954       int numpalette = 2;
1955       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1956       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1957       for (y = 0; y < height; ++y)
1958       for (x = 0; x < width; ++x)
1959       {
1960             imgStr->getPixel(buf);
1961             if(invert) 
1962                 buf[0]=1-buf[0];
1963             pic[width*y+x] = buf[0];
1964       }
1965       
1966       /* the size of the drawn image is added to the identifier
1967          as the same image may require different bitmaps if displayed
1968          at different sizes (due to antialiasing): */
1969       int found = -1;
1970       if(type3active) {
1971           unsigned char*pic2 = 0;
1972           numpalette = 16;
1973           
1974           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1975
1976           if(!pic2) {
1977             delete[] pic;
1978             delete imgStr;
1979             return;
1980           }
1981
1982           width = realwidth;
1983           height = realheight;
1984           free(pic);
1985           pic = pic2;
1986           
1987           /* make a black/white palette */
1988
1989           float r = 255./(float)(numpalette-1);
1990           int t;
1991           for(t=0;t<numpalette;t++) {
1992               pal[t].r = colToByte(rgb.r);
1993               pal[t].g = colToByte(rgb.g);
1994               pal[t].b = colToByte(rgb.b);
1995               pal[t].a = (unsigned char)(t*r);
1996           }
1997       }
1998
1999       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
2000       for (y = 0; y < height; ++y) {
2001         for (x = 0; x < width; ++x) {
2002           pic2[width*y+x] = pal[pic[y*width+x]];
2003         }
2004       }
2005       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2006       free(pic2);
2007       free(pic);
2008       delete imgStr;
2009       if(maskbitmap) free(maskbitmap);
2010       return;
2011   }
2012
2013   int x,y;
2014
2015   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
2016       gfxcolor_t*pic=new gfxcolor_t[width*height];
2017       for (y = 0; y < height; ++y) {
2018         for (x = 0; x < width; ++x) {
2019           imgStr->getPixel(pixBuf);
2020           colorMap->getRGB(pixBuf, &rgb);
2021           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
2022           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
2023           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
2024           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
2025           if(maskbitmap) {
2026               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2027           }
2028         }
2029       }
2030       if(str->getKind()==strDCT)
2031           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2032       else
2033           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2034       delete[] pic;
2035       delete imgStr;
2036       if(maskbitmap) free(maskbitmap);
2037       return;
2038   } else {
2039       gfxcolor_t*pic=new gfxcolor_t[width*height];
2040       gfxcolor_t pal[256];
2041       int n = 1 << colorMap->getBits();
2042       int t;
2043       for(t=0;t<256;t++) {
2044           pixBuf[0] = t;
2045           colorMap->getRGB(pixBuf, &rgb);
2046
2047           {/*if(maskColors && *maskColors==t) {
2048               msg("<notice> Color %d is transparent", t);
2049               if (imgData->maskColors) {
2050                 *alpha = 0;
2051                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
2052                   if (pix[i] < imgData->maskColors[2*i] ||
2053                       pix[i] > imgData->maskColors[2*i+1]) {
2054                     *alpha = 1;
2055                     break;
2056                   }
2057                 }
2058               } else {
2059                 *alpha = 1;
2060               }
2061               if(!*alpha) {
2062                     pal[t].r = 0;
2063                     pal[t].g = 0;
2064                     pal[t].b = 0;
2065                     pal[t].a = 0;
2066               }
2067           } else {*/
2068               pal[t].r = (unsigned char)(colToByte(rgb.r));
2069               pal[t].g = (unsigned char)(colToByte(rgb.g));
2070               pal[t].b = (unsigned char)(colToByte(rgb.b));
2071               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
2072           }
2073       }
2074       for (y = 0; y < height; ++y) {
2075         for (x = 0; x < width; ++x) {
2076           imgStr->getPixel(pixBuf);
2077           pic[width*y+x] = pal[pixBuf[0]];
2078         }
2079       }
2080       if(maskbitmap) {
2081           if(maskWidth < width && maskHeight < height) {
2082               for(y = 0; y < height; y++) {
2083                   for (x = 0; x < width; x++) {
2084                       pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
2085                   }
2086               }
2087           } else {
2088               msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
2089               gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
2090               double dx = width / maskWidth;
2091               double dy = height / maskHeight;
2092               double yy = 0;
2093               for(y = 0; y < maskHeight; y++) {
2094                   double xx = 0;
2095                   for (x = 0; x < maskWidth; x++) {
2096                       newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
2097                       newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
2098                       xx += dx;
2099                   }
2100                   yy += dy;
2101               }
2102               delete[] pic;
2103               pic = newpic;
2104               width = maskWidth;
2105               height = maskHeight;
2106           }
2107       }
2108       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
2109
2110       delete[] pic;
2111       delete imgStr;
2112       if(maskbitmap) free(maskbitmap);
2113       return;
2114   }
2115 }
2116
2117 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2118                                    int width, int height, GBool invert,
2119                                    GBool inlineImg) 
2120 {
2121     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2122     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
2123     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
2124 }
2125
2126 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2127                          int width, int height, GfxImageColorMap *colorMap,
2128                          int *maskColors, GBool inlineImg)
2129 {
2130     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
2131             colorMap?"colorMap":"no colorMap", 
2132             maskColors?"maskColors":"no maskColors",
2133             inlineImg);
2134     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
2135             colorMap?"colorMap":"no colorMap", 
2136             maskColors?"maskColors":"no maskColors",
2137             inlineImg);
2138     if(colorMap)
2139         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2140                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2141     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
2142 }
2143   
2144 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
2145                                int width, int height,
2146                                GfxImageColorMap *colorMap,
2147                                Stream *maskStr, int maskWidth, int maskHeight,
2148                                GBool maskInvert)
2149 {
2150     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2151             colorMap?"colorMap":"no colorMap", 
2152             maskWidth, maskHeight);
2153     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2154             colorMap?"colorMap":"no colorMap", 
2155             maskWidth, maskHeight);
2156     if(colorMap)
2157         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2158                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2159     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
2160 }
2161
2162 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
2163                                    int width, int height,
2164                                    GfxImageColorMap *colorMap,
2165                                    Stream *maskStr,
2166                                    int maskWidth, int maskHeight,
2167                                    GfxImageColorMap *maskColorMap)
2168 {
2169     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2170             colorMap?"colorMap":"no colorMap", 
2171             maskWidth, maskHeight);
2172     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2173             colorMap?"colorMap":"no colorMap", 
2174             maskWidth, maskHeight);
2175     if(colorMap)
2176         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d", colorMap->getNumPixelComps(),
2177                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2178     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2179 }
2180
2181 void GFXOutputDev::stroke(GfxState *state) 
2182 {
2183     dbg("stroke");
2184
2185     GfxPath * path = state->getPath();
2186     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2187     strokeGfxline(state, line, 0);
2188     gfxline_free(line);
2189 }
2190
2191 void GFXOutputDev::fill(GfxState *state) 
2192 {
2193     gfxcolor_t col = getFillColor(state);
2194     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2195
2196     GfxPath * path = state->getPath();
2197     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2198     if(config_optimize_polygons) {
2199         gfxline_t*line2 = gfxline_circularToEvenOdd(line);
2200         gfxline_free(line);
2201         line = line2;
2202     }
2203     fillGfxLine(state, line);
2204     gfxline_free(line);
2205 }
2206
2207 void GFXOutputDev::eoFill(GfxState *state) 
2208 {
2209     gfxcolor_t col = getFillColor(state);
2210     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2211
2212     GfxPath * path = state->getPath();
2213     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2214     fillGfxLine(state, line);
2215     gfxline_free(line);
2216 }
2217
2218
2219 static const char* dirseparator()
2220 {
2221 #ifdef WIN32
2222     return "\\";
2223 #else
2224     return "/";
2225 #endif
2226 }
2227
2228 void addGlobalFont(const char*filename)
2229 {
2230     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2231     memset(f, 0, sizeof(fontfile_t));
2232     f->filename = filename;
2233     int len = strlen(filename);
2234     char*r1 = strrchr(filename, '/');
2235     char*r2 = strrchr(filename, '\\');
2236     if(r2>r1)
2237         r1 = r2;
2238     if(r1) {
2239         len = strlen(r1+1);
2240     }
2241     f->len = len;
2242
2243     msg("<notice> Adding font \"%s\".", filename);
2244     if(global_fonts_next) {
2245         global_fonts_next->next = f;
2246         global_fonts_next = global_fonts_next->next;
2247     } else {
2248         global_fonts_next = global_fonts = f;
2249     }
2250 }
2251
2252 void addGlobalLanguageDir(const char*dir)
2253 {
2254     msg("<notice> Adding %s to language pack directories", dir);
2255
2256     FILE*fi = 0;
2257     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2258     strcpy(config_file, dir);
2259     strcat(config_file, dirseparator());
2260     strcat(config_file, "add-to-xpdfrc");
2261
2262     fi = fopen(config_file, "rb");
2263     if(!fi) {
2264         msg("<error> Could not open %s", config_file);
2265         return;
2266     }
2267     globalParams->parseFile(new GString(config_file), fi);
2268     fclose(fi);
2269 }
2270
2271 void addGlobalFontDir(const char*dirname)
2272 {
2273 #ifdef HAVE_DIRENT_H
2274     msg("<notice> Adding %s to font directories", dirname);
2275     lastfontdir = strdup(dirname);
2276     DIR*dir = opendir(dirname);
2277     if(!dir) {
2278         msg("<warning> Couldn't open directory %s", dirname);
2279         return;
2280     }
2281     struct dirent*ent;
2282     while(1) {
2283         ent = readdir (dir);
2284         if (!ent) 
2285             break;
2286         int l;
2287         char*name = ent->d_name;
2288         char type = 0;
2289         if(!name) continue;
2290         l=strlen(name);
2291         if(l<4)
2292             continue;
2293         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2294             type=1;
2295         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2296             type=3;
2297         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2298             type=2;
2299         if(type) {
2300             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2301             strcpy(fontname, dirname);
2302             strcat(fontname, dirseparator());
2303             strcat(fontname, name);
2304             addGlobalFont(fontname);
2305         }
2306     }
2307     closedir(dir);
2308 #else
2309     msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2310 #endif
2311 }
2312
2313 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2314 {
2315     if(pdfpage < 0)
2316         return;
2317
2318     if(!this->pages) {
2319         this->pagebuflen = 1024;
2320         this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2321         memset(this->pages, -1, this->pagebuflen*sizeof(int));
2322     } else {
2323         while(pdfpage >= this->pagebuflen)
2324         {
2325             int oldlen = this->pagebuflen;
2326             this->pagebuflen+=1024;
2327             this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2328             memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2329         }
2330     }
2331     this->pages[pdfpage] = outputpage;
2332     if(pdfpage>this->pagepos)
2333         this->pagepos = pdfpage;
2334 }
2335
2336 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2337                                       GfxColorSpace *blendingColorSpace,
2338                                       GBool isolated, GBool knockout,
2339                                       GBool forSoftMask)
2340 {
2341     const char*colormodename = "";
2342
2343     if(blendingColorSpace) {
2344         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2345     }
2346     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);
2347     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);
2348     
2349     //states[statepos].createsoftmask |= forSoftMask;
2350     states[statepos].createsoftmask = forSoftMask;
2351     states[statepos].transparencygroup = !forSoftMask;
2352     states[statepos].isolated = isolated;
2353
2354     states[statepos].olddevice = this->device;
2355     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2356
2357     gfxdevice_record_init(this->device);
2358     
2359     /*if(!forSoftMask) { ////???
2360         state->setFillOpacity(0.0);
2361     }*/
2362     dbgindent+=2;
2363 }
2364
2365 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2366 {
2367     dbgindent-=2;
2368     dbg("endTransparencyGroup");
2369     msg("<verbose> endTransparencyGroup");
2370
2371     gfxdevice_t*r = this->device;
2372
2373     this->device = states[statepos].olddevice;
2374
2375     gfxresult_t*recording = r->finish(r);
2376     if(states[statepos].createsoftmask) {
2377         states[statepos-1].softmaskrecording = recording;
2378     } else {
2379         states[statepos-1].grouprecording = recording;
2380     }
2381     
2382     states[statepos].createsoftmask = 0;
2383     states[statepos].transparencygroup = 0;
2384     free(r);
2385 }
2386
2387 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2388 {
2389     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2390                                "colordodge","colorburn","hardlight","softlight","difference",
2391                                "exclusion","hue","saturation","color","luminosity"};
2392
2393     dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2394     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2395    
2396     if(state->getBlendMode() == gfxBlendNormal)
2397         infofeature("transparency groups");
2398     else {
2399         char buffer[80];
2400         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2401         warnfeature(buffer, 0);
2402     }
2403
2404     gfxresult_t*grouprecording = states[statepos].grouprecording;
2405   
2406     int blendmode = state->getBlendMode();
2407     if(blendmode == gfxBlendNormal || blendmode == gfxBlendMultiply) {
2408         int alpha = state->getFillOpacity()*255;
2409         if(blendmode == gfxBlendMultiply && alpha>200)
2410             alpha = 128;
2411         gfxdevice_t ops;
2412         gfxdevice_ops_init(&ops, this->device, alpha);
2413         gfxresult_record_replay(grouprecording, &ops);
2414         ops.finish(&ops);
2415     }
2416     grouprecording->destroy(grouprecording);
2417
2418     states[statepos].grouprecording = 0;
2419 }
2420
2421 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2422 {
2423     /* alpha = 1: retrieve mask values from alpha layer
2424        alpha = 0: retrieve mask values from luminance */
2425     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2426             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2427     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2428             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2429     if(!alpha)
2430         infofeature("soft masks");
2431     else
2432         warnfeature("soft masks from alpha channel",0);
2433     
2434     states[statepos].olddevice = this->device;
2435     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2436     gfxdevice_record_init(this->device);
2437
2438     dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2439     
2440     states[statepos].softmask = 1;
2441     states[statepos].softmask_alpha = alpha;
2442 }
2443
2444 static inline Guchar div255(int x) {
2445   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2446 }
2447
2448 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2449 {
2450     if(c < min) c = min;
2451     if(c > max) c = max;
2452     return c;
2453 }
2454
2455 void GFXOutputDev::clearSoftMask(GfxState *state)
2456 {
2457     if(!states[statepos].softmask)
2458         return;
2459     states[statepos].softmask = 0;
2460     dbg("clearSoftMask statepos=%d", statepos);
2461     msg("<verbose> clearSoftMask");
2462     
2463     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2464         msg("<error> Error in softmask/tgroup ordering");
2465         return;
2466     }
2467   
2468     gfxresult_t*mask = states[statepos].softmaskrecording;
2469     gfxresult_t*below = this->device->finish(this->device);
2470     this->device = states[statepos].olddevice;
2471
2472     /* get outline of all objects below the soft mask */
2473     gfxdevice_t uniondev;
2474     gfxdevice_union_init(&uniondev, 0);
2475     gfxresult_record_replay(below, &uniondev);
2476     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2477     uniondev.finish(&uniondev);
2478
2479     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2480 #if 0 
2481     this->device->startclip(this->device, belowoutline);
2482     gfxresult_record_replay(below, this->device);
2483     gfxresult_record_replay(mask, this->device);
2484     this->device->endclip(this->device);
2485     gfxline_free(belowoutline);
2486 #endif
2487     
2488     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2489     if(width<=0 || height<=0)
2490         return;
2491
2492     gfxdevice_t belowrender;
2493     gfxdevice_render_init(&belowrender);
2494     if(states[statepos+1].isolated) {
2495         belowrender.setparameter(&belowrender, "fillwhite", "1");
2496     }
2497     belowrender.setparameter(&belowrender, "antialize", "2");
2498     belowrender.startpage(&belowrender, width, height);
2499     gfxresult_record_replay(below, &belowrender);
2500     belowrender.endpage(&belowrender);
2501     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2502     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2503     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2504
2505     gfxdevice_t maskrender;
2506     gfxdevice_render_init(&maskrender);
2507     maskrender.startpage(&maskrender, width, height);
2508     gfxresult_record_replay(mask, &maskrender);
2509     maskrender.endpage(&maskrender);
2510     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2511     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2512
2513     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2514         msg("<fatal> Internal error in mask drawing");
2515         return;
2516     }
2517
2518     int y,x;
2519     for(y=0;y<height;y++) {
2520         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2521         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2522         for(x=0;x<width;x++) {
2523             int alpha;
2524             if(states[statepos].softmask_alpha) {
2525                 alpha = l1->a;
2526             } else {
2527                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2528             }
2529
2530             l2->a = div255(alpha*l2->a);
2531
2532             /* DON'T premultiply alpha- this is done by fillbitmap,
2533                depending on the output device */
2534             //l2->r = div255(alpha*l2->r);
2535             //l2->g = div255(alpha*l2->g);
2536             //l2->b = div255(alpha*l2->b);
2537
2538             l1++;
2539             l2++;
2540         }
2541     }
2542     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2543
2544     gfxmatrix_t matrix;
2545     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2546     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2547
2548     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2549
2550     mask->destroy(mask);
2551     below->destroy(below);
2552     maskresult->destroy(maskresult);
2553     belowresult->destroy(belowresult);
2554     states[statepos].softmaskrecording = 0;
2555 }
2556   
2557 //class MemCheck
2558 //{
2559 //    public: ~MemCheck()
2560 //    {
2561 //        delete globalParams;globalParams=0;
2562 //        Object::memCheck(stderr);
2563 //        gMemReport(stderr);
2564 //    }
2565 //} myMemCheck;
2566