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