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