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