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