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