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