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