added type3 char handling
[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\n",x,y,(charid&127)>=32?charid:'?', charid, u, uLen, font->isCIDFont(), render);
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(getFontName(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     return font;
1511 }
1512
1513 void GFXOutputDev::updateFont(GfxState *state) 
1514 {
1515     GfxFont* gfxFont = state->getFont();
1516     if (!gfxFont) {
1517         return; 
1518     }  
1519     char*id = getFontID(gfxFont);
1520     msg("<verbose> Updating font to %s", id);
1521     if(gfxFont->getType() == fontType3) {
1522         infofeature("Type3 fonts");
1523         if(!config_extrafontdata) {
1524             return;
1525         }
1526     }
1527     if(!id) {
1528         msg("<error> Internal Error: FontID is null");
1529         return; 
1530     }
1531
1532     this->current_fontinfo = this->info->getFont(id);
1533     if(!this->current_fontinfo->seen) {
1534         dumpFontInfo("<verbose>", gfxFont);
1535     }
1536     
1537     gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1538     if(!font) {
1539         font = createGfxFont(gfxFont, current_fontinfo);
1540         gfxfontlist_addfont(this->gfxfontlist, font);
1541         device->addfont(device, font);
1542     }
1543     current_gfxfont = font;
1544     free(id);
1545
1546     updateFontMatrix(state);
1547 }
1548
1549 #define SQR(x) ((x)*(x))
1550
1551 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1552 {
1553     if((newwidth<2 || newheight<2) ||
1554        (width<=newwidth || height<=newheight))
1555         return 0;
1556     unsigned char*newdata;
1557     int x,y;
1558     newdata= (unsigned char*)malloc(newwidth*newheight);
1559     int t;
1560     double fx = (double)(width)/newwidth;
1561     double fy = (double)(height)/newheight;
1562     double px = 0;
1563     int blocksize = (int)(8192/(fx*fy));
1564     int r = 8192*256/palettesize;
1565     for(x=0;x<newwidth;x++) {
1566         double ex = px + fx;
1567         int fromx = (int)px;
1568         int tox = (int)ex;
1569         int xweight1 = (int)(((fromx+1)-px)*256);
1570         int xweight2 = (int)((ex-tox)*256);
1571         double py =0;
1572         for(y=0;y<newheight;y++) {
1573             double ey = py + fy;
1574             int fromy = (int)py;
1575             int toy = (int)ey;
1576             int yweight1 = (int)(((fromy+1)-py)*256);
1577             int yweight2 = (int)((ey-toy)*256);
1578             int a = 0;
1579             int xx,yy;
1580             for(xx=fromx;xx<=tox;xx++)
1581             for(yy=fromy;yy<=toy;yy++) {
1582                 int b = 1-data[width*yy+xx];
1583                 int weight=256;
1584                 if(xx==fromx) weight = (weight*xweight1)/256;
1585                 if(xx==tox) weight = (weight*xweight2)/256;
1586                 if(yy==fromy) weight = (weight*yweight1)/256;
1587                 if(yy==toy) weight = (weight*yweight2)/256;
1588                 a+=b*weight;
1589             }
1590             //if(a) a=(palettesize-1)*r/blocksize;
1591             newdata[y*newwidth+x] = (a*blocksize)/r;
1592             py = ey;
1593         }
1594         px = ex;
1595     }
1596     return newdata;
1597 }
1598
1599 #define IMAGE_TYPE_JPEG 0
1600 #define IMAGE_TYPE_LOSSLESS 1
1601
1602 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
1603         double x1,double y1,
1604         double x2,double y2,
1605         double x3,double y3,
1606         double x4,double y4, int type)
1607 {
1608     gfxcolor_t*newpic=0;
1609     
1610     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1611     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1612    
1613     gfxline_t p1,p2,p3,p4,p5;
1614     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1615     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1616     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1617     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1618     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1619
1620     {p1.x = (int)(p1.x*20)/20.0;
1621      p1.y = (int)(p1.y*20)/20.0;
1622      p2.x = (int)(p2.x*20)/20.0;
1623      p2.y = (int)(p2.y*20)/20.0;
1624      p3.x = (int)(p3.x*20)/20.0;
1625      p3.y = (int)(p3.y*20)/20.0;
1626      p4.x = (int)(p4.x*20)/20.0;
1627      p4.y = (int)(p4.y*20)/20.0;
1628      p5.x = (int)(p5.x*20)/20.0;
1629      p5.y = (int)(p5.y*20)/20.0;
1630     }
1631     
1632     float m00,m10,tx;
1633     float m01,m11,ty;
1634     
1635     gfxmatrix_t m;
1636     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1637     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1638     m.tx = p1.x - 0.5;
1639     m.ty = p1.y - 0.5;
1640
1641     gfximage_t img;
1642     img.data = (gfxcolor_t*)data;
1643     img.width = sizex;
1644     img.height = sizey;
1645   
1646     if(type == IMAGE_TYPE_JPEG)
1647         /* TODO: pass image_dpi to device instead */
1648         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1649
1650     dev->fillbitmap(dev, &p1, &img, &m, 0);
1651 }
1652
1653 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
1654         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1655 {
1656     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1657 }
1658
1659 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
1660         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1661 {
1662     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1663 }
1664
1665
1666 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1667                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
1668                                    GBool inlineImg, int mask, int*maskColors,
1669                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1670 {
1671   double x1,y1,x2,y2,x3,y3,x4,y4;
1672   ImageStream *imgStr;
1673   Guchar pixBuf[4];
1674   GfxRGB rgb;
1675   int ncomps = 1;
1676   int bits = 1;
1677   unsigned char* maskbitmap = 0;
1678                                  
1679   if(colorMap) {
1680     ncomps = colorMap->getNumPixelComps();
1681     bits = colorMap->getBits();
1682   }
1683       
1684   if(maskStr) {
1685       int x,y;
1686       unsigned char buf[8];
1687       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1688       if(maskColorMap) {
1689           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1690           imgMaskStr->reset();
1691           unsigned char pal[256];
1692           int n = 1 << colorMap->getBits();
1693           int t;
1694           for(t=0;t<n;t++) {
1695               GfxGray gray;
1696               pixBuf[0] = t;
1697               maskColorMap->getGray(pixBuf, &gray);
1698               pal[t] = colToByte(gray);
1699           }
1700           for (y = 0; y < maskHeight; y++) {
1701               for (x = 0; x < maskWidth; x++) {
1702                   imgMaskStr->getPixel(buf);
1703                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
1704               }
1705           }
1706           delete imgMaskStr;
1707       } else {
1708           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1709           imgMaskStr->reset();
1710           for (y = 0; y < maskHeight; y++) {
1711               for (x = 0; x < maskWidth; x++) {
1712                   imgMaskStr->getPixel(buf);
1713                   buf[0]^=maskInvert;
1714                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1715               }
1716           }
1717           delete imgMaskStr;
1718       }
1719       maskStr->close();
1720   }
1721   
1722   imgStr = new ImageStream(str, width, ncomps,bits);
1723   imgStr->reset();
1724
1725   if(!width || !height || (height<=1 && width<=1))
1726   {
1727       msg("<verbose> Ignoring %d by %d image", width, height);
1728       unsigned char buf[8];
1729       int x,y;
1730       for (y = 0; y < height; ++y)
1731       for (x = 0; x < width; ++x) {
1732           imgStr->getPixel(buf);
1733       }
1734       delete imgStr;
1735       if(maskbitmap)
1736           free(maskbitmap);
1737       return;
1738   }
1739
1740   state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1741   state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1742   state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1743   state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1744
1745   if(!pbminfo && !(str->getKind()==strDCT)) {
1746       if(!type3active) {
1747           msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1748           pbminfo = 1;
1749       }
1750       if(mask)
1751       msg("<verbose> drawing %d by %d masked picture\n", width, height);
1752   }
1753   if(!jpeginfo && (str->getKind()==strDCT)) {
1754       msg("<notice> file contains jpeg pictures");
1755       jpeginfo = 1;
1756   }
1757
1758   if(mask) {
1759       int i,j;
1760       unsigned char buf[8];
1761       int x,y;
1762       unsigned char*pic = new unsigned char[width*height];
1763       gfxcolor_t pal[256];
1764       GfxRGB rgb;
1765       state->getFillRGB(&rgb);
1766
1767       memset(pal,255,sizeof(pal));
1768       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1769       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1770       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1771       pal[0].a = 255;              pal[1].a = 0;
1772     
1773       int numpalette = 2;
1774       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1775       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1776       for (y = 0; y < height; ++y)
1777       for (x = 0; x < width; ++x)
1778       {
1779             imgStr->getPixel(buf);
1780             if(invert) 
1781                 buf[0]=1-buf[0];
1782             pic[width*y+x] = buf[0];
1783       }
1784       
1785       /* the size of the drawn image is added to the identifier
1786          as the same image may require different bitmaps if displayed
1787          at different sizes (due to antialiasing): */
1788       int t,found = -1;
1789       if(type3active) {
1790           unsigned char*pic2 = 0;
1791           numpalette = 16;
1792           
1793           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1794
1795           if(!pic2) {
1796             delete pic;
1797             delete imgStr;
1798             return;
1799           }
1800
1801           width = realwidth;
1802           height = realheight;
1803           free(pic);
1804           pic = pic2;
1805           
1806           /* make a black/white palette */
1807
1808           float r = 255/(numpalette-1);
1809           int t;
1810           for(t=0;t<numpalette;t++) {
1811               pal[t].r = colToByte(rgb.r);
1812               pal[t].g = colToByte(rgb.g);
1813               pal[t].b = colToByte(rgb.b);
1814               pal[t].a = (unsigned char)(t*r);
1815           }
1816       }
1817
1818       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1819       for (y = 0; y < height; ++y) {
1820         for (x = 0; x < width; ++x) {
1821           pic2[width*y+x] = pal[pic[y*width+x]];
1822         }
1823       }
1824       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1825       free(pic2);
1826       free(pic);
1827       delete imgStr;
1828       if(maskbitmap) free(maskbitmap);
1829       return;
1830   }
1831
1832   int x,y;
1833
1834   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1835       gfxcolor_t*pic=new gfxcolor_t[width*height];
1836       for (y = 0; y < height; ++y) {
1837         for (x = 0; x < width; ++x) {
1838           imgStr->getPixel(pixBuf);
1839           colorMap->getRGB(pixBuf, &rgb);
1840           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1841           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1842           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1843           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1844           if(maskbitmap) {
1845               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1846           }
1847         }
1848       }
1849       if(str->getKind()==strDCT)
1850           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1851       else
1852           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1853       delete pic;
1854       delete imgStr;
1855       if(maskbitmap) free(maskbitmap);
1856       return;
1857   } else {
1858       gfxcolor_t*pic=new gfxcolor_t[width*height];
1859       gfxcolor_t pal[256];
1860       int n = 1 << colorMap->getBits();
1861       int t;
1862       for(t=0;t<256;t++) {
1863           pixBuf[0] = t;
1864           colorMap->getRGB(pixBuf, &rgb);
1865
1866           {/*if(maskColors && *maskColors==t) {
1867               msg("<notice> Color %d is transparent", t);
1868               if (imgData->maskColors) {
1869                 *alpha = 0;
1870                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1871                   if (pix[i] < imgData->maskColors[2*i] ||
1872                       pix[i] > imgData->maskColors[2*i+1]) {
1873                     *alpha = 1;
1874                     break;
1875                   }
1876                 }
1877               } else {
1878                 *alpha = 1;
1879               }
1880               if(!*alpha) {
1881                     pal[t].r = 0;
1882                     pal[t].g = 0;
1883                     pal[t].b = 0;
1884                     pal[t].a = 0;
1885               }
1886           } else {*/
1887               pal[t].r = (unsigned char)(colToByte(rgb.r));
1888               pal[t].g = (unsigned char)(colToByte(rgb.g));
1889               pal[t].b = (unsigned char)(colToByte(rgb.b));
1890               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1891           }
1892       }
1893       for (y = 0; y < height; ++y) {
1894         for (x = 0; x < width; ++x) {
1895           imgStr->getPixel(pixBuf);
1896           pic[width*y+x] = pal[pixBuf[0]];
1897           if(maskbitmap) {
1898               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1899           }
1900         }
1901       }
1902       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1903
1904       delete pic;
1905       delete imgStr;
1906       if(maskbitmap) free(maskbitmap);
1907       return;
1908   }
1909 }
1910
1911 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1912                                    int width, int height, GBool invert,
1913                                    GBool inlineImg) 
1914 {
1915     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1916     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1917     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1918 }
1919
1920 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1921                          int width, int height, GfxImageColorMap *colorMap,
1922                          int *maskColors, GBool inlineImg)
1923 {
1924     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
1925             colorMap?"colorMap":"no colorMap", 
1926             maskColors?"maskColors":"no maskColors",
1927             inlineImg);
1928     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
1929             colorMap?"colorMap":"no colorMap", 
1930             maskColors?"maskColors":"no maskColors",
1931             inlineImg);
1932     if(colorMap)
1933         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1934                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1935     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1936 }
1937   
1938 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1939                                int width, int height,
1940                                GfxImageColorMap *colorMap,
1941                                Stream *maskStr, int maskWidth, int maskHeight,
1942                                GBool maskInvert)
1943 {
1944     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1945             colorMap?"colorMap":"no colorMap", 
1946             maskWidth, maskHeight);
1947     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1948             colorMap?"colorMap":"no colorMap", 
1949             maskWidth, maskHeight);
1950     if(colorMap)
1951         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1952                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1953     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1954 }
1955
1956 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1957                                    int width, int height,
1958                                    GfxImageColorMap *colorMap,
1959                                    Stream *maskStr,
1960                                    int maskWidth, int maskHeight,
1961                                    GfxImageColorMap *maskColorMap)
1962 {
1963     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1964             colorMap?"colorMap":"no colorMap", 
1965             maskWidth, maskHeight);
1966     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1967             colorMap?"colorMap":"no colorMap", 
1968             maskWidth, maskHeight);
1969     if(colorMap)
1970         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1971                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1972     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1973 }
1974
1975 void GFXOutputDev::stroke(GfxState *state) 
1976 {
1977     dbg("stroke");
1978
1979     GfxPath * path = state->getPath();
1980     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1981     strokeGfxline(state, line, 0);
1982     gfxline_free(line);
1983 }
1984
1985 void GFXOutputDev::fill(GfxState *state) 
1986 {
1987     gfxcolor_t col = getFillColor(state);
1988     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1989
1990     GfxPath * path = state->getPath();
1991     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1992     fillGfxLine(state, line);
1993     gfxline_free(line);
1994 }
1995
1996 void GFXOutputDev::eoFill(GfxState *state) 
1997 {
1998     gfxcolor_t col = getFillColor(state);
1999     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2000
2001     GfxPath * path = state->getPath();
2002     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2003     fillGfxLine(state, line);
2004     gfxline_free(line);
2005 }
2006
2007
2008 static const char* dirseparator()
2009 {
2010 #ifdef WIN32
2011     return "\\";
2012 #else
2013     return "/";
2014 #endif
2015 }
2016
2017 void addGlobalFont(const char*filename)
2018 {
2019     fontfile_t f;
2020     memset(&f, 0, sizeof(fontfile_t));
2021     f.filename = filename;
2022     if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
2023         msg("<verbose> Adding font \"%s\".", filename);
2024         msg("<warning> External fonts are not supported with this version. Ignoring font %s", filename);
2025         fonts[fontnum++] = f;
2026     } else {
2027         msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
2028     }
2029 }
2030
2031 void addGlobalLanguageDir(const char*dir)
2032 {
2033     msg("<notice> Adding %s to language pack directories", dir);
2034
2035     int l;
2036     FILE*fi = 0;
2037     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2038     strcpy(config_file, dir);
2039     strcat(config_file, dirseparator());
2040     strcat(config_file, "add-to-xpdfrc");
2041
2042     fi = fopen(config_file, "rb");
2043     if(!fi) {
2044         msg("<error> Could not open %s", config_file);
2045         return;
2046     }
2047     globalParams->parseFile(new GString(config_file), fi);
2048     fclose(fi);
2049 }
2050
2051 void addGlobalFontDir(const char*dirname)
2052 {
2053     msg("<warning> External fonts are not supported with this version. Ignoring directory %s", dirname);
2054     return;
2055 #ifdef HAVE_DIRENT_H
2056     msg("<notice> Adding %s to font directories", dirname);
2057     lastfontdir = strdup(dirname);
2058     DIR*dir = opendir(dirname);
2059     if(!dir) {
2060         msg("<warning> Couldn't open directory %s\n", dirname);
2061         return;
2062     }
2063     struct dirent*ent;
2064     while(1) {
2065         ent = readdir (dir);
2066         if (!ent) 
2067             break;
2068         int l;
2069         char*name = ent->d_name;
2070         char type = 0;
2071         if(!name) continue;
2072         l=strlen(name);
2073         if(l<4)
2074             continue;
2075         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2076             type=1;
2077         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2078             type=3;
2079         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2080             type=2;
2081         if(type)
2082         {
2083             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2084             strcpy(fontname, dirname);
2085             strcat(fontname, dirseparator());
2086             strcat(fontname, name);
2087             addGlobalFont(fontname);
2088         }
2089     }
2090     closedir(dir);
2091 #else
2092     msg("<warning> No dirent.h- unable to add font dir %s", dir);
2093 #endif
2094 }
2095
2096 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2097 {
2098     if(pdfpage < 0)
2099         return;
2100
2101     if(!this->pages) {
2102         this->pagebuflen = 1024;
2103         this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2104         memset(this->pages, -1, this->pagebuflen*sizeof(int));
2105     } else {
2106         while(pdfpage >= this->pagebuflen)
2107         {
2108             int oldlen = this->pagebuflen;
2109             this->pagebuflen+=1024;
2110             this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2111             memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2112         }
2113     }
2114     this->pages[pdfpage] = outputpage;
2115     if(pdfpage>this->pagepos)
2116         this->pagepos = pdfpage;
2117 }
2118
2119 struct BBox
2120 {
2121     double posx,posy;
2122     double width,height;
2123 };
2124
2125 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2126 {
2127     double xMin, yMin, xMax, yMax, x, y;
2128     double tx, ty, w, h;
2129     // transform the bbox
2130     state->transform(bbox[0], bbox[1], &x, &y);
2131     xMin = xMax = x;
2132     yMin = yMax = y;
2133     state->transform(bbox[0], bbox[3], &x, &y);
2134     if (x < xMin) {
2135       xMin = x;
2136     } else if (x > xMax) {
2137       xMax = x;
2138     }
2139     if (y < yMin) {
2140       yMin = y;
2141     } else if (y > yMax) {
2142       yMax = y;
2143     }
2144     state->transform(bbox[2], bbox[1], &x, &y);
2145     if (x < xMin) {
2146       xMin = x;
2147     } else if (x > xMax) {
2148       xMax = x;
2149     }
2150     if (y < yMin) {
2151       yMin = y;
2152     } else if (y > yMax) {
2153       yMax = y;
2154     }
2155     state->transform(bbox[2], bbox[3], &x, &y);
2156     if (x < xMin) {
2157       xMin = x;
2158     } else if (x > xMax) {
2159       xMax = x;
2160     }
2161     if (y < yMin) {
2162       yMin = y;
2163     } else if (y > yMax) {
2164       yMax = y;
2165     }
2166     tx = (int)floor(xMin);
2167     if (tx < 0) {
2168       tx = 0;
2169     } else if (tx > width) {
2170       tx = width;
2171     }
2172     ty = (int)floor(yMin);
2173     if (ty < 0) {
2174       ty = 0;
2175     } else if (ty > height) {
2176       ty = height;
2177     }
2178     w = (int)ceil(xMax) - tx + 1;
2179     if (tx + w > width) {
2180       w = width - tx;
2181     }
2182     if (w < 1) {
2183       w = 1;
2184     }
2185     h = (int)ceil(yMax) - ty + 1;
2186     if (ty + h > height) {
2187       h = height - ty;
2188     }
2189     if (h < 1) {
2190       h = 1;
2191     }
2192     BBox nbbox;
2193     nbbox.posx = xMin;
2194     nbbox.posx = yMin;
2195     nbbox.width = w;
2196     nbbox.height = h;
2197     return nbbox;
2198 }
2199
2200 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2201                                       GfxColorSpace *blendingColorSpace,
2202                                       GBool isolated, GBool knockout,
2203                                       GBool forSoftMask)
2204 {
2205     const char*colormodename = "";
2206     BBox rect = mkBBox(state, bbox, this->width, this->height);
2207
2208     if(blendingColorSpace) {
2209         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2210     }
2211     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);
2212     dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2213     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);
2214     
2215     states[statepos].createsoftmask |= forSoftMask;
2216     states[statepos].transparencygroup = !forSoftMask;
2217     states[statepos].isolated = isolated;
2218
2219     states[statepos].olddevice = this->device;
2220     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2221
2222     gfxdevice_record_init(this->device);
2223     
2224     /*if(!forSoftMask) { ////???
2225         state->setFillOpacity(0.0);
2226     }*/
2227     dbgindent+=2;
2228 }
2229
2230 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2231 {
2232     dbgindent-=2;
2233     dbg("endTransparencyGroup");
2234     msg("<verbose> endTransparencyGroup");
2235
2236     gfxdevice_t*r = this->device;
2237
2238     this->device = states[statepos].olddevice;
2239
2240     if(states[statepos].createsoftmask) {
2241         states[statepos-1].softmaskrecording = r->finish(r);
2242     } else {
2243         states[statepos-1].grouprecording = r->finish(r);
2244     }
2245     
2246     states[statepos].createsoftmask = 0;
2247     states[statepos].transparencygroup = 0;
2248     free(r);
2249 }
2250
2251 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2252 {
2253     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2254                                "colordodge","colorburn","hardlight","softlight","difference",
2255                                "exclusion","hue","saturation","color","luminosity"};
2256
2257     dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2258     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2259    
2260     if(state->getBlendMode() == gfxBlendNormal)
2261         infofeature("transparency groups");
2262     else {
2263         char buffer[80];
2264         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2265         warnfeature(buffer, 0);
2266     }
2267
2268     gfxresult_t*grouprecording = states[statepos].grouprecording;
2269    
2270     if(state->getBlendMode() == gfxBlendNormal) {
2271         gfxdevice_t ops;
2272         gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2273         gfxresult_record_replay(grouprecording, &ops);
2274         ops.finish(&ops);
2275     }
2276     grouprecording->destroy(grouprecording);
2277
2278     states[statepos].grouprecording = 0;
2279 }
2280
2281 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2282 {
2283     /* alpha = 1: retrieve mask values from alpha layer
2284        alpha = 0: retrieve mask values from luminance */
2285     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2286             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2287     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2288             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2289     if(!alpha)
2290         infofeature("soft masks");
2291     else
2292         warnfeature("soft masks from alpha channel",0);
2293     
2294     states[statepos].olddevice = this->device;
2295     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2296     gfxdevice_record_init(this->device);
2297
2298     dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2299     
2300     states[statepos].softmask = 1;
2301     states[statepos].softmask_alpha = alpha;
2302 }
2303
2304 static inline Guchar div255(int x) {
2305   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2306 }
2307
2308 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2309 {
2310     if(c < min) c = min;
2311     if(c > max) c = max;
2312     return c;
2313 }
2314
2315 void GFXOutputDev::clearSoftMask(GfxState *state)
2316 {
2317     if(!states[statepos].softmask)
2318         return;
2319     states[statepos].softmask = 0;
2320     dbg("clearSoftMask statepos=%d", statepos);
2321     msg("<verbose> clearSoftMask");
2322     
2323     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2324         msg("<error> Error in softmask/tgroup ordering");
2325         return;
2326     }
2327   
2328     gfxresult_t*mask = states[statepos].softmaskrecording;
2329     gfxresult_t*below = this->device->finish(this->device);
2330     this->device = states[statepos].olddevice;
2331
2332     /* get outline of all objects below the soft mask */
2333     gfxdevice_t uniondev;
2334     gfxdevice_union_init(&uniondev, 0);
2335     gfxresult_record_replay(below, &uniondev);
2336     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2337     uniondev.finish(&uniondev);
2338
2339     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2340 #if 0 
2341     this->device->startclip(this->device, belowoutline);
2342     gfxresult_record_replay(below, this->device);
2343     gfxresult_record_replay(mask, this->device);
2344     this->device->endclip(this->device);
2345     gfxline_free(belowoutline);
2346 #endif
2347     
2348     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2349     if(width<=0 || height<=0)
2350         return;
2351
2352     gfxdevice_t belowrender;
2353     gfxdevice_render_init(&belowrender);
2354     if(states[statepos+1].isolated) {
2355         belowrender.setparameter(&belowrender, "fillwhite", "1");
2356     }
2357     belowrender.setparameter(&belowrender, "antialize", "2");
2358     belowrender.startpage(&belowrender, width, height);
2359     gfxresult_record_replay(below, &belowrender);
2360     belowrender.endpage(&belowrender);
2361     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2362     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2363     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2364
2365     gfxdevice_t maskrender;
2366     gfxdevice_render_init(&maskrender);
2367     maskrender.startpage(&maskrender, width, height);
2368     gfxresult_record_replay(mask, &maskrender);
2369     maskrender.endpage(&maskrender);
2370     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2371     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2372
2373     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2374         msg("<fatal> Internal error in mask drawing");
2375         return;
2376     }
2377
2378     int y,x;
2379     for(y=0;y<height;y++) {
2380         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2381         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2382         for(x=0;x<width;x++) {
2383             int alpha;
2384             if(states[statepos].softmask_alpha) {
2385                 alpha = l1->a;
2386             } else {
2387                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2388             }
2389
2390             l2->a = div255(alpha*l2->a);
2391
2392             /* DON'T premultiply alpha- this is done by fillbitmap,
2393                depending on the output device */
2394             //l2->r = div255(alpha*l2->r);
2395             //l2->g = div255(alpha*l2->g);
2396             //l2->b = div255(alpha*l2->b);
2397
2398             l1++;
2399             l2++;
2400         }
2401     }
2402     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2403
2404     gfxmatrix_t matrix;
2405     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2406     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2407
2408     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2409
2410     mask->destroy(mask);
2411     below->destroy(below);
2412     maskresult->destroy(maskresult);
2413     belowresult->destroy(belowresult);
2414     states[statepos].softmaskrecording = 0;
2415 }
2416   
2417 //class MemCheck
2418 //{
2419 //    public: ~MemCheck()
2420 //    {
2421 //        delete globalParams;globalParams=0;
2422 //        Object::memCheck(stderr);
2423 //        gMemReport(stderr);
2424 //    }
2425 //} myMemCheck;
2426