reworked font backend
[swftools.git] / lib / pdf / GFXOutputDev.cc
1 /* pdfswf.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 this->do_interpretType3Chars;
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->do_interpretType3Chars = gTrue;
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,"rawtext")) {
381         this->do_interpretType3Chars = atoi(value)^1;
382     } else if(!strcmp(key,"breakonwarning")) {
383         this->config_break_on_warning = atoi(value);
384     } else if(!strcmp(key,"fontconfig")) {
385         this->config_use_fontconfig = atoi(value);
386     } else if(!strcmp(key,"remapunicode")) {
387         this->config_remapunicode = atoi(value);
388     } else if(!strcmp(key,"transparent")) {
389         this->config_transparent = 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
894 void GFXOutputDev::beginString(GfxState *state, GString *s) 
895
896     int render = state->getRender();
897     if(current_text_stroke) {
898         msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
899     }
900
901     msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
902     double m11,m21,m12,m22;
903     state->getFontTransMat(&m11, &m12, &m21, &m22);
904     m11 *= state->getHorizScaling();
905     m21 *= state->getHorizScaling();
906
907     this->current_font_matrix.m00 = m11 / 1024.0;
908     this->current_font_matrix.m01 = m12 / 1024.0;
909     this->current_font_matrix.m10 = -m21 / 1024.0;
910     this->current_font_matrix.m11 = -m22 / 1024.0;
911     this->current_font_matrix.tx = 0;
912     this->current_font_matrix.ty = 0;
913 }
914
915 static gfxline_t* mkEmptyGfxShape(double x, double y)
916 {
917     gfxline_t*line = (gfxline_t*)malloc(sizeof(gfxline_t));
918     line->x = x;line->y = y;line->type = gfx_moveTo;line->next = 0;
919     return line;
920 }
921
922 static char isValidUnicode(int c)
923 {
924     if(c>=32 && c<0x2fffe)
925         return 1;
926     return 0;
927 }
928
929 void GFXOutputDev::drawChar(GfxState *state, double x, double y,
930                         double dx, double dy,
931                         double originX, double originY,
932                         CharCode charid, int nBytes, Unicode *_u, int uLen)
933 {
934     if(!current_fontinfo || (unsigned)charid >= current_fontinfo->num_glyphs || !current_fontinfo->glyphs[charid]) {
935         msg("<error> Invalid charid %d for font %s", charid, current_font_id);
936         return;
937     }
938
939     CharCode glyphid = current_fontinfo->glyphs[charid]->glyphid;
940
941     int render = state->getRender();
942     gfxcolor_t col = getFillColor(state);
943
944     // check for invisible text -- this is used by Acrobat Capture
945     if (render == RENDER_INVISIBLE) {
946         col.a = 0;
947     }
948
949     GfxFont*font = state->getFont();
950
951     if(font->getType() == fontType3 && do_interpretType3Chars) {
952         /* type 3 chars are passed as graphics */
953         msg("<debug> type3 char at %f/%f", x, y);
954         return;
955     }
956
957     Unicode u = uLen?(_u[0]):0;
958     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);
959
960     gfxmatrix_t m = this->current_font_matrix;
961     state->transform(x, y, &m.tx, &m.ty);
962     m.tx += user_movex + clipmovex;
963     m.ty += user_movey + clipmovey;
964
965     if(render == RENDER_FILL || render == RENDER_INVISIBLE) {
966         device->drawchar(device, current_gfxfont, glyphid, &col, &m);
967     } else {
968         msg("<debug> Drawing glyph %d as shape", charid);
969         if(!textmodeinfo) {
970             msg("<notice> Some texts will be rendered as shape");
971             textmodeinfo = 1;
972         }
973         gfxline_t*glyph = current_gfxfont->glyphs[glyphid].line;
974         gfxline_t*tglyph = gfxline_clone(glyph);
975         gfxline_transform(tglyph, &m);
976         if((render&3) != RENDER_INVISIBLE) {
977             gfxline_t*add = gfxline_clone(tglyph);
978             current_text_stroke = gfxline_append(current_text_stroke, add);
979         }
980         if(render&RENDER_CLIP) {
981             gfxline_t*add = gfxline_clone(tglyph);
982             current_text_clip = gfxline_append(current_text_clip, add);
983             if(!current_text_clip) {
984                 current_text_clip = mkEmptyGfxShape(m.tx, m.ty);
985             }
986         }
987         gfxline_free(tglyph);
988     }
989 }
990
991 void GFXOutputDev::endString(GfxState *state) 
992
993     int render = state->getRender();
994     msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
995     
996     if(current_text_stroke) {
997         /* fillstroke and stroke text rendering objects we can process right
998            now (as there may be texts of other rendering modes in this
999            text object)- clipping objects have to wait until endTextObject,
1000            however */
1001         device->setparameter(device, "mark","TXT");
1002         if((render&3) == RENDER_FILL) {
1003             fillGfxLine(state, current_text_stroke);
1004             gfxline_free(current_text_stroke);
1005             current_text_stroke = 0;
1006         } else if((render&3) == RENDER_FILLSTROKE) {
1007             fillGfxLine(state, current_text_stroke);
1008             strokeGfxline(state, current_text_stroke,0);
1009             gfxline_free(current_text_stroke);
1010             current_text_stroke = 0;
1011         } else if((render&3) == RENDER_STROKE) {
1012             strokeGfxline(state, current_text_stroke,0);
1013             gfxline_free(current_text_stroke);
1014             current_text_stroke = 0;
1015         }
1016         device->setparameter(device, "mark","");
1017     }
1018 }    
1019
1020 void GFXOutputDev::endTextObject(GfxState *state)
1021 {
1022     int render = state->getRender();
1023     msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
1024     
1025     if(current_text_clip) {
1026         device->setparameter(device, "mark","TXT");
1027         clipToGfxLine(state, current_text_clip);
1028         device->setparameter(device, "mark","");
1029         gfxline_free(current_text_clip);
1030         current_text_clip = 0;
1031     }
1032 }
1033
1034 /* the logic seems to be as following:
1035    first, beginType3Char is called, with the charcode and the coordinates.
1036    if this function returns true, it already knew about the char and has now drawn it.
1037    if the function returns false, it's a new char, and type3D1 is called with some parameters-
1038    the all draw operations until endType3Char are part of the char (which in this moment is
1039    at the position first passed to beginType3Char). the char ends with endType3Char.
1040
1041    The drawing operations between beginType3Char and endType3Char are somewhat different to
1042    the normal ones. For example, the fillcolor equals the stroke color.
1043 */
1044
1045 GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
1046 {
1047     msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
1048     type3active = 1;
1049
1050     /*int t;
1051
1052     gfxcolor_t col={255,0,0,0};
1053     gfxmatrix_t m = {1,0,0, 0,1,0};
1054
1055     for(t=0;t<uLen;t++) {
1056         device->drawchar(device, 0, u[t], &col, &m);
1057     }*/
1058
1059     /* the character itself is going to be passed using the draw functions */
1060     return gFalse; /* gTrue= is_in_cache? */
1061 }
1062
1063 void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
1064     msg("<debug> type3D0 width=%f height=%f", wx, wy);
1065 }
1066 void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
1067     msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
1068             llx,lly,urx,ury);
1069 }
1070
1071 void GFXOutputDev::endType3Char(GfxState *state)
1072 {
1073     type3active = 0;
1074     msg("<debug> endType3Char");
1075 }
1076
1077 void GFXOutputDev::startFrame(int width, int height) 
1078 {
1079     if(outer_clip_box) {
1080         device->endclip(device);
1081         outer_clip_box = 0;
1082     }
1083
1084     device->startpage(device, width, height);
1085     this->width = width;
1086     this->height = height;
1087 }
1088
1089 void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2) 
1090 {
1091     this->currentpage = pageNum;
1092     double x1,y1,x2,y2;
1093     int rot = doc->getPageRotate(1);
1094     gfxcolor_t white;
1095     laststate = state;
1096     gfxline_t clippath[5];
1097
1098     white.r = white.g = white.b = white.a = 255;
1099
1100     /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
1101     state->transform(state->getX2(),state->getY2(),&x2,&y2);
1102     Use CropBox, not MediaBox, as page size
1103     */
1104     
1105     /*x1 = crop_x1;
1106     y1 = crop_y1;
1107     x2 = crop_x2;
1108     y2 = crop_y2;*/
1109     state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
1110     state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
1111
1112     if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
1113     if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
1114
1115     this->clipmovex = -(int)x1;
1116     this->clipmovey = -(int)y1;
1117     
1118     /* apply user clip box */
1119     if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
1120         /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
1121         /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
1122         /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
1123         /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
1124         msg("<verbose> Using user clip box %f/%f/%f/%f",x1,y1,x2,y2);
1125     }
1126
1127     //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
1128     
1129     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);
1130     if(rot!=0)
1131         msg("<verbose> page is rotated %d degrees\n", rot);
1132
1133     clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
1134     clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
1135     clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
1136     clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
1137     clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
1138     device->startclip(device, clippath); outer_clip_box = 1;
1139     if(!config_transparent)
1140         device->fill(device, clippath, &white);
1141 }
1142
1143
1144 void GFXOutputDev::processLink(Link *link, Catalog *catalog)
1145 {
1146     double x1, y1, x2, y2, w;
1147     gfxline_t points[5];
1148     int x, y;
1149     
1150     msg("<debug> drawlink\n");
1151
1152     link->getRect(&x1, &y1, &x2, &y2);
1153     cvtUserToDev(x1, y1, &x, &y);
1154     points[0].type = gfx_moveTo;
1155     points[0].x = points[4].x = x + user_movex + clipmovex;
1156     points[0].y = points[4].y = y + user_movey + clipmovey;
1157     points[0].next = &points[1];
1158     cvtUserToDev(x2, y1, &x, &y);
1159     points[1].type = gfx_lineTo;
1160     points[1].x = x + user_movex + clipmovex;
1161     points[1].y = y + user_movey + clipmovey;
1162     points[1].next = &points[2];
1163     cvtUserToDev(x2, y2, &x, &y);
1164     points[2].type = gfx_lineTo;
1165     points[2].x = x + user_movex + clipmovex;
1166     points[2].y = y + user_movey + clipmovey;
1167     points[2].next = &points[3];
1168     cvtUserToDev(x1, y2, &x, &y);
1169     points[3].type = gfx_lineTo;
1170     points[3].x = x + user_movex + clipmovex;
1171     points[3].y = y + user_movey + clipmovey;
1172     points[3].next = &points[4];
1173     cvtUserToDev(x1, y1, &x, &y);
1174     points[4].type = gfx_lineTo;
1175     points[4].x = x + user_movex + clipmovex;
1176     points[4].y = y + user_movey + clipmovey;
1177     points[4].next = 0;
1178     
1179     msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
1180             points[0].x, points[0].y,
1181             points[1].x, points[1].y,
1182             points[2].x, points[2].y,
1183             points[3].x, points[3].y); 
1184
1185     LinkAction*action=link->getAction();
1186     char buf[128];
1187     char*s = 0;
1188     const char*type = "-?-";
1189     char*named = 0;
1190     int page = -1;
1191     msg("<trace> drawlink action=%d\n", action->getKind());
1192     switch(action->getKind())
1193     {
1194         case actionGoTo: {
1195             type = "GoTo";
1196             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1197             LinkDest *dest=NULL;
1198             if (ha->getDest()==NULL) 
1199                 dest=catalog->findDest(ha->getNamedDest());
1200             else dest=ha->getDest();
1201             if (dest){ 
1202               if (dest->isPageRef()){
1203                 Ref pageref=dest->getPageRef();
1204                 page=catalog->findPage(pageref.num,pageref.gen);
1205               }
1206               else  page=dest->getPageNum();
1207               sprintf(buf, "%d", page);
1208               s = strdup(buf);
1209             }
1210         }
1211         break;
1212         case actionGoToR: {
1213             type = "GoToR";
1214             LinkGoToR*l = (LinkGoToR*)action;
1215             GString*g = l->getFileName();
1216             if(g)
1217              s = strdup(g->getCString());
1218             if(!s) {
1219                 /* if the GoToR link has no filename, then
1220                    try to find a refernce in the *local*
1221                    file */
1222                 GString*g = l->getNamedDest();
1223                 if(g)
1224                  s = strdup(g->getCString());
1225             }
1226         }
1227         break;
1228         case actionNamed: {
1229             type = "Named";
1230             LinkNamed*l = (LinkNamed*)action;
1231             GString*name = l->getName();
1232             if(name) {
1233                 s = strdup(name->lowerCase()->getCString());
1234                 named = name->getCString();
1235                 if(!strchr(s,':')) 
1236                 {
1237                     if(strstr(s, "next") || strstr(s, "forward"))
1238                     {
1239                         page = currentpage + 1;
1240                     }
1241                     else if(strstr(s, "prev") || strstr(s, "back"))
1242                     {
1243                         page = currentpage - 1;
1244                     }
1245                     else if(strstr(s, "last") || strstr(s, "end"))
1246                     {
1247                         if(pages && pagepos>0)
1248                             page = pages[pagepos-1];
1249                     }
1250                     else if(strstr(s, "first") || strstr(s, "top"))
1251                     {
1252                         page = 1;
1253                     }
1254                 }
1255             }
1256         }
1257         break;
1258         case actionLaunch: {
1259             type = "Launch";
1260             LinkLaunch*l = (LinkLaunch*)action;
1261             GString * str = new GString(l->getFileName());
1262             GString * params = l->getParams();
1263             if(params)
1264                 str->append(params);
1265             s = strdup(str->getCString());
1266             delete str;
1267         }
1268         break;
1269         case actionURI: {
1270             char*url = 0;
1271             type = "URI";
1272             LinkURI*l = (LinkURI*)action;
1273             GString*g = l->getURI();
1274             if(g) {
1275              url = g->getCString();
1276              s = strdup(url);
1277             }
1278         }
1279         break;
1280         case actionUnknown: {
1281             type = "Unknown";
1282             LinkUnknown*l = (LinkUnknown*)action;
1283             s = strdup("");
1284         }
1285         break;
1286         default: {
1287             msg("<error> Unknown link type!\n");
1288             break;
1289         }
1290     }
1291
1292     if(!s) s = strdup("-?-");
1293     
1294     msg("<trace> drawlink s=%s\n", s);
1295
1296     if(!linkinfo && (page || s))
1297     {
1298         msg("<notice> File contains links");
1299         linkinfo = 1;
1300     }
1301     
1302     if(page>0)
1303     {
1304         int t;
1305         int lpage = -1;
1306         for(t=1;t<=pagepos;t++) {
1307             if(pages[t]==page) {
1308                 lpage = t;
1309                 break;
1310             }
1311         }
1312         if(lpage<0) {
1313             lpage = page;
1314         }
1315         char buf[80];
1316         sprintf(buf, "page%d", lpage);
1317         device->drawlink(device, points, buf);
1318     }
1319     else if(s)
1320     {
1321         device->drawlink(device, points, s);
1322     }
1323
1324     msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1325     free(s);s=0;
1326 }
1327
1328 void GFXOutputDev::saveState(GfxState *state) {
1329     dbg("saveState");dbgindent+=2;
1330
1331     msg("<trace> saveState\n");
1332     updateAll(state);
1333     if(statepos>=64) {
1334       msg("<error> Too many nested states in pdf.");
1335       return;
1336     }
1337     statepos ++;
1338     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1339     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1340     states[statepos].clipping = 0;
1341 };
1342
1343 void GFXOutputDev::restoreState(GfxState *state) {
1344   dbgindent-=2; dbg("restoreState");
1345
1346   if(statepos==0) {
1347       msg("<error> Invalid restoreState");
1348       return;
1349   }
1350   msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1351                                   states[statepos].clipping?" (end clipping)":"");
1352   if(states[statepos].softmask) {
1353       clearSoftMask(state);
1354   }
1355   updateAll(state);
1356   while(states[statepos].clipping) {
1357       device->endclip(device);
1358       states[statepos].clipping--;
1359   }
1360   statepos--;
1361 }
1362
1363 void GFXOutputDev::updateLineWidth(GfxState *state)
1364 {
1365     double width = state->getTransformedLineWidth();
1366     //swfoutput_setlinewidth(&device, width);
1367 }
1368
1369 void GFXOutputDev::updateLineCap(GfxState *state)
1370 {
1371     int c = state->getLineCap();
1372 }
1373
1374 void GFXOutputDev::updateLineJoin(GfxState *state)
1375 {
1376     int j = state->getLineJoin();
1377 }
1378
1379 void GFXOutputDev::updateFillColor(GfxState *state) 
1380 {
1381     GfxRGB rgb;
1382     double opaq = state->getFillOpacity();
1383     state->getFillRGB(&rgb);
1384 }
1385 void GFXOutputDev::updateFillOpacity(GfxState *state)
1386 {
1387     GfxRGB rgb;
1388     double opaq = state->getFillOpacity();
1389     state->getFillRGB(&rgb);
1390     dbg("update fillopaq %f", opaq);
1391 }
1392 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1393 {
1394     double opaq = state->getFillOpacity();
1395     dbg("update strokeopaq %f", opaq);
1396 }
1397 void GFXOutputDev::updateFillOverprint(GfxState *state)
1398 {
1399     double opaq = state->getFillOverprint();
1400     dbg("update filloverprint %f", opaq);
1401 }
1402 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1403 {
1404     double opaq = state->getStrokeOverprint();
1405     dbg("update strokeoverprint %f", opaq);
1406 }
1407 void GFXOutputDev::updateTransfer(GfxState *state)
1408 {
1409     dbg("update transfer");
1410 }
1411
1412
1413 void GFXOutputDev::updateStrokeColor(GfxState *state) 
1414 {
1415     GfxRGB rgb;
1416     double opaq = state->getStrokeOpacity();
1417     state->getStrokeRGB(&rgb);
1418 }
1419
1420 void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref) 
1421 {
1422     this->doc = doc;
1423     this->xref = xref;
1424 }
1425
1426 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src)
1427 {
1428     gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1429     memset(font, 0, sizeof(gfxfont_t));
1430
1431     font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1432     memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1433     font->id = strdup(getFontName(xpdffont));
1434     int t;
1435     double quality = (1024 * 0.05) / src->max_size;
1436     double scale = 1;
1437     //printf("%d glyphs\n", font->num_glyphs);
1438     font->num_glyphs = 0;
1439     for(t=0;t<src->num_glyphs;t++) {
1440         if(src->glyphs[t]) {
1441             SplashPath*path = src->glyphs[t]->path;
1442             int len = path?path->getLength():0;
1443             //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1444             gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1445             src->glyphs[t]->glyphid = font->num_glyphs;
1446             glyph->unicode = src->glyphs[t]->unicode;
1447             if(glyph->unicode >= font->max_unicode)
1448                 font->max_unicode = glyph->unicode+1;
1449             gfxdrawer_t drawer;
1450             gfxdrawer_target_gfxline(&drawer);
1451             int s;
1452             int count = 0;
1453             double xmax = 0;
1454             for(s=0;s<len;s++) {
1455                 Guchar f;
1456                 double x, y;
1457                 path->getPoint(s, &x, &y, &f);
1458                 if(x > xmax)
1459                     xmax = x;
1460                 if(f&splashPathFirst) {
1461                     drawer.moveTo(&drawer, x*scale, y*scale);
1462                 }
1463                 if(f&splashPathCurve) {
1464                     double x2,y2;
1465                     path->getPoint(++s, &x2, &y2, &f);
1466                     if(f&splashPathCurve) {
1467                         double x3,y3;
1468                         path->getPoint(++s, &x3, &y3, &f);
1469                         gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1470                     } else {
1471                         drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1472                     }
1473                 } else {
1474                     drawer.lineTo(&drawer, x*scale, y*scale);
1475                 }
1476              //   printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1477              //                           (f&splashPathFirst)?"first":"",
1478              //                           (f&splashPathLast)?"last":"");
1479             }
1480             glyph->line = (gfxline_t*)drawer.result(&drawer);
1481             glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1482             font->num_glyphs++;
1483         }
1484     }
1485     font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1486     memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1487     for(t=0;t<font->num_glyphs;t++) {
1488         if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1489             font->unicode2glyph[font->glyphs[t].unicode] = t;
1490         }
1491
1492     }
1493     return font;
1494 }
1495
1496 void GFXOutputDev::updateFont(GfxState *state) 
1497 {
1498     GfxFont* gfxFont = state->getFont();
1499     if (!gfxFont) {
1500         return; 
1501     }  
1502     char*id = getFontID(gfxFont);
1503     if(!id) {
1504         msg("<error> Internal Error: FontID is null");
1505         return; 
1506     }
1507
1508     this->current_fontinfo = this->info->getFont(id);
1509     
1510     gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1511     if(!font) {
1512         font = createGfxFont(gfxFont, current_fontinfo);
1513         gfxfontlist_addfont(this->gfxfontlist, font);
1514         device->addfont(device, font);
1515     }
1516     current_gfxfont = font;
1517     free(id);
1518 }
1519
1520 #define SQR(x) ((x)*(x))
1521
1522 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1523 {
1524     if((newwidth<2 || newheight<2) ||
1525        (width<=newwidth || height<=newheight))
1526         return 0;
1527     unsigned char*newdata;
1528     int x,y;
1529     newdata= (unsigned char*)malloc(newwidth*newheight);
1530     int t;
1531     double fx = (double)(width)/newwidth;
1532     double fy = (double)(height)/newheight;
1533     double px = 0;
1534     int blocksize = (int)(8192/(fx*fy));
1535     int r = 8192*256/palettesize;
1536     for(x=0;x<newwidth;x++) {
1537         double ex = px + fx;
1538         int fromx = (int)px;
1539         int tox = (int)ex;
1540         int xweight1 = (int)(((fromx+1)-px)*256);
1541         int xweight2 = (int)((ex-tox)*256);
1542         double py =0;
1543         for(y=0;y<newheight;y++) {
1544             double ey = py + fy;
1545             int fromy = (int)py;
1546             int toy = (int)ey;
1547             int yweight1 = (int)(((fromy+1)-py)*256);
1548             int yweight2 = (int)((ey-toy)*256);
1549             int a = 0;
1550             int xx,yy;
1551             for(xx=fromx;xx<=tox;xx++)
1552             for(yy=fromy;yy<=toy;yy++) {
1553                 int b = 1-data[width*yy+xx];
1554                 int weight=256;
1555                 if(xx==fromx) weight = (weight*xweight1)/256;
1556                 if(xx==tox) weight = (weight*xweight2)/256;
1557                 if(yy==fromy) weight = (weight*yweight1)/256;
1558                 if(yy==toy) weight = (weight*yweight2)/256;
1559                 a+=b*weight;
1560             }
1561             //if(a) a=(palettesize-1)*r/blocksize;
1562             newdata[y*newwidth+x] = (a*blocksize)/r;
1563             py = ey;
1564         }
1565         px = ex;
1566     }
1567     return newdata;
1568 }
1569
1570 #define IMAGE_TYPE_JPEG 0
1571 #define IMAGE_TYPE_LOSSLESS 1
1572
1573 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
1574         double x1,double y1,
1575         double x2,double y2,
1576         double x3,double y3,
1577         double x4,double y4, int type)
1578 {
1579     gfxcolor_t*newpic=0;
1580     
1581     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1582     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1583    
1584     gfxline_t p1,p2,p3,p4,p5;
1585     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1586     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1587     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1588     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1589     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1590
1591     {p1.x = (int)(p1.x*20)/20.0;
1592      p1.y = (int)(p1.y*20)/20.0;
1593      p2.x = (int)(p2.x*20)/20.0;
1594      p2.y = (int)(p2.y*20)/20.0;
1595      p3.x = (int)(p3.x*20)/20.0;
1596      p3.y = (int)(p3.y*20)/20.0;
1597      p4.x = (int)(p4.x*20)/20.0;
1598      p4.y = (int)(p4.y*20)/20.0;
1599      p5.x = (int)(p5.x*20)/20.0;
1600      p5.y = (int)(p5.y*20)/20.0;
1601     }
1602     
1603     float m00,m10,tx;
1604     float m01,m11,ty;
1605     
1606     gfxmatrix_t m;
1607     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1608     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1609     m.tx = p1.x - 0.5;
1610     m.ty = p1.y - 0.5;
1611
1612     gfximage_t img;
1613     img.data = (gfxcolor_t*)data;
1614     img.width = sizex;
1615     img.height = sizey;
1616   
1617     if(type == IMAGE_TYPE_JPEG)
1618         /* TODO: pass image_dpi to device instead */
1619         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1620
1621     dev->fillbitmap(dev, &p1, &img, &m, 0);
1622 }
1623
1624 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
1625         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1626 {
1627     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1628 }
1629
1630 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
1631         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1632 {
1633     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1634 }
1635
1636
1637 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1638                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
1639                                    GBool inlineImg, int mask, int*maskColors,
1640                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1641 {
1642   double x1,y1,x2,y2,x3,y3,x4,y4;
1643   ImageStream *imgStr;
1644   Guchar pixBuf[4];
1645   GfxRGB rgb;
1646   int ncomps = 1;
1647   int bits = 1;
1648   unsigned char* maskbitmap = 0;
1649                                  
1650   if(colorMap) {
1651     ncomps = colorMap->getNumPixelComps();
1652     bits = colorMap->getBits();
1653   }
1654       
1655   if(maskStr) {
1656       int x,y;
1657       unsigned char buf[8];
1658       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1659       if(maskColorMap) {
1660           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1661           imgMaskStr->reset();
1662           unsigned char pal[256];
1663           int n = 1 << colorMap->getBits();
1664           int t;
1665           for(t=0;t<n;t++) {
1666               GfxGray gray;
1667               pixBuf[0] = t;
1668               maskColorMap->getGray(pixBuf, &gray);
1669               pal[t] = colToByte(gray);
1670           }
1671           for (y = 0; y < maskHeight; y++) {
1672               for (x = 0; x < maskWidth; x++) {
1673                   imgMaskStr->getPixel(buf);
1674                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
1675               }
1676           }
1677           delete imgMaskStr;
1678       } else {
1679           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1680           imgMaskStr->reset();
1681           for (y = 0; y < maskHeight; y++) {
1682               for (x = 0; x < maskWidth; x++) {
1683                   imgMaskStr->getPixel(buf);
1684                   buf[0]^=maskInvert;
1685                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1686               }
1687           }
1688           delete imgMaskStr;
1689       }
1690       maskStr->close();
1691   }
1692   
1693   imgStr = new ImageStream(str, width, ncomps,bits);
1694   imgStr->reset();
1695
1696   if(!width || !height || (height<=1 && width<=1))
1697   {
1698       msg("<verbose> Ignoring %d by %d image", width, height);
1699       unsigned char buf[8];
1700       int x,y;
1701       for (y = 0; y < height; ++y)
1702       for (x = 0; x < width; ++x) {
1703           imgStr->getPixel(buf);
1704       }
1705       delete imgStr;
1706       if(maskbitmap)
1707           free(maskbitmap);
1708       return;
1709   }
1710
1711   state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1712   state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1713   state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1714   state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1715
1716   if(!pbminfo && !(str->getKind()==strDCT)) {
1717       if(!type3active) {
1718           msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1719           pbminfo = 1;
1720       }
1721       if(mask)
1722       msg("<verbose> drawing %d by %d masked picture\n", width, height);
1723   }
1724   if(!jpeginfo && (str->getKind()==strDCT)) {
1725       msg("<notice> file contains jpeg pictures");
1726       jpeginfo = 1;
1727   }
1728
1729   if(mask) {
1730       int i,j;
1731       unsigned char buf[8];
1732       int x,y;
1733       unsigned char*pic = new unsigned char[width*height];
1734       gfxcolor_t pal[256];
1735       GfxRGB rgb;
1736       state->getFillRGB(&rgb);
1737
1738       memset(pal,255,sizeof(pal));
1739       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1740       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1741       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1742       pal[0].a = 255;              pal[1].a = 0;
1743     
1744       int numpalette = 2;
1745       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1746       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1747       for (y = 0; y < height; ++y)
1748       for (x = 0; x < width; ++x)
1749       {
1750             imgStr->getPixel(buf);
1751             if(invert) 
1752                 buf[0]=1-buf[0];
1753             pic[width*y+x] = buf[0];
1754       }
1755       
1756       /* the size of the drawn image is added to the identifier
1757          as the same image may require different bitmaps if displayed
1758          at different sizes (due to antialiasing): */
1759       int t,found = -1;
1760       if(type3active) {
1761           unsigned char*pic2 = 0;
1762           numpalette = 16;
1763           
1764           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1765
1766           if(!pic2) {
1767             delete pic;
1768             delete imgStr;
1769             return;
1770           }
1771
1772           width = realwidth;
1773           height = realheight;
1774           free(pic);
1775           pic = pic2;
1776           
1777           /* make a black/white palette */
1778
1779           float r = 255/(numpalette-1);
1780           int t;
1781           for(t=0;t<numpalette;t++) {
1782               pal[t].r = colToByte(rgb.r);
1783               pal[t].g = colToByte(rgb.g);
1784               pal[t].b = colToByte(rgb.b);
1785               pal[t].a = (unsigned char)(t*r);
1786           }
1787       }
1788
1789       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1790       for (y = 0; y < height; ++y) {
1791         for (x = 0; x < width; ++x) {
1792           pic2[width*y+x] = pal[pic[y*width+x]];
1793         }
1794       }
1795       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1796       free(pic2);
1797       free(pic);
1798       delete imgStr;
1799       if(maskbitmap) free(maskbitmap);
1800       return;
1801   }
1802
1803   int x,y;
1804
1805   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1806       gfxcolor_t*pic=new gfxcolor_t[width*height];
1807       for (y = 0; y < height; ++y) {
1808         for (x = 0; x < width; ++x) {
1809           imgStr->getPixel(pixBuf);
1810           colorMap->getRGB(pixBuf, &rgb);
1811           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1812           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1813           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1814           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1815           if(maskbitmap) {
1816               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1817           }
1818         }
1819       }
1820       if(str->getKind()==strDCT)
1821           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1822       else
1823           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1824       delete pic;
1825       delete imgStr;
1826       if(maskbitmap) free(maskbitmap);
1827       return;
1828   } else {
1829       gfxcolor_t*pic=new gfxcolor_t[width*height];
1830       gfxcolor_t pal[256];
1831       int n = 1 << colorMap->getBits();
1832       int t;
1833       for(t=0;t<256;t++) {
1834           pixBuf[0] = t;
1835           colorMap->getRGB(pixBuf, &rgb);
1836
1837           {/*if(maskColors && *maskColors==t) {
1838               msg("<notice> Color %d is transparent", t);
1839               if (imgData->maskColors) {
1840                 *alpha = 0;
1841                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1842                   if (pix[i] < imgData->maskColors[2*i] ||
1843                       pix[i] > imgData->maskColors[2*i+1]) {
1844                     *alpha = 1;
1845                     break;
1846                   }
1847                 }
1848               } else {
1849                 *alpha = 1;
1850               }
1851               if(!*alpha) {
1852                     pal[t].r = 0;
1853                     pal[t].g = 0;
1854                     pal[t].b = 0;
1855                     pal[t].a = 0;
1856               }
1857           } else {*/
1858               pal[t].r = (unsigned char)(colToByte(rgb.r));
1859               pal[t].g = (unsigned char)(colToByte(rgb.g));
1860               pal[t].b = (unsigned char)(colToByte(rgb.b));
1861               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1862           }
1863       }
1864       for (y = 0; y < height; ++y) {
1865         for (x = 0; x < width; ++x) {
1866           imgStr->getPixel(pixBuf);
1867           pic[width*y+x] = pal[pixBuf[0]];
1868           if(maskbitmap) {
1869               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1870           }
1871         }
1872       }
1873       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1874
1875       delete pic;
1876       delete imgStr;
1877       if(maskbitmap) free(maskbitmap);
1878       return;
1879   }
1880 }
1881
1882 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1883                                    int width, int height, GBool invert,
1884                                    GBool inlineImg) 
1885 {
1886     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1887     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1888     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1889 }
1890
1891 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1892                          int width, int height, GfxImageColorMap *colorMap,
1893                          int *maskColors, GBool inlineImg)
1894 {
1895     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
1896             colorMap?"colorMap":"no colorMap", 
1897             maskColors?"maskColors":"no maskColors",
1898             inlineImg);
1899     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
1900             colorMap?"colorMap":"no colorMap", 
1901             maskColors?"maskColors":"no maskColors",
1902             inlineImg);
1903     if(colorMap)
1904         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1905                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1906     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1907 }
1908   
1909 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1910                                int width, int height,
1911                                GfxImageColorMap *colorMap,
1912                                Stream *maskStr, int maskWidth, int maskHeight,
1913                                GBool maskInvert)
1914 {
1915     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1916             colorMap?"colorMap":"no colorMap", 
1917             maskWidth, maskHeight);
1918     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1919             colorMap?"colorMap":"no colorMap", 
1920             maskWidth, maskHeight);
1921     if(colorMap)
1922         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1923                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1924     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1925 }
1926
1927 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1928                                    int width, int height,
1929                                    GfxImageColorMap *colorMap,
1930                                    Stream *maskStr,
1931                                    int maskWidth, int maskHeight,
1932                                    GfxImageColorMap *maskColorMap)
1933 {
1934     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1935             colorMap?"colorMap":"no colorMap", 
1936             maskWidth, maskHeight);
1937     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1938             colorMap?"colorMap":"no colorMap", 
1939             maskWidth, maskHeight);
1940     if(colorMap)
1941         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1942                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1943     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
1944 }
1945
1946 void GFXOutputDev::stroke(GfxState *state) 
1947 {
1948     dbg("stroke");
1949
1950     GfxPath * path = state->getPath();
1951     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
1952     strokeGfxline(state, line, 0);
1953     gfxline_free(line);
1954 }
1955
1956 void GFXOutputDev::fill(GfxState *state) 
1957 {
1958     gfxcolor_t col = getFillColor(state);
1959     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1960
1961     GfxPath * path = state->getPath();
1962     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1963     fillGfxLine(state, line);
1964     gfxline_free(line);
1965 }
1966
1967 void GFXOutputDev::eoFill(GfxState *state) 
1968 {
1969     gfxcolor_t col = getFillColor(state);
1970     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
1971
1972     GfxPath * path = state->getPath();
1973     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
1974     fillGfxLine(state, line);
1975     gfxline_free(line);
1976 }
1977
1978
1979 static const char* dirseparator()
1980 {
1981 #ifdef WIN32
1982     return "\\";
1983 #else
1984     return "/";
1985 #endif
1986 }
1987
1988 void addGlobalFont(const char*filename)
1989 {
1990     fontfile_t f;
1991     memset(&f, 0, sizeof(fontfile_t));
1992     f.filename = filename;
1993     if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
1994         msg("<verbose> Adding font \"%s\".", filename);
1995         fonts[fontnum++] = f;
1996     } else {
1997         msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
1998     }
1999 }
2000
2001 void addGlobalLanguageDir(const char*dir)
2002 {
2003     msg("<notice> Adding %s to language pack directories", dir);
2004
2005     int l;
2006     FILE*fi = 0;
2007     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2008     strcpy(config_file, dir);
2009     strcat(config_file, dirseparator());
2010     strcat(config_file, "add-to-xpdfrc");
2011
2012     fi = fopen(config_file, "rb");
2013     if(!fi) {
2014         msg("<error> Could not open %s", config_file);
2015         return;
2016     }
2017     globalParams->parseFile(new GString(config_file), fi);
2018     fclose(fi);
2019 }
2020
2021 void addGlobalFontDir(const char*dirname)
2022 {
2023 #ifdef HAVE_DIRENT_H
2024     msg("<notice> Adding %s to font directories", dirname);
2025     lastfontdir = strdup(dirname);
2026     DIR*dir = opendir(dirname);
2027     if(!dir) {
2028         msg("<warning> Couldn't open directory %s\n", dirname);
2029         return;
2030     }
2031     struct dirent*ent;
2032     while(1) {
2033         ent = readdir (dir);
2034         if (!ent) 
2035             break;
2036         int l;
2037         char*name = ent->d_name;
2038         char type = 0;
2039         if(!name) continue;
2040         l=strlen(name);
2041         if(l<4)
2042             continue;
2043         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2044             type=1;
2045         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2046             type=3;
2047         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2048             type=2;
2049         if(type)
2050         {
2051             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2052             strcpy(fontname, dirname);
2053             strcat(fontname, dirseparator());
2054             strcat(fontname, name);
2055             addGlobalFont(fontname);
2056         }
2057     }
2058     closedir(dir);
2059 #else
2060     msg("<warning> No dirent.h- unable to add font dir %s", dir);
2061 #endif
2062 }
2063
2064 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2065 {
2066     if(pdfpage < 0)
2067         return;
2068
2069     if(!this->pages) {
2070         this->pagebuflen = 1024;
2071         this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2072         memset(this->pages, -1, this->pagebuflen*sizeof(int));
2073     } else {
2074         while(pdfpage >= this->pagebuflen)
2075         {
2076             int oldlen = this->pagebuflen;
2077             this->pagebuflen+=1024;
2078             this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2079             memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2080         }
2081     }
2082     this->pages[pdfpage] = outputpage;
2083     if(pdfpage>this->pagepos)
2084         this->pagepos = pdfpage;
2085 }
2086
2087 struct BBox
2088 {
2089     double posx,posy;
2090     double width,height;
2091 };
2092
2093 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2094 {
2095     double xMin, yMin, xMax, yMax, x, y;
2096     double tx, ty, w, h;
2097     // transform the bbox
2098     state->transform(bbox[0], bbox[1], &x, &y);
2099     xMin = xMax = x;
2100     yMin = yMax = y;
2101     state->transform(bbox[0], bbox[3], &x, &y);
2102     if (x < xMin) {
2103       xMin = x;
2104     } else if (x > xMax) {
2105       xMax = x;
2106     }
2107     if (y < yMin) {
2108       yMin = y;
2109     } else if (y > yMax) {
2110       yMax = y;
2111     }
2112     state->transform(bbox[2], bbox[1], &x, &y);
2113     if (x < xMin) {
2114       xMin = x;
2115     } else if (x > xMax) {
2116       xMax = x;
2117     }
2118     if (y < yMin) {
2119       yMin = y;
2120     } else if (y > yMax) {
2121       yMax = y;
2122     }
2123     state->transform(bbox[2], bbox[3], &x, &y);
2124     if (x < xMin) {
2125       xMin = x;
2126     } else if (x > xMax) {
2127       xMax = x;
2128     }
2129     if (y < yMin) {
2130       yMin = y;
2131     } else if (y > yMax) {
2132       yMax = y;
2133     }
2134     tx = (int)floor(xMin);
2135     if (tx < 0) {
2136       tx = 0;
2137     } else if (tx > width) {
2138       tx = width;
2139     }
2140     ty = (int)floor(yMin);
2141     if (ty < 0) {
2142       ty = 0;
2143     } else if (ty > height) {
2144       ty = height;
2145     }
2146     w = (int)ceil(xMax) - tx + 1;
2147     if (tx + w > width) {
2148       w = width - tx;
2149     }
2150     if (w < 1) {
2151       w = 1;
2152     }
2153     h = (int)ceil(yMax) - ty + 1;
2154     if (ty + h > height) {
2155       h = height - ty;
2156     }
2157     if (h < 1) {
2158       h = 1;
2159     }
2160     BBox nbbox;
2161     nbbox.posx = xMin;
2162     nbbox.posx = yMin;
2163     nbbox.width = w;
2164     nbbox.height = h;
2165     return nbbox;
2166 }
2167
2168 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2169                                       GfxColorSpace *blendingColorSpace,
2170                                       GBool isolated, GBool knockout,
2171                                       GBool forSoftMask)
2172 {
2173     const char*colormodename = "";
2174     BBox rect = mkBBox(state, bbox, this->width, this->height);
2175
2176     if(blendingColorSpace) {
2177         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2178     }
2179     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);
2180     dbg("using clipping rect %f/%f/%f/%f\n", rect.posx,rect.posy,rect.width,rect.height);
2181     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);
2182     
2183     states[statepos].createsoftmask |= forSoftMask;
2184     states[statepos].transparencygroup = !forSoftMask;
2185     states[statepos].isolated = isolated;
2186
2187     states[statepos].olddevice = this->device;
2188     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2189
2190     gfxdevice_record_init(this->device);
2191     
2192     /*if(!forSoftMask) { ////???
2193         state->setFillOpacity(0.0);
2194     }*/
2195     dbgindent+=2;
2196 }
2197
2198 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2199 {
2200     dbgindent-=2;
2201     dbg("endTransparencyGroup");
2202     msg("<verbose> endTransparencyGroup");
2203
2204     gfxdevice_t*r = this->device;
2205
2206     this->device = states[statepos].olddevice;
2207
2208     if(states[statepos].createsoftmask) {
2209         states[statepos-1].softmaskrecording = r->finish(r);
2210     } else {
2211         states[statepos-1].grouprecording = r->finish(r);
2212     }
2213     
2214     states[statepos].createsoftmask = 0;
2215     states[statepos].transparencygroup = 0;
2216     free(r);
2217 }
2218
2219 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2220 {
2221     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2222                                "colordodge","colorburn","hardlight","softlight","difference",
2223                                "exclusion","hue","saturation","color","luminosity"};
2224
2225     dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2226     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2227    
2228     if(state->getBlendMode() == gfxBlendNormal)
2229         infofeature("transparency groups");
2230     else {
2231         char buffer[80];
2232         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2233         warnfeature(buffer, 0);
2234     }
2235
2236     gfxresult_t*grouprecording = states[statepos].grouprecording;
2237    
2238     if(state->getBlendMode() == gfxBlendNormal) {
2239         gfxdevice_t ops;
2240         gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2241         gfxresult_record_replay(grouprecording, &ops);
2242         ops.finish(&ops);
2243     }
2244     grouprecording->destroy(grouprecording);
2245
2246     states[statepos].grouprecording = 0;
2247 }
2248
2249 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2250 {
2251     /* alpha = 1: retrieve mask values from alpha layer
2252        alpha = 0: retrieve mask values from luminance */
2253     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2254             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2255     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2256             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2257     if(!alpha)
2258         infofeature("soft masks");
2259     else
2260         warnfeature("soft masks from alpha channel",0);
2261     
2262     states[statepos].olddevice = this->device;
2263     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2264     gfxdevice_record_init(this->device);
2265
2266     dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2267     
2268     states[statepos].softmask = 1;
2269     states[statepos].softmask_alpha = alpha;
2270 }
2271
2272 static inline Guchar div255(int x) {
2273   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2274 }
2275
2276 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2277 {
2278     if(c < min) c = min;
2279     if(c > max) c = max;
2280     return c;
2281 }
2282
2283 void GFXOutputDev::clearSoftMask(GfxState *state)
2284 {
2285     if(!states[statepos].softmask)
2286         return;
2287     states[statepos].softmask = 0;
2288     dbg("clearSoftMask statepos=%d", statepos);
2289     msg("<verbose> clearSoftMask");
2290     
2291     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2292         msg("<error> Error in softmask/tgroup ordering");
2293         return;
2294     }
2295   
2296     gfxresult_t*mask = states[statepos].softmaskrecording;
2297     gfxresult_t*below = this->device->finish(this->device);
2298     this->device = states[statepos].olddevice;
2299
2300     /* get outline of all objects below the soft mask */
2301     gfxdevice_t uniondev;
2302     gfxdevice_union_init(&uniondev, 0);
2303     gfxresult_record_replay(below, &uniondev);
2304     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2305     uniondev.finish(&uniondev);
2306
2307     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2308 #if 0 
2309     this->device->startclip(this->device, belowoutline);
2310     gfxresult_record_replay(below, this->device);
2311     gfxresult_record_replay(mask, this->device);
2312     this->device->endclip(this->device);
2313     gfxline_free(belowoutline);
2314 #endif
2315     
2316     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2317     if(width<=0 || height<=0)
2318         return;
2319
2320     gfxdevice_t belowrender;
2321     gfxdevice_render_init(&belowrender);
2322     if(states[statepos+1].isolated) {
2323         belowrender.setparameter(&belowrender, "fillwhite", "1");
2324     }
2325     belowrender.setparameter(&belowrender, "antialize", "2");
2326     belowrender.startpage(&belowrender, width, height);
2327     gfxresult_record_replay(below, &belowrender);
2328     belowrender.endpage(&belowrender);
2329     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2330     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2331     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2332
2333     gfxdevice_t maskrender;
2334     gfxdevice_render_init(&maskrender);
2335     maskrender.startpage(&maskrender, width, height);
2336     gfxresult_record_replay(mask, &maskrender);
2337     maskrender.endpage(&maskrender);
2338     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2339     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2340
2341     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2342         msg("<fatal> Internal error in mask drawing");
2343         return;
2344     }
2345
2346     int y,x;
2347     for(y=0;y<height;y++) {
2348         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2349         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2350         for(x=0;x<width;x++) {
2351             int alpha;
2352             if(states[statepos].softmask_alpha) {
2353                 alpha = l1->a;
2354             } else {
2355                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2356             }
2357
2358             l2->a = div255(alpha*l2->a);
2359
2360             /* DON'T premultiply alpha- this is done by fillbitmap,
2361                depending on the output device */
2362             //l2->r = div255(alpha*l2->r);
2363             //l2->g = div255(alpha*l2->g);
2364             //l2->b = div255(alpha*l2->b);
2365
2366             l1++;
2367             l2++;
2368         }
2369     }
2370     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2371
2372     gfxmatrix_t matrix;
2373     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2374     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2375
2376     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2377
2378     mask->destroy(mask);
2379     below->destroy(below);
2380     maskresult->destroy(maskresult);
2381     belowresult->destroy(belowresult);
2382     states[statepos].softmaskrecording = 0;
2383 }
2384   
2385 //class MemCheck
2386 //{
2387 //    public: ~MemCheck()
2388 //    {
2389 //        delete globalParams;globalParams=0;
2390 //        Object::memCheck(stderr);
2391 //        gMemReport(stderr);
2392 //    }
2393 //} myMemCheck;
2394