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