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