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