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