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