added support for masks of higher resolutions than the bitmaps they mask
[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 (GfxOutputDev)");
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     if(getLogLevel() >= LOGLEVEL_TRACE)  {
1211         dump_outline(points);
1212     }
1213
1214     LinkAction*action=link->getAction();
1215     char buf[128];
1216     char*s = 0;
1217     const char*type = "-?-";
1218     char*named = 0;
1219     int page = -1;
1220     msg("<trace> drawlink action=%d\n", action->getKind());
1221     switch(action->getKind())
1222     {
1223         case actionGoTo: {
1224             type = "GoTo";
1225             LinkGoTo *ha=(LinkGoTo *)link->getAction();
1226             LinkDest *dest=NULL;
1227             if (ha->getDest()==NULL) 
1228                 dest=catalog->findDest(ha->getNamedDest());
1229             else dest=ha->getDest();
1230             if (dest){ 
1231               if (dest->isPageRef()){
1232                 Ref pageref=dest->getPageRef();
1233                 page=catalog->findPage(pageref.num,pageref.gen);
1234               }
1235               else  page=dest->getPageNum();
1236               sprintf(buf, "%d", page);
1237               s = strdup(buf);
1238             }
1239         }
1240         break;
1241         case actionGoToR: {
1242             type = "GoToR";
1243             LinkGoToR*l = (LinkGoToR*)action;
1244             GString*g = l->getFileName();
1245             if(g)
1246              s = strdup(g->getCString());
1247             if(!s) {
1248                 /* if the GoToR link has no filename, then
1249                    try to find a refernce in the *local*
1250                    file */
1251                 GString*g = l->getNamedDest();
1252                 if(g)
1253                  s = strdup(g->getCString());
1254             }
1255         }
1256         break;
1257         case actionNamed: {
1258             type = "Named";
1259             LinkNamed*l = (LinkNamed*)action;
1260             GString*name = l->getName();
1261             if(name) {
1262                 s = strdup(name->lowerCase()->getCString());
1263                 named = name->getCString();
1264                 if(!strchr(s,':')) 
1265                 {
1266                     if(strstr(s, "next") || strstr(s, "forward"))
1267                     {
1268                         page = currentpage + 1;
1269                     }
1270                     else if(strstr(s, "prev") || strstr(s, "back"))
1271                     {
1272                         page = currentpage - 1;
1273                     }
1274                     else if(strstr(s, "last") || strstr(s, "end"))
1275                     {
1276                         if(pages && pagepos>0)
1277                             page = pages[pagepos-1];
1278                     }
1279                     else if(strstr(s, "first") || strstr(s, "top"))
1280                     {
1281                         page = 1;
1282                     }
1283                 }
1284             }
1285         }
1286         break;
1287         case actionLaunch: {
1288             type = "Launch";
1289             LinkLaunch*l = (LinkLaunch*)action;
1290             GString * str = new GString(l->getFileName());
1291             GString * params = l->getParams();
1292             if(params)
1293                 str->append(params);
1294             s = strdup(str->getCString());
1295             delete str;
1296         }
1297         break;
1298         case actionURI: {
1299             char*url = 0;
1300             type = "URI";
1301             LinkURI*l = (LinkURI*)action;
1302             GString*g = l->getURI();
1303             if(g) {
1304              url = g->getCString();
1305              s = strdup(url);
1306             }
1307         }
1308         break;
1309         case actionUnknown: {
1310             type = "Unknown";
1311             LinkUnknown*l = (LinkUnknown*)action;
1312             s = strdup("");
1313         }
1314         break;
1315         default: {
1316             msg("<error> Unknown link type!\n");
1317             break;
1318         }
1319     }
1320
1321     if(!s) s = strdup("-?-");
1322     
1323     msg("<trace> drawlink s=%s\n", s);
1324
1325     if(!linkinfo && (page || s))
1326     {
1327         msg("<notice> File contains links");
1328         linkinfo = 1;
1329     }
1330     
1331     if(page>0)
1332     {
1333         int t;
1334         int lpage = -1;
1335         for(t=1;t<=pagepos;t++) {
1336             if(pages[t]==page) {
1337                 lpage = t;
1338                 break;
1339             }
1340         }
1341         if(lpage<0) {
1342             lpage = page;
1343         }
1344         char buf[80];
1345         sprintf(buf, "page%d", lpage);
1346         device->drawlink(device, points, buf);
1347     }
1348     else if(s)
1349     {
1350         device->drawlink(device, points, s);
1351     }
1352
1353     msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
1354     free(s);s=0;
1355 }
1356
1357 void GFXOutputDev::saveState(GfxState *state) {
1358     dbg("saveState");dbgindent+=2;
1359
1360     msg("<trace> saveState\n");
1361     updateAll(state);
1362     if(statepos>=64) {
1363       msg("<error> Too many nested states in pdf.");
1364       return;
1365     }
1366     statepos ++;
1367     states[statepos].createsoftmask = states[statepos-1].createsoftmask;
1368     states[statepos].transparencygroup = states[statepos-1].transparencygroup;
1369     states[statepos].clipping = 0;
1370 };
1371
1372 void GFXOutputDev::restoreState(GfxState *state) {
1373   dbgindent-=2; dbg("restoreState");
1374
1375   if(statepos==0) {
1376       msg("<error> Invalid restoreState");
1377       return;
1378   }
1379   msg("<trace> restoreState%s%s", states[statepos].softmask?" (end softmask)":"",
1380                                   states[statepos].clipping?" (end clipping)":"");
1381   if(states[statepos].softmask) {
1382       clearSoftMask(state);
1383   }
1384   updateAll(state);
1385   while(states[statepos].clipping) {
1386       device->endclip(device);
1387       states[statepos].clipping--;
1388   }
1389   statepos--;
1390 }
1391
1392 void GFXOutputDev::updateLineWidth(GfxState *state)
1393 {
1394     double width = state->getTransformedLineWidth();
1395     //swfoutput_setlinewidth(&device, width);
1396 }
1397
1398 void GFXOutputDev::updateLineCap(GfxState *state)
1399 {
1400     int c = state->getLineCap();
1401 }
1402
1403 void GFXOutputDev::updateLineJoin(GfxState *state)
1404 {
1405     int j = state->getLineJoin();
1406 }
1407
1408 void GFXOutputDev::updateFillColor(GfxState *state) 
1409 {
1410     GfxRGB rgb;
1411     double opaq = state->getFillOpacity();
1412     state->getFillRGB(&rgb);
1413 }
1414 void GFXOutputDev::updateFillOpacity(GfxState *state)
1415 {
1416     GfxRGB rgb;
1417     double opaq = state->getFillOpacity();
1418     state->getFillRGB(&rgb);
1419     dbg("update fillopaq %f", opaq);
1420 }
1421 void GFXOutputDev::updateStrokeOpacity(GfxState *state)
1422 {
1423     double opaq = state->getFillOpacity();
1424     dbg("update strokeopaq %f", opaq);
1425 }
1426 void GFXOutputDev::updateFillOverprint(GfxState *state)
1427 {
1428     double opaq = state->getFillOverprint();
1429     dbg("update filloverprint %f", opaq);
1430 }
1431 void GFXOutputDev::updateStrokeOverprint(GfxState *state)
1432 {
1433     double opaq = state->getStrokeOverprint();
1434     dbg("update strokeoverprint %f", opaq);
1435 }
1436 void GFXOutputDev::updateTransfer(GfxState *state)
1437 {
1438     dbg("update transfer");
1439 }
1440
1441
1442 void GFXOutputDev::updateStrokeColor(GfxState *state) 
1443 {
1444     GfxRGB rgb;
1445     double opaq = state->getStrokeOpacity();
1446     state->getStrokeRGB(&rgb);
1447 }
1448
1449
1450 gfxfont_t* createGfxFont(GfxFont*xpdffont, FontInfo*src, double config_fontquality)
1451 {
1452     gfxfont_t*font = (gfxfont_t*)malloc(sizeof(gfxfont_t));
1453     memset(font, 0, sizeof(gfxfont_t));
1454
1455     font->glyphs = (gfxglyph_t*)malloc(sizeof(gfxglyph_t)*src->num_glyphs);
1456     memset(font->glyphs, 0, sizeof(gfxglyph_t)*src->num_glyphs);
1457     font->id = strdup(getFontID(xpdffont));
1458     int t;
1459     
1460     double quality = (INTERNAL_FONT_SIZE * 200 / config_fontquality) / src->max_size;
1461     double scale = 1;
1462     //printf("%d glyphs\n", font->num_glyphs);
1463     font->num_glyphs = 0;
1464     for(t=0;t<src->num_glyphs;t++) {
1465         if(src->glyphs[t]) {
1466             SplashPath*path = src->glyphs[t]->path;
1467             int len = path?path->getLength():0;
1468             //printf("glyph %d) %08x (%d line segments)\n", t, path, len);
1469             gfxglyph_t*glyph = &font->glyphs[font->num_glyphs];
1470             src->glyphs[t]->glyphid = font->num_glyphs;
1471             glyph->unicode = src->glyphs[t]->unicode;
1472             if(glyph->unicode >= font->max_unicode)
1473                 font->max_unicode = glyph->unicode+1;
1474             gfxdrawer_t drawer;
1475             gfxdrawer_target_gfxline(&drawer);
1476             int s;
1477             int count = 0;
1478             double xmax = 0;
1479             for(s=0;s<len;s++) {
1480                 Guchar f;
1481                 double x, y;
1482                 path->getPoint(s, &x, &y, &f);
1483                 if(!s || x > xmax)
1484                     xmax = x;
1485                 if(f&splashPathFirst) {
1486                     drawer.moveTo(&drawer, x*scale, y*scale);
1487                 }
1488                 if(f&splashPathCurve) {
1489                     double x2,y2;
1490                     path->getPoint(++s, &x2, &y2, &f);
1491                     if(f&splashPathCurve) {
1492                         double x3,y3;
1493                         path->getPoint(++s, &x3, &y3, &f);
1494                         gfxdraw_cubicTo(&drawer, x*scale, y*scale, x2*scale, y2*scale, x3*scale, y3*scale, quality);
1495                     } else {
1496                         drawer.splineTo(&drawer, x*scale, y*scale, x2*scale, y2*scale);
1497                     }
1498                 } else {
1499                     drawer.lineTo(&drawer, x*scale, y*scale);
1500                 }
1501              //   printf("%f %f %s %s\n", x, y, (f&splashPathCurve)?"curve":"",
1502              //                           (f&splashPathFirst)?"first":"",
1503              //                           (f&splashPathLast)?"last":"");
1504             }
1505             glyph->line = (gfxline_t*)drawer.result(&drawer);
1506             glyph->advance = xmax*scale; // we don't know the real advance value, so this'll have to do
1507             font->num_glyphs++;
1508         }
1509     }
1510     font->unicode2glyph = (int*)malloc(sizeof(int)*font->max_unicode);
1511     memset(font->unicode2glyph, -1, sizeof(int)*font->max_unicode);
1512     for(t=0;t<font->num_glyphs;t++) {
1513         if(font->glyphs[t].unicode>0 && font->glyphs[t].unicode<font->max_unicode) {
1514             font->unicode2glyph[font->glyphs[t].unicode] = t;
1515         }
1516
1517     }
1518     msg("<trace> %d glyphs.", t, font->num_glyphs);
1519     return font;
1520 }
1521
1522 void GFXOutputDev::updateFont(GfxState *state) 
1523 {
1524     GfxFont* gfxFont = state->getFont();
1525     if (!gfxFont) {
1526         return; 
1527     }  
1528     char*id = getFontID(gfxFont);
1529     msg("<verbose> Updating font to %s", id);
1530     if(gfxFont->getType() == fontType3) {
1531         infofeature("Type3 fonts");
1532         if(!config_extrafontdata) {
1533             return;
1534         }
1535     }
1536     if(!id) {
1537         msg("<error> Internal Error: FontID is null");
1538         return; 
1539     }
1540
1541     this->current_fontinfo = this->info->getFont(id);
1542     if(!this->current_fontinfo) {
1543         msg("<error> Internal Error: no fontinfo for font %s\n", id);
1544         return;
1545     }
1546     if(!this->current_fontinfo->seen) {
1547         dumpFontInfo("<verbose>", gfxFont);
1548     }
1549     
1550     gfxfont_t*font = gfxfontlist_findfont(this->gfxfontlist,id);
1551     if(!font) {
1552         font = createGfxFont(gfxFont, current_fontinfo, this->config_fontquality);
1553         font->id = strdup(id);
1554         this->gfxfontlist = gfxfontlist_addfont(this->gfxfontlist, font);
1555         device->addfont(device, font);
1556     }
1557     current_gfxfont = font;
1558     free(id);
1559
1560     updateFontMatrix(state);
1561 }
1562
1563 #define SQR(x) ((x)*(x))
1564
1565 unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
1566 {
1567     if((newwidth<2 || newheight<2) ||
1568        (width<=newwidth || height<=newheight))
1569         return 0;
1570     unsigned char*newdata;
1571     int x,y;
1572     newdata= (unsigned char*)malloc(newwidth*newheight);
1573     double fx = (double)(width)/newwidth;
1574     double fy = (double)(height)/newheight;
1575     double px = 0;
1576     int blocksize = (int)(8192/(fx*fy));
1577     int r = 8192*256/palettesize;
1578     for(x=0;x<newwidth;x++) {
1579         double ex = px + fx;
1580         int fromx = (int)px;
1581         int tox = (int)ex;
1582         int xweight1 = (int)(((fromx+1)-px)*256);
1583         int xweight2 = (int)((ex-tox)*256);
1584         double py =0;
1585         for(y=0;y<newheight;y++) {
1586             double ey = py + fy;
1587             int fromy = (int)py;
1588             int toy = (int)ey;
1589             int yweight1 = (int)(((fromy+1)-py)*256);
1590             int yweight2 = (int)((ey-toy)*256);
1591             int a = 0;
1592             int xx,yy;
1593             for(xx=fromx;xx<=tox;xx++)
1594             for(yy=fromy;yy<=toy;yy++) {
1595                 int b = 1-data[width*yy+xx];
1596                 int weight=256;
1597                 if(xx==fromx) weight = (weight*xweight1)/256;
1598                 if(xx==tox) weight = (weight*xweight2)/256;
1599                 if(yy==fromy) weight = (weight*yweight1)/256;
1600                 if(yy==toy) weight = (weight*yweight2)/256;
1601                 a+=b*weight;
1602             }
1603             //if(a) a=(palettesize-1)*r/blocksize;
1604             newdata[y*newwidth+x] = (a*blocksize)/r;
1605             py = ey;
1606         }
1607         px = ex;
1608     }
1609     return newdata;
1610 }
1611
1612 #define IMAGE_TYPE_JPEG 0
1613 #define IMAGE_TYPE_LOSSLESS 1
1614
1615 static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey, 
1616         double x1,double y1,
1617         double x2,double y2,
1618         double x3,double y3,
1619         double x4,double y4, int type)
1620 {
1621     gfxcolor_t*newpic=0;
1622     
1623     double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
1624     double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
1625    
1626     gfxline_t p1,p2,p3,p4,p5;
1627     p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
1628     p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
1629     p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
1630     p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
1631     p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
1632
1633     {p1.x = (int)(p1.x*20)/20.0;
1634      p1.y = (int)(p1.y*20)/20.0;
1635      p2.x = (int)(p2.x*20)/20.0;
1636      p2.y = (int)(p2.y*20)/20.0;
1637      p3.x = (int)(p3.x*20)/20.0;
1638      p3.y = (int)(p3.y*20)/20.0;
1639      p4.x = (int)(p4.x*20)/20.0;
1640      p4.y = (int)(p4.y*20)/20.0;
1641      p5.x = (int)(p5.x*20)/20.0;
1642      p5.y = (int)(p5.y*20)/20.0;
1643     }
1644     
1645     gfxmatrix_t m;
1646     m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
1647     m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
1648     m.tx = p1.x - 0.5;
1649     m.ty = p1.y - 0.5;
1650
1651     gfximage_t img;
1652     img.data = (gfxcolor_t*)data;
1653     img.width = sizex;
1654     img.height = sizey;
1655   
1656     if(type == IMAGE_TYPE_JPEG)
1657         /* TODO: pass image_dpi to device instead */
1658         dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
1659
1660     dev->fillbitmap(dev, &p1, &img, &m, 0);
1661 }
1662
1663 void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
1664         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1665 {
1666     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
1667 }
1668
1669 void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey, 
1670         double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
1671 {
1672     drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
1673 }
1674
1675
1676 void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
1677                                    int width, int height, GfxImageColorMap*colorMap, GBool invert,
1678                                    GBool inlineImg, int mask, int*maskColors,
1679                                    Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
1680 {
1681   double x1,y1,x2,y2,x3,y3,x4,y4;
1682   ImageStream *imgStr;
1683   Guchar pixBuf[4];
1684   GfxRGB rgb;
1685   int ncomps = 1;
1686   int bits = 1;
1687   unsigned char* maskbitmap = 0;
1688                                  
1689   if(colorMap) {
1690     ncomps = colorMap->getNumPixelComps();
1691     bits = colorMap->getBits();
1692   }
1693       
1694   if(maskStr) {
1695       int x,y;
1696       unsigned char buf[8];
1697       maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
1698       if(maskColorMap) {
1699           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
1700           imgMaskStr->reset();
1701           unsigned char pal[256];
1702           int n = 1 << colorMap->getBits();
1703           int t;
1704           for(t=0;t<n;t++) {
1705               GfxGray gray;
1706               pixBuf[0] = t;
1707               maskColorMap->getGray(pixBuf, &gray);
1708               pal[t] = colToByte(gray);
1709           }
1710           for (y = 0; y < maskHeight; y++) {
1711               for (x = 0; x < maskWidth; x++) {
1712                   imgMaskStr->getPixel(buf);
1713                   maskbitmap[y*maskWidth+x] = pal[buf[0]];
1714               }
1715           }
1716           delete imgMaskStr;
1717       } else {
1718           ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
1719           imgMaskStr->reset();
1720           for (y = 0; y < maskHeight; y++) {
1721               for (x = 0; x < maskWidth; x++) {
1722                   imgMaskStr->getPixel(buf);
1723                   buf[0]^=maskInvert;
1724                   maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
1725               }
1726           }
1727           delete imgMaskStr;
1728       }
1729       maskStr->close();
1730   }
1731   
1732   imgStr = new ImageStream(str, width, ncomps,bits);
1733   imgStr->reset();
1734
1735   if(!width || !height || (height<=1 && width<=1 && maskWidth<=1 && maskHeight<=1))
1736   {
1737       msg("<verbose> Ignoring %d by %d image", width, height);
1738       unsigned char buf[8];
1739       int x,y;
1740       for (y = 0; y < height; ++y)
1741       for (x = 0; x < width; ++x) {
1742           imgStr->getPixel(buf);
1743       }
1744       delete imgStr;
1745       if(maskbitmap)
1746           free(maskbitmap);
1747       return;
1748   }
1749
1750   state->transform(0, 1, &x1, &y1); x1 += user_movex + clipmovex; y1 += user_movey + clipmovey;
1751   state->transform(0, 0, &x2, &y2); x2 += user_movex + clipmovex; y2 += user_movey + clipmovey;
1752   state->transform(1, 0, &x3, &y3); x3 += user_movex + clipmovex; y3 += user_movey + clipmovey;
1753   state->transform(1, 1, &x4, &y4); x4 += user_movex + clipmovex; y4 += user_movey + clipmovey;
1754
1755   if(!pbminfo && !(str->getKind()==strDCT)) {
1756       if(!type3active) {
1757           msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
1758           pbminfo = 1;
1759       }
1760       if(mask)
1761       msg("<verbose> drawing %d by %d masked picture\n", width, height);
1762   }
1763   if(!jpeginfo && (str->getKind()==strDCT)) {
1764       msg("<notice> file contains jpeg pictures");
1765       jpeginfo = 1;
1766   }
1767
1768   if(mask) {
1769       unsigned char buf[8];
1770       int x,y;
1771       unsigned char*pic = new unsigned char[width*height];
1772       gfxcolor_t pal[256];
1773       GfxRGB rgb;
1774       state->getFillRGB(&rgb);
1775
1776       memset(pal,255,sizeof(pal));
1777       pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
1778       pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
1779       pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
1780       pal[0].a = 255;              pal[1].a = 0;
1781     
1782       int numpalette = 2;
1783       int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
1784       int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
1785       for (y = 0; y < height; ++y)
1786       for (x = 0; x < width; ++x)
1787       {
1788             imgStr->getPixel(buf);
1789             if(invert) 
1790                 buf[0]=1-buf[0];
1791             pic[width*y+x] = buf[0];
1792       }
1793       
1794       /* the size of the drawn image is added to the identifier
1795          as the same image may require different bitmaps if displayed
1796          at different sizes (due to antialiasing): */
1797       int found = -1;
1798       if(type3active) {
1799           unsigned char*pic2 = 0;
1800           numpalette = 16;
1801           
1802           pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
1803
1804           if(!pic2) {
1805             delete[] pic;
1806             delete imgStr;
1807             return;
1808           }
1809
1810           width = realwidth;
1811           height = realheight;
1812           free(pic);
1813           pic = pic2;
1814           
1815           /* make a black/white palette */
1816
1817           float r = 255./(float)(numpalette-1);
1818           int t;
1819           for(t=0;t<numpalette;t++) {
1820               pal[t].r = colToByte(rgb.r);
1821               pal[t].g = colToByte(rgb.g);
1822               pal[t].b = colToByte(rgb.b);
1823               pal[t].a = (unsigned char)(t*r);
1824           }
1825       }
1826
1827       gfxcolor_t*pic2 = new gfxcolor_t[width*height];
1828       for (y = 0; y < height; ++y) {
1829         for (x = 0; x < width; ++x) {
1830           pic2[width*y+x] = pal[pic[y*width+x]];
1831         }
1832       }
1833       drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1834       free(pic2);
1835       free(pic);
1836       delete imgStr;
1837       if(maskbitmap) free(maskbitmap);
1838       return;
1839   }
1840
1841   int x,y;
1842
1843   if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
1844       gfxcolor_t*pic=new gfxcolor_t[width*height];
1845       for (y = 0; y < height; ++y) {
1846         for (x = 0; x < width; ++x) {
1847           imgStr->getPixel(pixBuf);
1848           colorMap->getRGB(pixBuf, &rgb);
1849           pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
1850           pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
1851           pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
1852           pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
1853           if(maskbitmap) {
1854               pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1855           }
1856         }
1857       }
1858       if(str->getKind()==strDCT)
1859           drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1860       else
1861           drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1862       delete[] pic;
1863       delete imgStr;
1864       if(maskbitmap) free(maskbitmap);
1865       return;
1866   } else {
1867       gfxcolor_t*pic=new gfxcolor_t[width*height];
1868       gfxcolor_t pal[256];
1869       int n = 1 << colorMap->getBits();
1870       int t;
1871       for(t=0;t<256;t++) {
1872           pixBuf[0] = t;
1873           colorMap->getRGB(pixBuf, &rgb);
1874
1875           {/*if(maskColors && *maskColors==t) {
1876               msg("<notice> Color %d is transparent", t);
1877               if (imgData->maskColors) {
1878                 *alpha = 0;
1879                 for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
1880                   if (pix[i] < imgData->maskColors[2*i] ||
1881                       pix[i] > imgData->maskColors[2*i+1]) {
1882                     *alpha = 1;
1883                     break;
1884                   }
1885                 }
1886               } else {
1887                 *alpha = 1;
1888               }
1889               if(!*alpha) {
1890                     pal[t].r = 0;
1891                     pal[t].g = 0;
1892                     pal[t].b = 0;
1893                     pal[t].a = 0;
1894               }
1895           } else {*/
1896               pal[t].r = (unsigned char)(colToByte(rgb.r));
1897               pal[t].g = (unsigned char)(colToByte(rgb.g));
1898               pal[t].b = (unsigned char)(colToByte(rgb.b));
1899               pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
1900           }
1901       }
1902       for (y = 0; y < height; ++y) {
1903         for (x = 0; x < width; ++x) {
1904           imgStr->getPixel(pixBuf);
1905           pic[width*y+x] = pal[pixBuf[0]];
1906         }
1907       }
1908       if(maskbitmap) {
1909           if(maskWidth < width && maskHeight < height) {
1910               for(y = 0; y < height; y++) {
1911                   for (x = 0; x < width; x++) {
1912                       pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
1913                   }
1914               }
1915           } else {
1916               msg("<verbose> resampling %dx%d to mask size (%dx%d)", width, height, maskWidth, maskHeight);
1917               gfxcolor_t*newpic=new gfxcolor_t[maskWidth*maskHeight];
1918               double dx = width / maskWidth;
1919               double dy = height / maskHeight;
1920               double yy = 0;
1921               for(y = 0; y < maskHeight; y++) {
1922                   double xx = 0;
1923                   for (x = 0; x < maskWidth; x++) {
1924                       newpic[maskWidth*y+x] = pic[int(yy)*width+int(xx)];
1925                       newpic[maskWidth*y+x].a = maskbitmap[maskWidth*y+x];
1926                       xx += dx;
1927                   }
1928                   yy += dy;
1929               }
1930               delete[] pic;
1931               pic = newpic;
1932               width = maskWidth;
1933               height = maskHeight;
1934           }
1935       }
1936       drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
1937
1938       delete[] pic;
1939       delete imgStr;
1940       if(maskbitmap) free(maskbitmap);
1941       return;
1942   }
1943 }
1944
1945 void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1946                                    int width, int height, GBool invert,
1947                                    GBool inlineImg) 
1948 {
1949     dbg("drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1950     msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
1951     drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
1952 }
1953
1954 void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1955                          int width, int height, GfxImageColorMap *colorMap,
1956                          int *maskColors, GBool inlineImg)
1957 {
1958     dbg("drawImage %dx%d, %s, %s, inline=%d", width, height, 
1959             colorMap?"colorMap":"no colorMap", 
1960             maskColors?"maskColors":"no maskColors",
1961             inlineImg);
1962     msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height, 
1963             colorMap?"colorMap":"no colorMap", 
1964             maskColors?"maskColors":"no maskColors",
1965             inlineImg);
1966     if(colorMap)
1967         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1968                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1969     drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
1970 }
1971   
1972 void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
1973                                int width, int height,
1974                                GfxImageColorMap *colorMap,
1975                                Stream *maskStr, int maskWidth, int maskHeight,
1976                                GBool maskInvert)
1977 {
1978     dbg("drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1979             colorMap?"colorMap":"no colorMap", 
1980             maskWidth, maskHeight);
1981     msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1982             colorMap?"colorMap":"no colorMap", 
1983             maskWidth, maskHeight);
1984     if(colorMap)
1985         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
1986                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
1987     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
1988 }
1989
1990 void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
1991                                    int width, int height,
1992                                    GfxImageColorMap *colorMap,
1993                                    Stream *maskStr,
1994                                    int maskWidth, int maskHeight,
1995                                    GfxImageColorMap *maskColorMap)
1996 {
1997     dbg("drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
1998             colorMap?"colorMap":"no colorMap", 
1999             maskWidth, maskHeight);
2000     msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height, 
2001             colorMap?"colorMap":"no colorMap", 
2002             maskWidth, maskHeight);
2003     if(colorMap)
2004         msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
2005                 colorMap->getBits(),colorMap->getColorSpace()->getMode());
2006     drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
2007 }
2008
2009 void GFXOutputDev::stroke(GfxState *state) 
2010 {
2011     dbg("stroke");
2012
2013     GfxPath * path = state->getPath();
2014     gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex + clipmovex, user_movey + clipmovey);
2015     strokeGfxline(state, line, 0);
2016     gfxline_free(line);
2017 }
2018
2019 void GFXOutputDev::fill(GfxState *state) 
2020 {
2021     gfxcolor_t col = getFillColor(state);
2022     dbg("fill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2023
2024     GfxPath * path = state->getPath();
2025     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2026     fillGfxLine(state, line);
2027     gfxline_free(line);
2028 }
2029
2030 void GFXOutputDev::eoFill(GfxState *state) 
2031 {
2032     gfxcolor_t col = getFillColor(state);
2033     dbg("eofill %02x%02x%02x%02x",col.r,col.g,col.b,col.a);
2034
2035     GfxPath * path = state->getPath();
2036     gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex + clipmovex, user_movey + clipmovey);
2037     fillGfxLine(state, line);
2038     gfxline_free(line);
2039 }
2040
2041
2042 static const char* dirseparator()
2043 {
2044 #ifdef WIN32
2045     return "\\";
2046 #else
2047     return "/";
2048 #endif
2049 }
2050
2051 void addGlobalFont(const char*filename)
2052 {
2053     fontfile_t* f = (fontfile_t*)malloc(sizeof(fontfile_t));
2054     memset(f, 0, sizeof(fontfile_t));
2055     f->filename = filename;
2056     int len = strlen(filename);
2057     char*r1 = strrchr(filename, '/');
2058     char*r2 = strrchr(filename, '\\');
2059     if(r2>r1)
2060         r1 = r2;
2061     if(r1) {
2062         len = strlen(r1+1);
2063     }
2064     f->len = len;
2065
2066     msg("<notice> Adding font \"%s\".", filename);
2067     if(global_fonts_next) {
2068         global_fonts_next->next = f;
2069         global_fonts_next = global_fonts_next->next;
2070     } else {
2071         global_fonts_next = global_fonts = f;
2072     }
2073 }
2074
2075 void addGlobalLanguageDir(const char*dir)
2076 {
2077     msg("<notice> Adding %s to language pack directories", dir);
2078
2079     FILE*fi = 0;
2080     char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
2081     strcpy(config_file, dir);
2082     strcat(config_file, dirseparator());
2083     strcat(config_file, "add-to-xpdfrc");
2084
2085     fi = fopen(config_file, "rb");
2086     if(!fi) {
2087         msg("<error> Could not open %s", config_file);
2088         return;
2089     }
2090     globalParams->parseFile(new GString(config_file), fi);
2091     fclose(fi);
2092 }
2093
2094 void addGlobalFontDir(const char*dirname)
2095 {
2096 #ifdef HAVE_DIRENT_H
2097     msg("<notice> Adding %s to font directories", dirname);
2098     lastfontdir = strdup(dirname);
2099     DIR*dir = opendir(dirname);
2100     if(!dir) {
2101         msg("<warning> Couldn't open directory %s\n", dirname);
2102         return;
2103     }
2104     struct dirent*ent;
2105     while(1) {
2106         ent = readdir (dir);
2107         if (!ent) 
2108             break;
2109         int l;
2110         char*name = ent->d_name;
2111         char type = 0;
2112         if(!name) continue;
2113         l=strlen(name);
2114         if(l<4)
2115             continue;
2116         if(!strncasecmp(&name[l-4], ".pfa", 4)) 
2117             type=1;
2118         if(!strncasecmp(&name[l-4], ".pfb", 4)) 
2119             type=3;
2120         if(!strncasecmp(&name[l-4], ".ttf", 4)) 
2121             type=2;
2122         if(type) {
2123             char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
2124             strcpy(fontname, dirname);
2125             strcat(fontname, dirseparator());
2126             strcat(fontname, name);
2127             addGlobalFont(fontname);
2128         }
2129     }
2130     closedir(dir);
2131 #else
2132     msg("<warning> No dirent.h- unable to add font dir %s", dirname);
2133 #endif
2134 }
2135
2136 void GFXOutputDev::preparePage(int pdfpage, int outputpage)
2137 {
2138     if(pdfpage < 0)
2139         return;
2140
2141     if(!this->pages) {
2142         this->pagebuflen = 1024;
2143         this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
2144         memset(this->pages, -1, this->pagebuflen*sizeof(int));
2145     } else {
2146         while(pdfpage >= this->pagebuflen)
2147         {
2148             int oldlen = this->pagebuflen;
2149             this->pagebuflen+=1024;
2150             this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
2151             memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
2152         }
2153     }
2154     this->pages[pdfpage] = outputpage;
2155     if(pdfpage>this->pagepos)
2156         this->pagepos = pdfpage;
2157 }
2158
2159 struct BBox
2160 {
2161     double posx,posy;
2162     double width,height;
2163 };
2164
2165 BBox mkBBox(GfxState*state, double*bbox, double width, double height)
2166 {
2167     double xMin, yMin, xMax, yMax, x, y;
2168     double tx, ty, w, h;
2169     // transform the bbox
2170     state->transform(bbox[0], bbox[1], &x, &y);
2171     xMin = xMax = x;
2172     yMin = yMax = y;
2173     state->transform(bbox[0], bbox[3], &x, &y);
2174     if (x < xMin) {
2175       xMin = x;
2176     } else if (x > xMax) {
2177       xMax = x;
2178     }
2179     if (y < yMin) {
2180       yMin = y;
2181     } else if (y > yMax) {
2182       yMax = y;
2183     }
2184     state->transform(bbox[2], bbox[1], &x, &y);
2185     if (x < xMin) {
2186       xMin = x;
2187     } else if (x > xMax) {
2188       xMax = x;
2189     }
2190     if (y < yMin) {
2191       yMin = y;
2192     } else if (y > yMax) {
2193       yMax = y;
2194     }
2195     state->transform(bbox[2], bbox[3], &x, &y);
2196     if (x < xMin) {
2197       xMin = x;
2198     } else if (x > xMax) {
2199       xMax = x;
2200     }
2201     if (y < yMin) {
2202       yMin = y;
2203     } else if (y > yMax) {
2204       yMax = y;
2205     }
2206     tx = (int)floor(xMin);
2207     if (tx < 0) {
2208       tx = 0;
2209     } else if (tx > width) {
2210       tx = width;
2211     }
2212     ty = (int)floor(yMin);
2213     if (ty < 0) {
2214       ty = 0;
2215     } else if (ty > height) {
2216       ty = height;
2217     }
2218     w = (int)ceil(xMax) - tx + 1;
2219     if (tx + w > width) {
2220       w = width - tx;
2221     }
2222     if (w < 1) {
2223       w = 1;
2224     }
2225     h = (int)ceil(yMax) - ty + 1;
2226     if (ty + h > height) {
2227       h = height - ty;
2228     }
2229     if (h < 1) {
2230       h = 1;
2231     }
2232     BBox nbbox;
2233     nbbox.posx = xMin;
2234     nbbox.posx = yMin;
2235     nbbox.width = w;
2236     nbbox.height = h;
2237     return nbbox;
2238 }
2239
2240 void GFXOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2241                                       GfxColorSpace *blendingColorSpace,
2242                                       GBool isolated, GBool knockout,
2243                                       GBool forSoftMask)
2244 {
2245     const char*colormodename = "";
2246
2247     if(blendingColorSpace) {
2248         colormodename = GfxColorSpace::getColorSpaceModeName(blendingColorSpace->getMode());
2249     }
2250     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);
2251     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);
2252     
2253     //states[statepos].createsoftmask |= forSoftMask;
2254     states[statepos].createsoftmask = forSoftMask;
2255     states[statepos].transparencygroup = !forSoftMask;
2256     states[statepos].isolated = isolated;
2257
2258     states[statepos].olddevice = this->device;
2259     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2260
2261     gfxdevice_record_init(this->device);
2262     
2263     /*if(!forSoftMask) { ////???
2264         state->setFillOpacity(0.0);
2265     }*/
2266     dbgindent+=2;
2267 }
2268
2269 void GFXOutputDev::endTransparencyGroup(GfxState *state)
2270 {
2271     dbgindent-=2;
2272     dbg("endTransparencyGroup");
2273     msg("<verbose> endTransparencyGroup");
2274
2275     gfxdevice_t*r = this->device;
2276
2277     this->device = states[statepos].olddevice;
2278
2279     gfxresult_t*recording = r->finish(r);
2280     if(states[statepos].createsoftmask) {
2281         states[statepos-1].softmaskrecording = recording;
2282     } else {
2283         states[statepos-1].grouprecording = recording;
2284     }
2285     
2286     states[statepos].createsoftmask = 0;
2287     states[statepos].transparencygroup = 0;
2288     free(r);
2289 }
2290
2291 void GFXOutputDev::paintTransparencyGroup(GfxState *state, double *bbox)
2292 {
2293     const char*blendmodes[] = {"normal","multiply","screen","overlay","darken", "lighten",
2294                                "colordodge","colorburn","hardlight","softlight","difference",
2295                                "exclusion","hue","saturation","color","luminosity"};
2296
2297     dbg("paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2298     msg("<verbose> paintTransparencyGroup blend=%s softmaskon=%d", blendmodes[state->getBlendMode()], states[statepos].softmask);
2299    
2300     if(state->getBlendMode() == gfxBlendNormal)
2301         infofeature("transparency groups");
2302     else {
2303         char buffer[80];
2304         sprintf(buffer, "%s blended transparency groups", blendmodes[state->getBlendMode()]);
2305         warnfeature(buffer, 0);
2306     }
2307
2308     gfxresult_t*grouprecording = states[statepos].grouprecording;
2309    
2310     if(state->getBlendMode() == gfxBlendNormal) {
2311         gfxdevice_t ops;
2312         gfxdevice_ops_init(&ops, this->device, (unsigned char)(state->getFillOpacity()*255));
2313         gfxresult_record_replay(grouprecording, &ops);
2314         ops.finish(&ops);
2315     }
2316     grouprecording->destroy(grouprecording);
2317
2318     states[statepos].grouprecording = 0;
2319 }
2320
2321 void GFXOutputDev::setSoftMask(GfxState *state, double *bbox, GBool alpha, Function *transferFunc, GfxColor *rgb)
2322 {
2323     /* alpha = 1: retrieve mask values from alpha layer
2324        alpha = 0: retrieve mask values from luminance */
2325     dbg("setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2326             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2327     msg("<verbose> setSoftMask %.1f/%.1f/%.1f/%.1f alpha=%d backdrop=%02x%02x%02x",
2328             bbox[0], bbox[1], bbox[2], bbox[3], alpha, colToByte(rgb->c[0]), colToByte(rgb->c[1]), colToByte(rgb->c[2]));
2329     if(!alpha)
2330         infofeature("soft masks");
2331     else
2332         warnfeature("soft masks from alpha channel",0);
2333     
2334     states[statepos].olddevice = this->device;
2335     this->device = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
2336     gfxdevice_record_init(this->device);
2337
2338     dbg("softmaskrecording is %08x at statepos %d\n", states[statepos].softmaskrecording, statepos);
2339     
2340     states[statepos].softmask = 1;
2341     states[statepos].softmask_alpha = alpha;
2342 }
2343
2344 static inline Guchar div255(int x) {
2345   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
2346 }
2347
2348 static unsigned char clampU8(unsigned char c, unsigned char min, unsigned char max)
2349 {
2350     if(c < min) c = min;
2351     if(c > max) c = max;
2352     return c;
2353 }
2354
2355 void GFXOutputDev::clearSoftMask(GfxState *state)
2356 {
2357     if(!states[statepos].softmask)
2358         return;
2359     states[statepos].softmask = 0;
2360     dbg("clearSoftMask statepos=%d", statepos);
2361     msg("<verbose> clearSoftMask");
2362     
2363     if(!states[statepos].softmaskrecording || strcmp(this->device->name, "record")) {
2364         msg("<error> Error in softmask/tgroup ordering");
2365         return;
2366     }
2367   
2368     gfxresult_t*mask = states[statepos].softmaskrecording;
2369     gfxresult_t*below = this->device->finish(this->device);
2370     this->device = states[statepos].olddevice;
2371
2372     /* get outline of all objects below the soft mask */
2373     gfxdevice_t uniondev;
2374     gfxdevice_union_init(&uniondev, 0);
2375     gfxresult_record_replay(below, &uniondev);
2376     gfxline_t*belowoutline = gfxdevice_union_getunion(&uniondev);
2377     uniondev.finish(&uniondev);
2378
2379     gfxbbox_t bbox = gfxline_getbbox(belowoutline);
2380 #if 0 
2381     this->device->startclip(this->device, belowoutline);
2382     gfxresult_record_replay(below, this->device);
2383     gfxresult_record_replay(mask, this->device);
2384     this->device->endclip(this->device);
2385     gfxline_free(belowoutline);
2386 #endif
2387     
2388     int width = (int)bbox.xmax,height = (int)bbox.ymax;
2389     if(width<=0 || height<=0)
2390         return;
2391
2392     gfxdevice_t belowrender;
2393     gfxdevice_render_init(&belowrender);
2394     if(states[statepos+1].isolated) {
2395         belowrender.setparameter(&belowrender, "fillwhite", "1");
2396     }
2397     belowrender.setparameter(&belowrender, "antialize", "2");
2398     belowrender.startpage(&belowrender, width, height);
2399     gfxresult_record_replay(below, &belowrender);
2400     belowrender.endpage(&belowrender);
2401     gfxresult_t* belowresult = belowrender.finish(&belowrender);
2402     gfximage_t* belowimg = (gfximage_t*)belowresult->get(belowresult,"page0");
2403     //writePNG("below.png", (unsigned char*)belowimg->data, belowimg->width, belowimg->height);
2404
2405     gfxdevice_t maskrender;
2406     gfxdevice_render_init(&maskrender);
2407     maskrender.startpage(&maskrender, width, height);
2408     gfxresult_record_replay(mask, &maskrender);
2409     maskrender.endpage(&maskrender);
2410     gfxresult_t* maskresult = maskrender.finish(&maskrender);
2411     gfximage_t* maskimg = (gfximage_t*)maskresult->get(maskresult,"page0");
2412
2413     if(belowimg->width != maskimg->width || belowimg->height != maskimg->height) {
2414         msg("<fatal> Internal error in mask drawing");
2415         return;
2416     }
2417
2418     int y,x;
2419     for(y=0;y<height;y++) {
2420         gfxcolor_t* l1 = &maskimg->data[maskimg->width*y];
2421         gfxcolor_t* l2 = &belowimg->data[belowimg->width*y];
2422         for(x=0;x<width;x++) {
2423             int alpha;
2424             if(states[statepos].softmask_alpha) {
2425                 alpha = l1->a;
2426             } else {
2427                 alpha = (77*l1->r + 151*l1->g + 28*l1->b) >> 8;
2428             }
2429
2430             l2->a = div255(alpha*l2->a);
2431
2432             /* DON'T premultiply alpha- this is done by fillbitmap,
2433                depending on the output device */
2434             //l2->r = div255(alpha*l2->r);
2435             //l2->g = div255(alpha*l2->g);
2436             //l2->b = div255(alpha*l2->b);
2437
2438             l1++;
2439             l2++;
2440         }
2441     }
2442     gfxline_t*line = gfxline_makerectangle(0,0,width,height);
2443
2444     gfxmatrix_t matrix;
2445     matrix.m00 = 1.0; matrix.m10 = 0.0; matrix.tx = 0.0;
2446     matrix.m01 = 0.0; matrix.m11 = 1.0; matrix.ty = 0.0;
2447
2448     this->device->fillbitmap(this->device, line, belowimg, &matrix, 0);
2449
2450     mask->destroy(mask);
2451     below->destroy(below);
2452     maskresult->destroy(maskresult);
2453     belowresult->destroy(belowresult);
2454     states[statepos].softmaskrecording = 0;
2455 }
2456   
2457 //class MemCheck
2458 //{
2459 //    public: ~MemCheck()
2460 //    {
2461 //        delete globalParams;globalParams=0;
2462 //        Object::memCheck(stderr);
2463 //        gMemReport(stderr);
2464 //    }
2465 //} myMemCheck;
2466